File: _kicore_execution.c

package info (click to toggle)
python-kinterbasdb 3.1-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 1,044 kB
  • ctags: 1,157
  • sloc: ansic: 6,879; python: 2,517; makefile: 77
file content (901 lines) | stat: -rw-r--r-- 31,533 bytes parent folder | download
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
/* KInterbasDB Python Package - Implementation of SQL Statement Execution, etc.
**
** Version 3.1
**
** 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-2004 [dsr]   David Rushby          <woodsplitter@rocketmail.com>
** [Contributors:]
**   2001      [eac]   Evgeny A. Cherkashin  <eugeneai@icc.ru>
**   2001-2002 [janez] Janez Jere            <janez.jere@void.si>
*/


/****************** "PRIVATE" DECLARATIONS:BEGIN *******************/
static int _prepare_statement_if_necessary(CursorObject *cursor, PyObject *sql);

static boolean _check_statement_length(long length);

static int determine_statement_type(
    isc_stmt_handle *statementHandle, ISC_STATUS statusVector[]
  );
/****************** "PRIVATE" DECLARATIONS:END *******************/


/* For an explanation of this function's purpose, see the documentation for
** Python function kinterbasdb.create_database. */
static PyObject *pyob_create_database( PyObject *self, PyObject *args ) {
  ConnectionObject *con;
  char *sql = NULL;
  int sql_len = -1;
  short dialect = 0;

  if ( !PyArg_ParseTuple(args, "s#|h", &sql, &sql_len, &dialect) ) {
    return NULL;
  }

  if (!_check_statement_length(sql_len)) {
    return NULL;
  }

  /* A negative value for the dialect is not acceptable because the IB/FB API
  ** requires an UNSIGNED SHORT. */
  if (dialect < 0) {
    raise_exception(ProgrammingError, "con dialect must be > 0");
    return NULL;
  }

  con = new_connection();
  if (con == NULL) {
    /* The new_connection function will already have set an exception. */
    return NULL;
  }

  /* conn->dialect is set to a default value in the new_connection
  ** function, so we only need to change it if we received a dialect argument
  ** to this function. */
  if (dialect > 0) {
    con->dialect = (unsigned short) dialect;
  }
  assert (con->dialect > 0);

  ENTER_DB
  isc_dsql_execute_immediate(
      con->status_vector,
      /* 2003.04.27: CREATE DATABASE will never be issued from a distributed
      ** transaction, so we do not use CON_GET_TRANS_HANDLE_ADDR below. */
      &con->db_handle,
      &con->trans_handle,
      (unsigned short) sql_len,
      sql,
      con->dialect,
      NULL
    );
  LEAVE_DB

  if ( DB_API_ERROR(con->status_vector) ) {
    raise_sql_exception(ProgrammingError, "pyob_create_database: ",
        con->status_vector
      );
    pyob_connection_del( (PyObject *) con );
    return NULL;
  } else {
    con->_state = CONNECTION_STATE_OPEN;
    return (PyObject *) con;
  }
} /* pyob_create_database */


/* For an explanation of this function's purpose, see the documentation for
** Python function kinterbasdb.drop_database. */
static PyObject *pyob_drop_database( PyObject *self, PyObject *args ) {
  ConnectionObject *con;

  if ( !PyArg_ParseTuple(args, "O!", &ConnectionType, &con) ) {
    return NULL;
  }

  CONN_REQUIRE_OPEN(con);
  /* CONN_REQUIRE_OPEN should enforce non-null db_handle, but assert anyway: */
  assert (con->db_handle != NULL);
  /* Already enforced at Python level: */
  assert (con->group == NULL);

  /* If there's an unresolved transaction, roll it back before dropping the
  ** database.  Otherwise, the database would be dropped and then the
  ** connection would attempt to roll back the transaction in close_connection
  ** (called by the destructor delete_connection), resulting in subtle memory
  ** corruption. */
  if (OP_RESULT_OK !=
      rollback_transaction(con->trans_handle, FALSE, TRUE, con->status_vector)
    )
  {
    return NULL;
  }
  con->trans_handle = NULL; /* 2003.10.14 */

  /* 2003.10.15: begin block */
  /* Normally, free_field_precision_cache will involve calls to
  ** isc_dsql_free_statement, so it *must* be performed before the database
  ** handle is invalidated. */
  #ifdef DETERMINE_FIELD_PRECISION
  free_field_precision_cache( con->desc_cache,
      TRUE, /* Yes, it *should* try to free the statement handles. */
      con->status_vector
    );
  con->desc_cache = NULL;
  #endif
  /* 2003.10.15: end block */

  ENTER_DB
  isc_drop_database( con->status_vector, &(con->db_handle) );
  LEAVE_DB

  if ( DB_API_ERROR(con->status_vector) ) {
    raise_sql_exception(OperationalError, "pyob_drop_database: ",
      con->status_vector);
    return NULL;
  }

  con->db_handle = NULL; /* 2003.10.08 */
  con->_state = CONNECTION_STATE_CLOSED; /* 2003.10.14 */

  RETURN_PY_NONE;
} /* pyob_drop_database */


