/* KInterbasDB Python Package - Header File for Core
 *
 * 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>
 */

#ifndef _KINTERBASDB_H
#define _KINTERBASDB_H

#define KIDB_HOME_PAGE "http://sourceforge.net/projects/kinterbasdb"
#define KIDB_REPORT " -- please report this to the developers at "

/* Define PY_SSIZE_T_CLEAN to make PyArg_ParseTuple's "s#" and "t#" codes
 * output Py_ssize_t rather than int when using Python 2.5+.
 * This definition must appear before #include "Python.h". */
#define PY_SSIZE_T_CLEAN

/* The standard guide to Embedding and Extending says: "Since Python may
 * define some pre-processor definitions which affect the standard headers
 * on some systems, you must include Python.h before any standard headers
 * are included." */
#include "Python.h"
/* structmember.h contains definitions used during C-level type creation in
 * newer versions of Python. */
#include "structmember.h"


/* Shut MSVC 8.0+ up (this should appear after the "Python.h" inclusion): */
#ifndef _CRT_SECURE_NO_DEPRECATE
  #define _CRT_SECURE_NO_DEPRECATE
#endif


#if (PY_MAJOR_VERSION >= 2)
  #if (PY_MINOR_VERSION >= 4)
    #define PYTHON_2_4_OR_LATER
  #endif
  #if (PY_MINOR_VERSION >= 5)
    #define PYTHON_2_5_OR_LATER
  #endif
#endif

#ifdef MS_WIN32
  #define PLATFORM_WINDOWS

  #if defined(_MSC_VER)
    #define COMPILER_IS_MSVC_WIN32
  #elif defined(__BORLANDC__)
    #define COMPILER_IS_BCPP_WIN32
  #else
    #define COMPILER_IS_MINGW_WIN32
  #endif
#endif

#include <assert.h>

#include <time.h>
#ifdef PLATFORM_WINDOWS
  #include <sys/timeb.h>
#endif

#include <math.h>

#include "ibase.h"

#include "__ki_platform_config.h"
#ifndef HAVE__useconds_t
  typedef unsigned int useconds_t;
#endif


#ifdef ENABLE_CONCURRENCY
  #define DEFAULT_CONCURRENCY_LEVEL 1
#else
  #define DEFAULT_CONCURRENCY_LEVEL 0
#endif
#define UNKNOWN_CONCURRENCY_LEVEL -1

typedef unsigned char boolean;
  #define TRUE  1
  #define FALSE 0

#include "_kimem.h"


/* Python 2.3 no longer defines LONG_LONG. */
#ifndef LONG_LONG
  #define LONG_LONG PY_LONG_LONG
#endif

#ifndef LONG_LONG_MIN
  #ifdef LLONG_MIN
    #define LONG_LONG_MIN LLONG_MIN
    #define LONG_LONG_MAX LLONG_MAX
  #else
    #ifdef _I64_MIN
      #define LONG_LONG_MIN _I64_MIN
      #define LONG_LONG_MAX _I64_MAX
    #endif
  #endif
#endif

/* Python's long type, which has unlimited range, is computationally
 * expensive relative to Python's int type, which wraps a native long.  So:
 * 1. On platforms where C's long is 64 bits or larger,
 *    PythonIntOrLongFrom64BitValue unconditionally creates a Python int.
 * 2. On platforms where C's long is smaller than 64 bits,
 *    PythonIntOrLongFrom64BitValue tests the value at runtime to see if it's
 *    within the range of a Python int; if so, it creates a Python int rather
 *    than a Python long.  It is expected that in most contexts, the cost of
 *    the test will be less than the cost of using a Python long. */
#if defined(_MSC_VER) || defined(__BORLANDC__)
  /* MSVC 6 and BCPP 5.5 won't accept the LL suffix. */
  #define NativeLongIsAtLeast64Bits \
    (LONG_MAX >= 9223372036854775807i64 && LONG_MIN <= (-9223372036854775807i64 - 1))
#else
  #define NativeLongIsAtLeast64Bits \
    (LONG_MAX >= 9223372036854775807LL  && LONG_MIN <= (-9223372036854775807LL  - 1))
#endif

#if NativeLongIsAtLeast64Bits
  #define PythonIntOrLongFrom64BitValue(x) PyInt_FromLong((long) (x))
#else
  #define PythonIntOrLongFrom64BitValue(x) \
    ( ((x) < LONG_MIN || (x) > LONG_MAX) ? PyLong_FromLongLong(x) : PyInt_FromLong((long) (x)) )
#endif


#if (SIZEOF_POINTER > 4 && defined(FB_API_VER) && FB_API_VER >= 20)
  /* 64-bit platform with FB2.0+: */
  #define _NULL_FB_API_HANDLE 0
#else
    /*64-bit platform with FB 1.5, and all 32-bit platforms: */
  #define _NULL_FB_API_HANDLE NULL
#endif

#define NULL_DB_HANDLE     _NULL_FB_API_HANDLE
#define NULL_SVC_HANDLE    _NULL_FB_API_HANDLE
#define NULL_TRANS_HANDLE  _NULL_FB_API_HANDLE
#define NULL_STMT_HANDLE   _NULL_FB_API_HANDLE
#define NULL_BLOB_HANDLE   _NULL_FB_API_HANDLE


/* Size arguments (such as the second argument of PyString_FromStringAndSize)
 * are expected to change from int to size_t in Python 2.5.  Until then, the
 * best way to deal with int-vs-size_t warnings is to silence them. */
#ifndef PYTHON_2_5_OR_LATER
  typedef int Py_ssize_t;
  #define PY_SSIZE_T_MAX INT_MAX
  #define PY_SSIZE_T_MIN INT_MIN

  #define Py_ssize_t_STRING_FORMAT "%d"
  #define Py_ssize_t_EXTRACTION_CODE "i"

  #define PyInt_FromSsize_t PyInt_FromLong
  #define PyInt_AsSsize_t   PyInt_AsLong
#else
  #define Py_ssize_t_STRING_FORMAT "%zd"
  #define Py_ssize_t_EXTRACTION_CODE "n"
#endif
#define SIZE_T_TO_PYTHON_SIZE(x) ((Py_ssize_t)(x))
#define PYTHON_SIZE_TO_SIZE_T(x) ((size_t) (x))

/* As of Python 2.5a1, a preprocessor statement such as
 *   #if PY_SSIZE_T_MAX > LONG_MAX
 * is rejected by the compiler, but assigning PY_SSIZE_T_MAX to a constant,
 * then referring to that constant, is accepted. */
const Py_ssize_t PY_SSIZE_T_MAX__CIRCUMVENT_COMPILER_COMPLAINT = PY_SSIZE_T_MAX;


/* FB 1.5 exposes ISC_SHORT in its header file, but FB 1.0 does not. */
#ifndef ISC_SHORT
  #define ISC_SHORT short
#endif


/* Detect Interbase 6 or later (including Firebird). */
#if (defined(SQL_DIALECT_CURRENT) && defined(SQL_DIALECT_V6))
  #define INTERBASE_6_OR_LATER
  #define SQL_DIALECT_DEFAULT SQL_DIALECT_CURRENT
  #define SQL_DIALECT_OLDEST SQL_DIALECT_V5
#else
  #define SQL_DIALECT_DEFAULT 1
  #define SQL_DIALECT_OLDEST 1
  /* IB < 6.0 had less differentiated datetime types; SQL_TIMESTAMP is more
   * representative of what columns called SQL_DATE actually contain. */
  #define SQL_TIMESTAMP SQL_DATE
  /* IB 6 API Guide p 301:  "The isc_decode_timestamp() is exactly the same as
   * the isc_decode_date() function in versions of InterBase prior to 6.0." */
  #define isc_decode_timestamp  isc_decode_date
  /* IB 6 API Guide p 346:  "This call [isc_encode_timestamp()] is exactly the
   * same as the older isc_encode_date(), which is still available for backward
   * compatibility. */
  #define isc_encode_timestamp  isc_encode_date

  #define ISC_TIMESTAMP  ISC_QUAD
  #define blr_timestamp  blr_date

  /* 2004.07.18: IB < 6.0 doesn't define ISC_ULONG: */
  #define ISC_ULONG unsigned long
#endif

#if (defined(METADATALENGTH) && defined(SQLDA_VERSION2) && defined(blr_boolean_dtype))
  #define INTERBASE_7_OR_LATER
#else
  /* Provide some constants so that versions of KInterbasDB compiled against
   * Firebird can still work at least minimally with Interbase 7+.  The FB
   * developers will probably maintain compatibility with these constants when
   * FB adds more types, so compatibility shouldn't be a problem. */
  #ifndef ISC_BOOLEAN
    #define ISC_BOOLEAN signed short
  #endif
  #ifndef SQL_BOOLEAN
    #define SQL_BOOLEAN 590
  #endif
  #ifndef blr_boolean_dtype
    #define blr_boolean_dtype 17
  #endif