/* 2002.02.25:
** execute_immediate in the context of a con.
** Like isc_dsql_execute_immediate (which it wraps), this function cannot
** execute SQL statements that return result sets.
**
** It would be easy to separately wrap isc_dsql_exec_immed2, which can return
** up to one row of output, but I don't see the point.  Note that
** isc_dsql_exec_immed2 would have to be SEPARATELY wrapped, because here
** (unlike in pyob_execute), we don't have a prepared representation of
** the query, and therefore CANNOT do a similar dynamic selection of execution
** function, for we don't know the statement type. */
static PyObject *pyob_execute_immediate (
    PyObject *self, PyObject *args
) {
  ConnectionObject *con;
  char *sql = NULL;
  int sql_len = -1;

  if ( !PyArg_ParseTuple( args, "O!s#", &ConnectionType, &con, &sql, &sql_len ) ) {
    return NULL;
  }

  CONN_REQUIRE_OPEN(con);

  /* 2003.02.17b: */
  if (CON_GET_TRANS_HANDLE(con) == NULL) { /* 2003.10.15a:OK */
    /* The Python layer of kinterbasdb should have prevented this from happening. */
    raise_exception(InternalError, "pyob_execute_immediate: null transaction");
    return NULL;
  }

  if (!_check_statement_length(sql_len)) {
    return NULL;
  }

  {
    /* 2003.10.15a:  Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */
    isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(con);

    ENTER_DB
    isc_dsql_execute_immediate( con->status_vector,
        &con->db_handle,
        trans_handle_addr,
        (unsigned short) sql_len,
        sql,
        con->dialect,
        NULL
      );
    LEAVE_DB
  }

  if ( DB_API_ERROR(con->status_vector) ) {
    raise_sql_exception( ProgrammingError,
        "isc_dsql_execute_immediate: ", con->status_vector
      );
    return NULL;
  }

  RETURN_PY_NONE;
} /* pyob_execute_immediate */