#endif

/* Beginning with Firebird 1.5, Firebird defines an integer constant FB_API_VER
 * that we can use to determine the version of the client library we're
 * compiling against.  Unfortunately, Firebird 1.0 defines no such thing, so
 * we just test for an ad hoc definition that's defined in Firebird 1.0 but not
 * Interbase 6.0. */
#if (defined(dtype_null) || defined(FB_API_VER))
  #define FIREBIRD_1_0_OR_LATER
#endif

#ifndef FB_API_VER
  /* If FB_API_VER is not defined, we're working with FB 1.0 (or Interbase). */
  #define FB_API_VER 10
#else
  #if FB_API_VER >= 15
    #define FIREBIRD_1_5_OR_LATER
  #endif
  #if FB_API_VER >= 20
    #define FIREBIRD_2_0_OR_LATER
  #endif
#endif /* def FB_API_VER? */


#if (defined(FIREBIRD_1_0_OR_LATER) && !defined(FIREBIRD_1_5_OR_LATER))
  #define FIREBIRD_1_0_ONLY
#endif

#if (defined(FIREBIRD_1_5_OR_LATER) && !defined(FIREBIRD_2_0_OR_LATER))
  #define FIREBIRD_1_5_ONLY
#endif


/* The field precision determination code uses DB fields not present before IB
 * 6.0, so if we're using an earlier version than that, disable the feature
 * regardless of the user's setting. */
#ifndef INTERBASE_6_OR_LATER
  #undef ENABLE_FIELD_PRECISION_DETERMINATION
#endif

#ifdef INTERBASE_7_OR_LATER
  #define SQLDA_VERSION_KIDB SQLDA_CURRENT_VERSION
#else
  #define SQLDA_VERSION_KIDB SQLDA_VERSION1
#endif


/******************** COMPILATION OPTIONS:BEGIN ********************/

/* INITIAL_SQLVAR_CAPACITY is the number of XSQLVARs that a freshly allocated
 * XSQLDA (such as the in_sqlda and out_sqlda of a new cursor) is configured
 * to hold.
 * If the initial capacity proves insufficient, more space is allocated
 * automatically.  Therefore, INITIAL_SQLVAR_CAPACITY is an "optimization hint"
 * rather than a "hard-coded limit". */
#define INITIAL_SQLVAR_CAPACITY        16

/******************** COMPILATION OPTIONS:END ********************/

/******************** HARD-CODED LIMITS:BEGIN ********************/

/* MAX_BLOB_SEGMENT_SIZE is an IB/Firebird engine constraint, not something
 * we could overcome here in kinterbasdb.  For that matter, it's not a
 * "constraint" in any meaningful sense, since a single blob can have many
 * segments, and kinterbasdb manages the segmentation transparently. */
#define MAX_BLOB_SEGMENT_SIZE USHRT_MAX

/* Generally, the following hard-coded limits are those used by the Interbase 6
 * API Guide, though it does not officially define the limits, which leaves the
 * potential for buffer overflows.
 * DSR has made considerable effort to minimize the likelihood of buffer
 * overflows; you can find notes about those measures in the source code where
 * these constants are used. */

#define MAX_ISC_ERROR_MESSAGE_BUFFER_SIZE           4096

/* Firebird 1.5 and later publish the size of the status vector as
 * ISC_STATUS_LENGTH. */
#ifdef ISC_STATUS_LENGTH
  #define STATUS_VECTOR_SIZE           ISC_STATUS_LENGTH
#else
  #define STATUS_VECTOR_SIZE                          20
#endif

#define DPB_BUFFER_SIZE                              256

#define ISC_INFO_BUFFER_SIZE                          20

#define MAX_DPB_SIZE                            SHRT_MAX
#define MAX_DSN_SIZE                            SHRT_MAX


/* XSQLDA and XSQLVAR-related limits: */

/* With FB 1.0 and 1.5, attempting to prepare and execute a statement with an
 * extremely large number of parameters causes the Firebird client library to
 * mishandle memory and risk a segfault.
 * I have observed the problem to begin around 3625 parameters.
 * kinterbasdb defines MAX_XSQLVARS_IN_SQLDA to prevent Python programmers
 * from inadvertently running afoul of the FB client library's
 * irresponsibility.
 * If in the future the client library is fixed, there'll be no reason not to
 * raise MAX_XSQLVARS_IN_SQLDA. */
#define MAX_XSQLVARS_IN_SQLDA     1024

/* The database engine does not allow more than 16 database handles to
 * participate in a single distributed transaction. */
#define DIST_TRANS_MAX_DATABASES    16

/******************** HARD-CODED LIMITS:END ********************/

/******************** HANDY ABBREVIATIONS:BEGIN ********************/

typedef enum {
  SIGN_NEGATIVE = -1,
  SIGN_POSITIVE =  1
} NumberSign;

typedef enum {
  SUBTYPE_NONE    = 0,
  SUBTYPE_NUMERIC = 1,
  SUBTYPE_DECIMAL = 2
} SQLSubtype;

typedef enum {
  SQLIND_NULL     = -1,
  SQLIND_NOT_NULL =  0
} SQLIndicator_State;


/* See IB6 API Guide page 92. */
#define XSQLVAR_IS_ALLOWED_TO_BE_NULL(sqlvar) \
  (((sqlvar)->sqltype & 1) != 0)

#define XSQLVAR_IS_NULL(sqlvar) \
  (*(sqlvar)->sqlind == SQLIND_NULL)

#define XSQLVAR_SQLTYPE_IGNORING_NULL_FLAG(sqlvar) \
  ((sqlvar)->sqltype & ~1)

#define XSQLVAR_SQLTYPE_READ_NULL_FLAG(sqlvar) \
  ((sqlvar)->sqltype & 1)

#define XSQLVAR_SET_NULL(sqlvar) \
  (*(sqlvar)->sqlind = SQLIND_NULL)

#define XSQLVAR_SET_NOT_NULL(sqlvar) \
  (*(sqlvar)->sqlind = SQLIND_NOT_NULL)

/* Macros for printing debugging information to stderr. */
#ifdef VERBOSE_DEBUGGING
  /* MS Visual C++ 6.0 and 7.1 don't support variadic macros. */
  #define debug_print(format) fprintf(stderr, format)
  #define debug_print1(format, a) fprintf(stderr, format, a)
  #define debug_print2(format, a, b) fprintf(stderr, format, a, b)
  #define debug_print3(format, a, b, c) fprintf(stderr, format, a, b, c)
  #define debug_print4(format, a, b, c, d) fprintf(stderr, format, a, b, c, d)
#else
  #define debug_print(format)
  #define debug_print1(format, a)
  #define debug_print2(format, a, b)
  #define debug_print3(format, a, b, c)
  #define debug_print4(format, a, b, c, d)
#endif /* VERBOSE_DEBUGGING */

/* DB_API_ERROR acts like a boolean function, returning TRUE if
 * status_vector indicates an error. */
#define DB_API_ERROR(status_vector) \
  ( (((status_vector)[0] == 1) && (status_vector)[1] > 0) ? TRUE : FALSE )

#define MIN(a, b) ( (a < b) ? a : b )
#define MAX(a, b) ( (a > b) ? a : b )

#define RETURN_PY_NONE \
  Py_INCREF(Py_None); \
  return Py_None;

#define HANDLE_ERROR_WHEN_POSSIBLY_NOT_ALLOWED_TO_RAISE(allowed_to_raise) \
  if (allowed_to_raise) { \
    goto fail; \
  } else { \
    status = -1; \
    SUPPRESS_EXCEPTION; \
  }

/******************** HANDY ABBREVIATIONS:END ********************/

/******************** MODULE TYPE DEFINITIONS:BEGIN ********************/

/* See Appendix B of Interbase 6 API Guide for explanation of Interbase API
 * structures. */

typedef struct _AnyTracker {
  void *contained;
  struct _AnyTracker *next;
} AnyTracker;

extern PyTypeObject TransactionType;
extern PyTypeObject BlobReaderType;
extern PyTypeObject ConnectionType;
extern PyTypeObject CursorType;
extern PyTypeObject StandaloneTransactionHandleType;
extern PyTypeObject PreparedStatementType;
extern PyTypeObject EventConduitType;

struct _BlobReaderTracker;
struct _CursorTracker;
struct _CConnection;

typedef enum {
  TR_STATE_CREATED,
  TR_STATE_UNRESOLVED,
  TR_STATE_RESOLVED,
  TR_STATE_CLOSED,
  TR_STATE_CONNECTION_TIMED_OUT
} TransactionState;