static PyObject *pyob_execute( PyObject *self, PyObject *args ) {
  /* This function returns a Python DB API description tuple upon successful
  ** execution of a result-set-returning statement (that is, a query), or
  ** None upon successful execution of any other statement.  It sets a
  ** Python exception and returns NULL in case of failure. */
  PyObject *cursor_description;

  CursorObject *cursor;
  ConnectionObject *conn; /* Just a lookup cache. */
  PyObject *sql = NULL; /* Either PyString or PyUnicode. */
  PyObject *params = NULL;

  int statementType;

  if ( !PyArg_ParseTuple( args, "O!OO", &CursorType, &cursor, &sql, &params ) ) {
    return NULL;
  }

  CUR_REQUIRE_OPEN(cursor);
  conn = cursor->connection;

  /* For $params, accept any sequence except a string. */
  if ( !PySequence_Check(params) ) {
    raise_exception( InterfaceError, "input parameter container is not a sequence" );
    return NULL;
  }
  if ( PyString_Check(params) ) {
    raise_exception( InterfaceError, "input parameter sequence cannot be a string" );
    return NULL;
  }

  #ifdef KIDB_DEBUGGERING /* 2003.09.06 */
  {
    PyObject *sql_str = PyObject_Str(sql);
    PyObject *params_str = PyObject_Str(params);
    fprintf(stderr, "{SQL TRACE:\n  [%s]\n with params\n  %s\n}\n",
        PyString_AS_STRING(sql_str), PyString_AS_STRING(params_str)
      );
    Py_DECREF(sql_str);
    Py_DECREF(params_str);
  }
  #endif /* KIDB_DEBUGGERING */

  /* If this cursor was associated with a previous statement,
  ** "freshen" it up so that it can deal can deal with another statement
  ** execution (possibly of the same SQL statement, with different input
  ** parameters) and perhaps also retrieve another result set. */
  clear_cursor(cursor, sql);

  /* 2003.02.17b: */
  if (CON_GET_TRANS_HANDLE(conn) == NULL) { /* 2003.10.15a:OK */
    /* The Python layer of kinterbasdb should have prevented this from happening. */
    raise_exception(InternalError, "pyob_execute: null transaction");
    return NULL;
  }

  if ( 0 != _prepare_statement_if_necessary(cursor, sql) ) {
    /* Note that this cleanup route excludes the
    ** free_XSQLVAR_dynamically_allocated_memory call. */
    goto EXECUTE_ERROR_CLEANUP_EXCEPT_DYNVARALLOC;
  }

  /* Convert the Python input arguments to their XSQLVAR equivalents. */
  if ( PyObject2XSQLDA(cursor, cursor->in_sqlda, params) < 0 ) {
    goto EXECUTE_ERROR_CLEANUP;
  }

  /* 2002.08.19:  allocate_output_buffer call was originally here; I moved
  ** it to within _prepare_statement_if_necessary. */

  /* 2002.01.01:
  ** It looks like the original author was dealing with some similar
  ** EXECUTE PROCEDURE troubles, but they weren't the same as the one I'm
  ** here to fix.
  **
  ** The fact that there are output fields (that is,
  ** cursors[cur].in_sqlda->sqld is >= 1) does not guarantee that the
  ** statement is compatible with a standard SELECT-fetch approach; it
  ** could be an EXECUTE on a stored procedure with output params.
  **
  ** Special case:
  **   The cursor's statement type is isc_info_sql_stmt_exec_procedure
  **   and it has at least one output parameter.
  **
  **   Unlike a SELECT statement whose target is a stored procedure, an
  **   EXECUTE PROCEDURE statement must not return more than one row.
  **   This is because such a procedure must be executed with
  **   isc_dsql_execute2, which returns its results immediately and is
  **   therefore not compatible with a standard execute-fetch approach.
  **
  **   However, I later (2002.02.21) imposed the "appearance of execute-fetch"
  **   on even this special case by caching the results in the cursor object
  **   and returning the ONE *cached* row if/when fetch is called.
  **   This behavior was set up in order to satisfy the Python DB API spec.
  **
  **   kinterbasdb up to and including version 2.0-0.3.1 would choke with an
  **   exception instead of behaving in the standard way.  It did so because
  **   it tried to execute the procedure in this special case with
  **   isc_dsql_execute rather than isc_dsql_execute2.  That appeared to work
  **   fine during the execution step, but gagged if/when fetch was
  **   subsequently called.
  */

  /* 2003.01.26:  The statement type is now cached by the cursor; it need not
  ** be recomputed every time. */
  statementType = cursor->statement_type;
  assert (statementType != NULL_STATEMENT_TYPE);

  debug_printf1( "[in pyob_execute] STATEMENT TYPE: %d\n", statementType );

  if (     statementType == isc_info_sql_stmt_exec_procedure
        && cursor->out_sqlda->sqld > 0
     )
  {
    /* 2002.01.01:
    ** The crucial difference between isc_dsql_execute and
    ** isc_dsql_execute2 is that the latter loads information about the
    ** first output row into the output structures immediately, without
    ** waiting for a call to isc_dsql_fetch().  It is IMPORTANT to prevent
    ** isc_dsql_fetch from being called on a cursor that has been executed
    ** with isc_dsql_execute2.
    ** 2002.02.21:
    ** Although it is true that isc_dsql_fetch must not be called on a cursor
    ** that has been executed with isc_dsql_execute2, we must impose the
    ** appearance of that behavior in order to satisfy the Python DB API
    ** Specification 2.0, which does not allow a direct return of stored
    ** procedure results unless they are output parameters or
    ** input/output parameters (in the sense that a client variables is passed
    ** in and its memory space if filled with the output value).  IB and FB
    ** lack such "shared memory space" parameters, so even though we have
    ** the result row immediately after calling isc_dsql_execute2, we do not
    ** return it directly but instead save it and wait for a possible fetch
    ** call at some later point.
    ** The current implementation caches the single result row from
    ** isc_dsql_execute2 in cursor->exec_proc_results. */
    {
      /* 2003.10.15a:  Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */
      isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(conn);

      ENTER_DB
      isc_dsql_execute2( cursor->status_vector,
          trans_handle_addr,
          &(cursor->stmt_handle),
          conn->dialect,
          cursor->in_sqlda,
          cursor->out_sqlda
        );
      LEAVE_DB
    }

    debug_dump_status_vector(cursor->status_vector);
    if ( DB_API_ERROR(cursor->status_vector) ) {
      raise_sql_exception( ProgrammingError,
          "isc_dsql_execute2: ", cursor->status_vector
        );
      goto EXECUTE_ERROR_CLEANUP;
    }

    /* First, cache the result of the procedure call so that it is available
    ** if and when fetch is called in the future. */
    cursor->exec_proc_results = XSQLDA2Tuple(cursor, cursor->out_sqlda);
    if (cursor->exec_proc_results == NULL) {
      goto EXECUTE_ERROR_CLEANUP;
    }

    /* Next, return the description (but not the results, as the initial
    ** fix from circa 2002.01.01 was doing), just as would be done in the
    ** case of a normal result-returning query (which we are crudely
    ** simulating via the cursor->exec_proc_results cache variable). */

    cursor_description = XSQLDA2Description(cursor->out_sqlda, cursor);

    if (cursor_description == NULL) goto EXECUTE_ERROR_CLEANUP;

    return cursor_description;
  } else { /* Everything except the special case(s) defined above. */
    {
      /* 2003.10.15a:  Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */
      isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(conn);

      ENTER_DB
      isc_dsql_execute( cursor->status_vector, trans_handle_addr,
          &cursor->stmt_handle, conn->dialect, cursor->in_sqlda
        );
      LEAVE_DB
    }

    if ( DB_API_ERROR(cursor->status_vector) ) {
      raise_sql_exception( ProgrammingError, "isc_dsql_execute: ",
          cursor->status_vector
        );
      goto EXECUTE_ERROR_CLEANUP;
    }
  }

  assert ( DB_API_ERROR(cursor->status_vector) == FALSE );

  /* YYY:2002.02.21:
  ** (as part of fix for bug #520793)
  ** Conceptually, there should be an error in the status vector at this
  ** point in cases where
  **   a SELECT statement whose target is
  **     a stored procedure
  **       that raises an IB user-defined EXCEPTION
  ** has just been executed.  However, said exception is present ONLY
  ** conceptually, not really.
  **
  ** Apparently, the IB API only "realizes" that a user-defined EXCEPTION has
  ** been raised when it tries to fetch the first row of output from the
  ** stored procedure in question.  Because of the way IB stored procedures
  ** work (generate... suspend; generate... suspend;), I can understand why
  ** the IB API works the way it does.
  **
  ** This quirk explains why the branch of this function that uses
  ** isc_dsql_execute2 works as it conceptually should (i.e., it "realizes"
  ** that a user-defined exception has been raised as soon as it is
  ** conceptually raised), and why the branch that uses isc_dsql_execute--
  ** the variant that does not immediately fetch the first row of output--
  ** does not raise an error until fetch is called (and fetch may never be
  ** called!).
  **
  ** Here is the basic problem:  suppose a user executes a statement like:
  ** SELECT * FROM PROCEDURE_THAT_RAISES_EXCEPTION
  ** , but the user happens not to call fetch (i.e., decides to ignore the
  ** result set).  In such a case, the user would never know that an
  ** exception had been raised, because the IB API itself would never
  ** register the fact that an exception had taken place.
  **
  ** I've searched extensively for a way to fix this, but haven't found one.
  */

  if (cursor->out_sqlda->sqld == 0) {
    /* The DB API spec says of Cursor.description: "This attribute will be
    ** None for operations that do not return rows...". */
    Py_INCREF(Py_None);
    cursor_description = Py_None;
  } else {
    cursor_description = XSQLDA2Description(cursor->out_sqlda, cursor);
    if (cursor_description == NULL) goto EXECUTE_ERROR_CLEANUP;
  }

  /* In case of success, deliberately fall through into EXECUTE_NORMAL_RETURN. */

/* EXECUTE_NORMAL_RETURN: */
  if ( free_XSQLVAR_dynamically_allocated_memory(cursor) != 0 ) {
    goto EXECUTE_ERROR_CLEANUP_EXCEPT_DYNVARALLOC;
  }
  return cursor_description;

EXECUTE_ERROR_CLEANUP:
  free_XSQLVAR_dynamically_allocated_memory(cursor);
EXECUTE_ERROR_CLEANUP_EXCEPT_DYNVARALLOC:
  /* Note that the cursor is closed AFTER the call to
  ** free_XSQLVAR_dynamically_allocated_memory. */
  close_cursor_with_error(cursor); /* 2003.02.17c: */

  return NULL;
} /* pyob_execute */