typedef struct { /* Transaction */
  PyObject_HEAD /* Python API - infrastructural macro. */

  TransactionState state;

  /* Will be NULL if this Transaction has been close()d: */
  struct _CConnection *con;

  /* We maintain references to the CConnection instance and its Python wrapper
   * object (an instance of Python class kinterbasdb.Connection) separately.
   * The CConnection is what we actually use, but we need to ensure that the
   * Python wrapper object sticks around so that the CConnection remains
   * functional. */
  PyObject *con_python_wrapper;

  isc_tr_handle trans_handle; /* database API - transaction handle. */
  PyObject *group; /* distributed transaction group */

  PyObject *default_tpb;

  /* open_cursors serves the same purpose as open_blobreaders, but for Cursors.
   * See the documentation of open_blobreaders (below) for an overview. */
  struct _CursorTracker *open_cursors;

  /* Documentation for member open_blobreaders:
   *
   * The database server requires that open blob handles be closed explicitly,
   * lest there be leakage.  Many users of CPython, however, are used to
   * relying on the deterministic, reference-count-based finalization to close
   * open resources for them implicitly, as in:
   *   file('/blah/blah', 'rb').read()
   *
   * All operations on blob handles require an active database handle and
   * transaction handle, so it is clearly necessary to close any open
   * BlobReaders before resolving a transaction, lest those BlobReaders become
   * uncloseable and cause memory leakage in the server process.
   *
   * BlobReader_open adds the new BlobReader to the Transaction's tracker, and
   * BlobReader_close_with_unlink (which is called by the BlobReader destructor
   * and also explicitly callable by the client programmer) removes the
   * BlobReader from that tracker.
   *
   * However, if the transaction is about to be resolved, any BlobReaders that
   * have not yet been garbage collected or explicitly closed need to be closed
   * before the transaction ends.
   *
   * The obvious data structure with which a Transaction could track its open
   * BlobReaders is the Python dictionary, but that would create a cycle.
   * A BlobReader already maintains a reference to its Transaction, so it is
   * guaranteed that a BlobReader's Transaction will outlive the BlobReader.
   * If each BlobReader were added to a Python dictionary owned by the
   * Transaction, there'd be be a cycle.
   *
   * Python's garbage collector has special facilities for handling cyclic
   * garbage, but I'd rather not use them because they violate the determinism
   * that most CPython users expect from their garbage collector.  So I opted
   * for a C linked list as the data structure of the Transaction's tracker.
   * The number of BlobReaders tracked at any given time is likely to be very
   * small, so the poor lookup performance of the linked list is not likely to
   * cause significant performance degradation. */
  struct _BlobReaderTracker *open_blobreaders;

  LONG_LONG n_physical_transactions_started;
  LONG_LONG n_prepared_statements_executed_since_current_phys_start;
} Transaction;

typedef struct _TransactionTracker {
  Transaction *contained;
  struct _TransactionTracker *next;
} TransactionTracker;

/* Note the distinction between a Transaction object being:
 *  - "not closed" (that is, its close() method hasn't been called)
 * and
 *  - "active" (that is, a physical transaction has been started and not yet
 *    resolved) */
#define Transaction_is_not_closed(self) ((self)->state < TR_STATE_CLOSED)
#define Transaction_is_active(self) ((self)->state == TR_STATE_UNRESOLVED)

#define Transaction_con_timed_out(self) \
  ((self)->state == TR_STATE_CONNECTION_TIMED_OUT)

/* This structure supports a moderately ugly approach to determining the
 * precision of a given field (by querying the system tables--see
 * _kiconversion_field_precision.c). */
typedef struct {
  isc_stmt_handle stmt_handle_table;
  isc_stmt_handle stmt_handle_stored_procedure;

  XSQLDA *in_da; /* in_da will point to in_da_mem. */
    char in_da_mem[XSQLDA_LENGTH(2)];

  XSQLDA *out_da; /* out_da will point to out_da_mem. */
    char out_da_mem[XSQLDA_LENGTH(1)];
    /* out_da->sqlvar->sqldata will point to out_var_sqldata. */
    short out_var_sqldata;
    /* out_da->sqlvar->sqlind will point to out_var_sqlind. */
    short out_var_sqlind;

  PyObject *result_cache;
} CursorDescriptionCache;


/* Predeclare these so CConnection can refer to them: */
#ifdef ENABLE_CONNECTION_TIMEOUT
  struct _ConnectionTimeoutParams;
#endif