static int _prepare_statement_if_necessary(
    CursorObject *cursor, PyObject *sql
  )
{
  ConnectionObject *conn = cursor->connection;

  assert (CON_GET_TRANS_HANDLE(conn) != NULL); /* 2003.10.15a:OK */
  assert (cursor->_state == CURSOR_STATE_CLOSED);

  /* If this is another execution of the previous statement, no new preparation
  ** is necessary. */
  {
    PyObject *prev_sql = cursor->previous_sql;
    if (prev_sql != NULL) {
      /* If the PyObject pointers point to the same memory location, the two
      ** objects are certainly equal--in fact, they're IDentical
      ** (id(prev_sql) == id(sql)).  If the pointers refer to different memory
      ** locations, the two objects are still equal if their contents match. */
      if (sql == prev_sql || PyObject_Compare(sql, prev_sql) == 0) {
        cursor->_state = CURSOR_STATE_OPEN;
        return 0;
      }
    }
  }

  if ( !( PyString_Check(sql) || PyUnicode_Check(sql) ) ) {
    raise_exception(PyExc_TypeError, "SQL must be string or unicode object.");
    return -1;
  }

  /* Free the old statement (if any) and any resources the cursor was caching
  ** in association with the old statement, including its output buffer. */
  close_cursor(cursor);
  /* Moved statement allocation here from new_cursor in order to defer it.
  ** (The deferment makes cursor cleanup code much cleaner.) */

  /* Allocate new statement handle. */
  assert(cursor->stmt_handle == NULL);
  ENTER_DB
  isc_dsql_allocate_statement( cursor->status_vector,
      &(conn->db_handle),
      &(cursor->stmt_handle)
    );
  LEAVE_DB

  if ( DB_API_ERROR(cursor->status_vector) ) {
    raise_sql_exception( OperationalError,
        "pyob_execute.isc_dsql_allocate_statement: ", cursor->status_vector
      );
    return -1;
  }
  assert(cursor->stmt_handle != NULL);

  {
    /* translated_sql will only become non-NULL if $sql is a Unicode object
    ** rather than a string (and therefore must be translated to ASCII before
    ** being passed to isc_dsql_prepare).
    ** Since translated_sql, when non-NULL, refers to a *new* PyStringObject, it
    ** is unconditionally Py_XDECREFed at the end of this block. */
    PyObject *translated_sql = NULL;

    char *sql_raw_buffer = NULL;
    int sql_len = -1;

    if ( PyString_Check(sql) ) {
      sql_raw_buffer = PyString_AS_STRING(sql);
      sql_len = PyString_GET_SIZE(sql);
    } else {
      /* At this point, $sql is certain to be a unicode object, because its
      ** type was constrained to either string or unicode at the beginning of
      ** this function. */
      /* 2003.10.14:
      ** Ideally, we would pass the database engine's C API the incoming SQL
      ** statement without converting it to ASCII (i.e., via
      ** PyUnicode_AsWideChar or something similar), but it seems that the C
      ** API doesn't accept anything but ASCII. */

      /* PyUnicode_AsASCIIString creates a *new* PyStringObject. */
      translated_sql = PyUnicode_AsASCIIString(sql);
      if (translated_sql == NULL) {
        return -1;
      }

      sql_raw_buffer = PyString_AS_STRING(translated_sql);
      sql_len = PyString_GET_SIZE(translated_sql);
    }
    assert (sql_raw_buffer != NULL && sql_len >= 0);

    if (!_check_statement_length(sql_len)) {
      Py_XDECREF(translated_sql);
      return -1;
    }

    /* Ask the database engine to compile the statement. */
    {
      /* 2003.10.15a:  Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */
      isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(conn);

      ENTER_DB
      isc_dsql_prepare( cursor->status_vector,
          trans_handle_addr,
          &(cursor->stmt_handle),
          (unsigned short) sql_len,
          sql_raw_buffer,
          conn->dialect,
          cursor->out_sqlda
        );
      LEAVE_DB
    }

    Py_XDECREF(translated_sql);
  } /* Ends block in which sql_raw_buffer is dealt with. */

  if ( DB_API_ERROR(cursor->status_vector) ) {
    raise_sql_exception( ProgrammingError,
        "isc_dsql_prepare: ", cursor->status_vector
      );
    return -1;
  }

  /* Ensure that the output XSQLDA has enough XSQLVAR slots for the statement's
  ** output variables. */
  if ( reallocate_sqlda( &(cursor->out_sqlda), FALSE ) >= 0 ) {
    /* Reallocation succeeded, so bind information about the OUTput XSQLDA's
    ** variables. */
    ENTER_DB
    isc_dsql_describe( cursor->status_vector,
        &(cursor->stmt_handle),
        conn->dialect,
        cursor->out_sqlda /* OUTPUT */
      );
    LEAVE_DB

    if ( DB_API_ERROR(cursor->status_vector) ) {
      raise_sql_exception( OperationalError,
          "isc_dsql_describe for output params: ", cursor->status_vector
        );
      return -1;
    }
  } else {
    /* Reallocation failed. */
    return -1;
  }

  /* Bind information about the INput XSQLDA's variables. */
  ENTER_DB
  isc_dsql_describe_bind( cursor->status_vector,
      &(cursor->stmt_handle),
      conn->dialect,
      cursor->in_sqlda /* INPUT */
    );
  LEAVE_DB

  if ( DB_API_ERROR(cursor->status_vector) ) {
    raise_sql_exception( OperationalError,
        "isc_dsql_describe_bind for input params: ", cursor->status_vector
      );
    return -1;
  }

  {
    int input_sqlda_reallocation_result = reallocate_sqlda( &(cursor->in_sqlda), TRUE );

    #ifdef KIDB_DEBUGGERING
    printf("[in _prepare_statement_if_necessary] XSQLDA has: %d; needs: %d;"
        " reallocation result: %d\n",
        cursor->in_sqlda->sqln, cursor->in_sqlda->sqld, input_sqlda_reallocation_result
      );
    #endif

    if (input_sqlda_reallocation_result == 0) {
      /* No actual reallocation was necessary, so there's no need to rebind. */
    } else if (input_sqlda_reallocation_result == 1) {
      /* Reallocation was necessary, so the XSQLDA's parameter information must
      ** be rebound. */

      ENTER_DB
      isc_dsql_describe_bind( cursor->status_vector,
          &(cursor->stmt_handle),
          conn->dialect,
          cursor->in_sqlda  /* INPUT */
        );
      LEAVE_DB

      if ( DB_API_ERROR(cursor->status_vector) ) {
        raise_sql_exception( OperationalError,
            "isc_dsql_describe_bind[2] for input params: ", cursor->status_vector
          );
        return -1;
      }
    } else {
      /* Reallocation failed. */
      return -1;
    }
  }

  /* The statement needed to be prepared anew, so we must now update the
  ** cursor's cache.
  ** IMPORTANT: The cache is not updated until this point, near the END of
  ** this function, because all of the compilation preliminaries must first be
  ** hashed out.
  */
  free_cursor_cache(cursor);

  {
    XSQLVAR *sqlvar;
    OriginalXSQLVARSpecificationCache *spec_cache;
    short var_no, column_count;

    column_count = cursor->in_sqlda->sqld;

    /* 2003.03.31: */
    cursor->in_var_orig_spec = kimem_plain_malloc(
        sizeof(OriginalXSQLVARSpecificationCache) * column_count
      );

    for ( sqlvar = cursor->in_sqlda->sqlvar, var_no = 0,
            spec_cache = cursor->in_var_orig_spec;
          var_no < column_count;
          sqlvar++, var_no++, spec_cache++
        )
    {
      spec_cache->sqltype = sqlvar->sqltype;
      spec_cache->sqllen = sqlvar->sqllen;
    }
  }

  Py_XDECREF(cursor->previous_sql); /* 2003.02.13 */
  Py_INCREF(sql);
  cursor->previous_sql = sql;

  /* 2003.01.26: Determine the database API's internal type code for this
  ** statement and cache that code in the cursor. */
  cursor->statement_type = determine_statement_type(
      &(cursor->stmt_handle), cursor->status_vector
    );

  /* If this is a query statement, allocate a buffer for the output values.
  ** Ensure that this function freed the output buffer (via close_cursor)
  ** before it prepared the new statement. */
  assert (cursor->out_buffer == NULL);
  if (cursor->out_sqlda->sqld > 0) {
    cursor->out_buffer = allocate_output_buffer(cursor->out_sqlda);
    if (cursor->out_buffer == NULL) {
      return -1;
    }
  }
  /* Done updating cursor's cache. */

  /* The cursor was closed before we prepared the new statement; it is now
  ** open, and must be flagged accordingly. */
  cursor->_state = CURSOR_STATE_OPEN;
  return 0;
} /* _prepare_statement_if_necessary */


static int determine_statement_type(
    isc_stmt_handle *statementHandle, ISC_STATUS statusVector[]
  )
{
  /* Dynamically determine the statement type.  Follows p. 343 of IB Beta 6
  ** API Guide and IB example apifull.c. */
  int statementType;
  int statementTypeLength;
  static char sqlInfoStatementTypeRequest[] = { isc_info_sql_stmt_type };
  char sqlInfoResultBuffer[ ISC_INFO_BUFFER_SIZE ];

  ENTER_DB
  isc_dsql_sql_info( statusVector, statementHandle,
      sizeof(sqlInfoStatementTypeRequest),
      sqlInfoStatementTypeRequest,

      sizeof(sqlInfoResultBuffer),
      sqlInfoResultBuffer
    );

  /* Next two lines follow the Interbase example apifull.c rather than the
  ** API Guide. */
  statementTypeLength = (short)(
      isc_vax_integer( (char *) sqlInfoResultBuffer + 1, 2 )
    );
  statementType = (int) (
      isc_vax_integer( (char *) sqlInfoResultBuffer + 3,
          (short)statementTypeLength
        )
    );
  LEAVE_DB

  return statementType;
} /* determine_statement_type */


/* 2003.05.15: */
static PyObject *pyob_rowcount( PyObject *self, PyObject *args ) {
  CursorObject *cur;
  int cursor_stmt_type;

  char request_params[] = {isc_info_sql_records, isc_info_end};

  /* YYY: The fixed size of res_buf introduces the possibility of a buffer
  ** overflow, but it's extremely unlikely because we know quite a bit about
  ** the size requirements (they're not affected by input from the client
  ** programmer, or anything like that).  */
  char res_buf[256];

  char *res_walk;
  long cur_count = -1;
  char cur_count_type; /* What type of statement does this count concern? */
  short length_of_cur_count_in_buffer;

  if ( !PyArg_ParseTuple( args, "O!", &CursorType, &cur ) ) {
    return NULL;
  }

  cursor_stmt_type = cur->statement_type;
  if (cursor_stmt_type == NULL_STATEMENT_TYPE) {
    /* Python DB API Spec requires us to return -1 rather than raise exception. */
    return PyInt_FromLong(-1);
  }
  assert(cur->stmt_handle != NULL);

  if (   cursor_stmt_type != isc_info_sql_stmt_select
      && cursor_stmt_type != isc_info_sql_stmt_insert
      && cursor_stmt_type != isc_info_sql_stmt_update
      && cursor_stmt_type != isc_info_sql_stmt_delete
    )
  {
    /* Python DB API Spec requires us to return -1 rather than raise exception. */
    return PyInt_FromLong(-1);
  }

  ENTER_DB
  isc_dsql_sql_info( cur->status_vector,
      &cur->stmt_handle,
      sizeof(request_params), request_params,
      sizeof(res_buf), res_buf
    );
  LEAVE_DB

  if ( DB_API_ERROR(cur->status_vector) ) {
    raise_sql_exception(OperationalError, "pyob_rowcount: ", cur->status_vector);
    return NULL;
  }

  /* res_buf[0] indicates what type of information is being returned (in this
  ** situation, it never varies). */
  assert (res_buf[0] == isc_info_sql_records);

  /* Start res_walk after the first 3 bytes, which are infrastructural. */
  res_walk = res_buf + 3;
  while ( (cur_count_type = *res_walk) != isc_info_end ) {
    res_walk += 1;
    length_of_cur_count_in_buffer = (short) isc_vax_integer(res_walk, sizeof(short));
    res_walk += sizeof(short);
    cur_count = isc_vax_integer(res_walk, length_of_cur_count_in_buffer);
    res_walk += length_of_cur_count_in_buffer;

    /* If the count that we've just extracted from the result buffer concerns
    ** the same statement type as the last statement executed by the cursor,
    ** immediately return that count to the Python level.
    ** All temporary storage in this function is allocated on the stack, so
    ** there's nothing for us to manually release. */
    if (
         (   cur_count_type == isc_info_req_select_count
          && cursor_stmt_type == isc_info_sql_stmt_select
         ) || (
             cur_count_type == isc_info_req_insert_count
          && cursor_stmt_type == isc_info_sql_stmt_insert
         ) || (
             cur_count_type == isc_info_req_update_count
          && cursor_stmt_type == isc_info_sql_stmt_update
         ) || (
             cur_count_type == isc_info_req_delete_count
          && cursor_stmt_type == isc_info_sql_stmt_delete
         )
       )
    {
      return PyInt_FromLong(cur_count);
    }
  } /* end of "while not at end of result buffer..." loop */

  /* Because of the "guards" near the beginning of this function, it is not
  ** expected that this code will ever be reached.  However, a future version
  ** of the database engine may throw a curveball at us, so we'll behave as
  ** the Python DB API requires us to behave in cases where the row count
  ** cannot be determined (that is, return -1). */
  return PyInt_FromLong(-1);
} /* pyob_rowcount */