typedef enum {
  CON_STATE_CLOSED = 0,
  CON_STATE_OPEN = 1
} ConnectionState;

#define Connection_is_closed(con) ((con)->state == CON_STATE_CLOSED)

typedef struct _CConnection { /* definition of type CConnection */
  PyObject_HEAD /* Python API - infrastructural macro. */

  ConnectionState state;

  /* CConnection is merely an opaque structure manipulated by the Python
   * class kinterbasdb.Connection.  Some code, such as the BlobReader
   * constructor, needs to be able to find a reference to the Python
   * kinterbasdb.Connection instance given a pointer to its associated
   * CConnection instance.
   * In python_wrapper_obj, we maintain a borrowed reference to our Python
   * companion.  It's safe to maintain the reference without owning it because
   * this opaque CConnection is guaranteed not to outlive its companion
   * Python kinterbasdb.Connection instance.  Conveniently, maintaining a
   * *borrowed* reference prevents the creation of a cycle. */
  PyObject *python_wrapper_obj; /* 2005.06.19 */

  unsigned short   dialect;
  isc_db_handle    db_handle; /* Interbase API - database handle. */

  /* Added separate Transaction class in January 2007 for KIDB 3.3: */
  Transaction *main_trans;
  struct _TransactionTracker *transactions;

  /* Buffer used by Interbase API to store error status of calls. */
  ISC_STATUS       status_vector[STATUS_VECTOR_SIZE];

  #if (defined(ENABLE_FIELD_PRECISION_DETERMINATION) && !defined(INTERBASE_7_OR_LATER))
  CursorDescriptionCache *desc_cache;
  #endif

  /* 2005.06.21: */
  /* Nested Python dictionary of the following form:
   *   {'RELATION_NAME_0': {'FIELD_NAME_0': characterSetId, ...}, ...}
   * This allows us to avoid the overhead of calling isc_blob_lookup_desc
   * every time a blob is handled when treat_subtype_text_as_text is true. */
  PyObject *blob_charset_cache;

  /* Dynamic type translation support fields: */
  PyObject *type_trans_in;
  PyObject *type_trans_out;

  PyObject *output_type_trans_return_type_dict;

  #ifdef ENABLE_CONNECTION_TIMEOUT
    struct _ConnectionTimeoutParams *timeout;
    #define Connection_timeout_enabled(con) \
      ((boolean) ((con)->timeout != NULL))
  #else
    #define Connection_timeout_enabled(con) FALSE
  #endif

  char *dsn; /* Data source name */
  short dsn_len;
  char *dpb; /* Database parameter buffer */
  short dpb_len;
} CConnection;


#define CONN_REQUIRE_OPEN(connection) \
  if ( Connection_require_open(connection, NULL) != 0 ) \
    return NULL;

#define CONN_REQUIRE_OPEN2(connection, failure_message) \
  if ( Connection_require_open(connection, failure_message) != 0 ) \
    return NULL;


typedef enum {
  blob_mode_stream      =  1,
  blob_mode_materialize =  2
} BlobMode;

typedef enum {
  BLOBREADER_STATE_LIMBO,
  BLOBREADER_STATE_OPEN,
  BLOBREADER_STATE_CLOSED,
  BLOBREADER_STATE_CONNECTION_TIMED_OUT
} BlobReaderState;

typedef struct { /* definition of type BlobReader */
  PyObject_HEAD /* Python API - infrastructural macro. */

  BlobReaderState state;

  Transaction *trans;
  /* TAG:TRANSACTION_SUBORDINATE_OBJECT_SURVIVAL_BYPASS: */
  PyObject *con_python_wrapper;

  isc_blob_handle blob_handle;

  /* The maximum blob size supported by current versions of the engine is
   * 2GB, so signed 32-bit int is appropriate here (in current versions of the
   * engine, ISC_LONG is the same as int). */
  ISC_LONG total_size;
  unsigned short max_segment_size;

  ISC_LONG pos;
  int iter_chunk_size;
} BlobReader;

typedef struct _BlobReaderTracker {
  BlobReader *contained;
  struct _BlobReaderTracker *next;
} BlobReaderTracker;

/* The following BlobReader/BlobReaderTracker support stuff must be declared
 * early: */
#define DTT_BLOB_CONFIG_INVALID -1
#define DTT_BLOB_CONFIG_VALID    0

/* Not upper case because it's "method-like": */
#define BlobReader_is_open(self) ((self)->state == BLOBREADER_STATE_OPEN)
#define BlobReader_con_timed_out(self) \
  ((self)->state == BLOBREADER_STATE_CONNECTION_TIMED_OUT)

#define OBJECT_IS_VALID_TYPE_FOR_PREPARED_STATEMENT_SQL(o) \
  ((o) != NULL && PyString_CheckExact((o)))

typedef enum {
  PS_STATE_CREATED = 0, /* Before isc_dsql_allocate_statement, etc. */
  PS_STATE_OPEN,
  PS_STATE_CLOSED,  /* After isc_dsql_free_statement(..., DSQL_close) */
  PS_STATE_DROPPED,  /* After isc_dsql_free_statement(..., DSQL_drop) */
  PS_STATE_CONNECTION_TIMED_OUT
} PreparedStatementState;

typedef struct {
  short sqltype;
  short sqllen;
} OriginalXSQLVARSpecificationCache;

typedef struct { /* definition of type PreparedStatement */
  PyObject_HEAD /* Python API - infrastructural macro. */

  PreparedStatementState state;
  boolean for_internal_use;

  isc_stmt_handle stmt_handle;
  PyObject *sql;

  struct _Cursor *cur;

  int statement_type;
  #define NULL_STATEMENT_TYPE -1

  /* Database API - containers for input and result parameter structures. */
  XSQLDA *in_sqlda;
    /* A short for each input XSQLVAR (each XSQLVAR's ->sqlind will point to
     * a corresponding slot in in_sqlda_sqlind_array). */
    short *in_sqlda_sqlind_array;

  XSQLDA *out_sqlda;

  /* Supporting structure for "implicit parameter conversion": */
  /* The in_var_orig_spec cache variable allows us to record the original
   * values supplied by the database engine for each XSQLVAR's sqltype and
   * sqllen fields.  These values are reset in cases of implicit parameter
   * conversion (or in *any* case with CHAR/VARCHAR fields), but the original
   * settings must be
   *   remembered (see _Cursor_prepare_statement_if_necessary in _kinterbasdb.c)
   * !AND!
   *   restored (see convert_input_parameters in _kiconversion.c)
   * before attempting to convert each incoming parameter list.
   *
   *   For example, suppose a VARCHAR field is defined to be 20 characters at
   * most.  Suppose the first row of input parameters to an insert statement
   * supplies a string value that is only 15 characters long.  In this case,
   * the incoming parameter conversion function (PyObject2XSQLVAR) does the
   * following:
   * 1. sets the XSQLVAR's sqltype flag to SQL_TEXT (was SQL_VARYING)
   * 2. sets the XSQLVAR's sqldata pointer to point to the internal buffer
   *   of the Python string (was NULL)
   * 3. sets the XSQLVAR's sqllen field to indicate the length of that
   *   specific value, which is 15 (was the maximum length of the field, 20).
   *
   *   Suppose that the next row of input parameters includes a 17-character
   * string for the same field.  Unless the original sqllen value (20) has been
   * restored, the range checking code will conclude that the field has a
   * maximum length of 15 characters (which just happened to be the lentgh of
   * the last value, now stored in the XSQLVAR's sqllen flag).
   *   With the XSQLVAR's original sqltype and sqllen flags restored to their
   * pre-implicit-conversion values (sqltype SQL_VARYING and sqllen 20, in the
   * running example), everything proceeds fine. */
  OriginalXSQLVARSpecificationCache *in_var_orig_spec;

  /* Utility buffer where query result values from the database reside before
   * they're converted to Python values. */
  char *out_buffer;

  PyObject *description;

} PreparedStatement;

/* PSCache is used *solely* for internal PreparedStatement caching when
 * Cursor.execute is called with a string argument rather than a
 * PreparedStatement.  Notice that storage is via array, not linked list. */
typedef struct {
  PreparedStatement **container;
  unsigned short capacity;
  unsigned short start;
  PreparedStatement *most_recently_found;
} PSCache;
#define PREP_STMT_CACHE_CAPACITY 32

/* PSTracker is used *solely* for tracking PreparedStatements
 * created by the client programmer via Cursor.prep.  We must ensure that all
 * PreparedStatements opened on a Cursor are closed before the Cursor
 * closes.  Notice that storage is via linked list, not array. */
typedef struct _PreparedStatementTracker {
  PreparedStatement *contained;
  struct _PreparedStatementTracker *next;
} PSTracker;