/* 2003.02.20:  Although the sql-statement-length parameter to such Firebird
** API functions as isc_dsql_prepare and isc_dsql_execute_immediate is an
** unsigned short, the documentation says that the length can be left zero for
** null-terminated strings, in which case the database engine will figure out
** the length itself.
**   As of 2003.02.13, Firebird cannot handle SQL statements longer than the
** maximum value of an unsigned short even if zero is passed as the length. */
static boolean _check_statement_length(long length) {
  /* Test the length and raise an exception if it's too long for safe passage
  ** to isc_* functions.  Return TRUE if OK; FALSE otherwise. */
  if (length > (long) USHRT_MAX) {
    char *err_msg = NULL;
    #if PYTHON_2_2_OR_LATER
      PyObject *buffer = PyString_FromFormat(
          "SQL statement of %ld bytes is too long (max %d allowed). Consider"
          " using parameters to shorten the SQL code, rather than passing large"
          " values as part of the SQL string.",
          length, USHRT_MAX
        );
      if (buffer == NULL) {
        return FALSE;
      }
      err_msg = PyString_AS_STRING(buffer);
    #else
      err_msg = "Length of SQL statement must be <= USHRT_MAX";
    #endif /* PYTHON_2_2_OR_LATER */
    assert (err_msg != NULL);

    raise_exception(ProgrammingError, err_msg);

    #if PYTHON_2_2_OR_LATER
      Py_DECREF(buffer);
    #endif

    return FALSE;
  }
  return TRUE;
} /* _validate_statement_length */