typedef enum {
  CURSOR_STATE_CREATED = 0,
  CURSOR_STATE_OPEN = 1,
  CURSOR_STATE_CLOSED = 2,
  CURSOR_STATE_DROPPED = 3
} CursorState;

#define Cursor_is_open(cur) ((cur)->state <= CURSOR_STATE_OPEN)

typedef struct _Cursor { /* definition of type Cursor */
  PyObject_HEAD /* Python API - infrastructural macro. */

  CursorState state;

  Transaction *trans;
  /* TAG:TRANSACTION_SUBORDINATE_OBJECT_SURVIVAL_BYPASS: */
  PyObject *con_python_wrapper;

  PreparedStatement *ps_current;
  /* See notes at declarations of these types.  A given PreparedStatement will
   * only ever be in one of the following containers, not both. */
  PSCache ps_cache_internal;
  PSTracker *ps_tracker;

  /* Support named cursors with "SELECT ... FOR UPDATE" syntax. */
  PyObject *name;

  /* Python DB API "arraysize" read/write attribute: */
  Py_ssize_t arraysize;
  #define PYTHONDBAPI_DEFAULT_ARRAYSIZE 1

  PyObject *objects_to_release_after_execute;

  /* Member to support caching a single row of results from a stored procedure
   * invoked using EXECUTE PROCEDURE syntax.  This approach is exceptionaly
   * clumsy, but necessary in order to deal with engine API quirks and still
   * comply with the Python DB API 2.0.
   * (See special cases involving isc_info_sql_stmt_exec_procedure in
   * functions Cursor_execute and _Cursor_fetch.) */
  PyObject *exec_proc_results;

  /* last_fetch_status preservers the return code of the last isc_dsql_fetch
   * call made with this cursor, in order to assist in bridging a gap between
   * the engine API and the Python DB API.
   *
   * The Python DB API requires that extraneous fetches after a result set has
   * been exhausted be tolerated, whereas the IB API raises an error in such
   * cases.  This flag allows kinterbasdb to detect this situation and handle
   * it in the Pythonic way, instead of calling isc_dsql_fetch and encountering
   * an error. */
  ISC_STATUS last_fetch_status;
  #define NO_FETCH_ATTEMPTED_YET   -1
  #define RESULT_SET_EXHAUSTED    100

  /* Dynamic type translation support fields: */
  PyObject *type_trans_in;
  PyObject *type_trans_out;

  /* A dictionary that maps DTT slot name to the type returned for fields that
   * fall under that slot.  E.g., {'FIXED': decimal.Decimal} */
  PyObject *output_type_trans_return_type_dict;

  /* Buffer used by database API to store error status of calls. */
  ISC_STATUS status_vector[STATUS_VECTOR_SIZE];
} Cursor;

typedef struct _CursorTracker {
  Cursor *contained;
  struct _CursorTracker *next;
} CursorTracker;


/* Distributed transaction support: */

/* ISC_TEB:  See IB6 API Guide page 71. */
typedef struct teb {
  long *db_ptr;
  long tpb_len;
  char *tpb_ptr;
} ISC_TEB;

typedef struct { /* definition of type StandaloneTransactionHandle */
  PyObject_HEAD /* Python API - infrastructural macro. */

  isc_tr_handle native_handle;
} StandaloneTransactionHandle;

#define StandaloneTransactionHandle_Check(obj) \
  ((obj)->ob_type == &StandaloneTransactionHandleType)


/******************** MODULE TYPE DEFINITIONS:END ********************/

/******************** FUNCTION PROTOTYPES:BEGIN ********************/

typedef enum {
  INPUT_OK = 0,
  INPUT_ERROR = -1
} InputStatus;


#ifndef _MSC_VER
void init_kinterbasdb(void);
#endif /* _MSC_VER */


extern void raise_sql_exception(PyObject *exc, const char *where,
    ISC_STATUS pvector[]
  );

extern void raise_exception(PyObject *exc, const char *description);

/******************** FUNCTION PROTOTYPES:END ********************/

/* ISWEC stands for "Initialize String With Error Check": */
#define ISWEC(varName, stringConst) \
  varName = PyString_FromString(stringConst); \
  if (varName == NULL) { goto fail; }


/* Cast away the 'volatile' qualifier to silence compiler warning: */
#define DEVOLATILE(typ, v) ((typ) (v))

#define DV_PYO(PyObject_ptr) DEVOLATILE(PyObject *, PyObject_ptr)
#define DV_CCON(con) ((CConnection *) (con))

#endif /* not def _KINTERBASDB_H */
