/* KInterbasDB Python Package - Implementation of 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>
 */

#include "_kinterbasdb.h"

#if (defined(ENABLE_DB_EVENT_SUPPORT) || defined(ENABLE_CONNECTION_TIMEOUT))
  #include "_kisupport.h"
  #include "_kisupport_platform.c"
#endif

#ifdef ENABLE_DB_EVENT_SUPPORT
  #ifndef ENABLE_CONCURRENCY
    /* setup.py should've already enforced this: */
    #error "ENABLE_CONCURRENCY is required for ENABLE_DB_EVENT_SUPPORT."
  #endif

  #include "_kievents.h"
#endif

#include "_kilock.h"

/****************** "PRIVATE" DECLARATIONS:BEGIN *******************/
static int Connection_require_open(CConnection *self, char *failure_message);
static int CConnection_clear_ps_description_tuples(CConnection *);
/****************** "PRIVATE" DECLARATIONS:END *******************/

/******************** GLOBAL VARIABLES:BEGIN ********************/
/* These min/max constants are initialized in init_kinterbasdb, and retained
 * throughout the life of the process. */
static PyObject *py_SHRT_MIN = NULL;
static PyObject *py_SHRT_MAX = NULL;

static PyObject *py_INT_MIN = NULL;
static PyObject *py_INT_MAX = NULL;

static PyObject *py_LONG_MIN = NULL;
static PyObject *py_LONG_MAX = NULL;

static PyObject *py_LONG_LONG_MIN = NULL;
static PyObject *py_LONG_LONG_MAX = NULL;

static PyObject *py_PY_SSIZE_T_MIN = NULL;
static PyObject *py_PY_SSIZE_T_MAX = NULL;

/* Global references to this module's exception type objects, as documented in
 * the Python DB API (initialized by init_kidb_exceptions). */
static PyObject *Warning                   = NULL;
static PyObject *Error                     = NULL;
static PyObject *InterfaceError            = NULL;
static PyObject *DatabaseError             = NULL;
static PyObject *DataError                 = NULL;
static PyObject *OperationalError          = NULL;
static PyObject *TransactionConflict       = NULL;
#ifdef ENABLE_DB_EVENT_SUPPORT
  static PyObject *ConduitWasClosed        = NULL;
#endif
#ifdef ENABLE_CONNECTION_TIMEOUT
  static PyObject *ConnectionTimedOut      = NULL;
#endif
static PyObject *IntegrityError            = NULL;
static PyObject *InternalError             = NULL;
static PyObject *ProgrammingError          = NULL;
static PyObject *NotSupportedError         = NULL;


/* 2005.11.04: */
static int global_concurrency_level = UNKNOWN_CONCURRENCY_LEVEL;
#ifdef ENABLE_CONCURRENCY
  PyThread_type_lock _global_db_client_lock = NULL;
  /* _global_db_client_lock is not a PyObject *, but we need to pass it to the
   * _kiservices module through Python, so we wrap it in a PyCObject: */
  PyObject *_global_db_client_lock__python_Wrapper = NULL;
#endif

/* Reference to an object of ConnectionType that is used as a strongly typed
 * None: */
static CConnection *null_connection;

/* This Python callable is used by the Cursor implementation to transform
 * ordinary fetch* rows into fetch*map rows. */
static PyObject *py_RowMapping_constructor = NULL;
static PyObject *py__make_output_translator_return_type_dict_from_trans_dict = NULL;
static PyObject *py_look_up_array_descriptor = NULL;
static PyObject *py_look_up_array_subtype = NULL;
static PyObject *pyob_Cursor_execute_exception_type_filter = NULL;
static PyObject *pyob_validate_tpb = NULL;
static PyObject *pyob_trans_info = NULL;

/* These Python string objects are created when the module loads, then used by
 * multiple subsystems throughout the life of the process: */
static PyObject *shared___s__C_con; /* _C_con */
static PyObject *shared___s__main_trans; /* _main_trans */
static PyObject *shared___s_ascii; /* ascii (canonical name of ASCII codec) */
static PyObject *shared___s_charset; /* _charset */
static PyObject *shared___s_DB_CHAR_SET_NAME_TO_PYTHON_ENCODING_MAP;
static PyObject *shared___s_execute_immediate; /* execute_immediate */
static PyObject *shared___s_strict; /* strict */

/******************** GLOBAL VARIABLES:END ********************/

/******************** IMPLEMENTATION:BEGIN ********************/
PyObject *pyob_TrackerToList(AnyTracker *tracker);

/* CConnection-related: */
#ifdef ENABLE_CONNECTION_TIMEOUT
  static int Connection_close_from_CTT(volatile CConnection *con);

  /* When a Connection times out, it cuts loose certain terminal objects
   * (BlobReaders and PreparedStatements) which cannot under any circumstance
   * be transparently resumed.  An attempt by the client programmer to use
   * such an object should raise ConnectionTimedOut rather than
   * ProgrammingError.  To implement that, we copy pointers to all BlobReaders
   * and PreparedStatements attached to a Connection before we attempt to time
   * the Connection out.  After timing it out, we then update the state
   * indicator of the non-resumable objects so that they can raise
   * ConnectionTimedOut if the client programmer attempts to use them again. */
  static BlobReader **Connection_copy_BlobReader_pointers(
      volatile CConnection *con, Py_ssize_t *count
    );
  static void Connection_former_BlobReaders_flag_timeout_and_free(
      BlobReader **blob_readers, Py_ssize_t count
    );

  #define Connection_former_BlobReader_pointers_free(blob_readers) \
    kimem_main_free(blob_readers)

  static PreparedStatement **Connection_copy_PreparedStatement_pointers(
      volatile CConnection *con, Py_ssize_t *count
    );
  static void Connection_former_PreparedStatements_flag_timeout_and_free(
      PreparedStatement **prepared_statements, Py_ssize_t count
    );
  #define Connection_former_PreparedStatement_pointers_free(prepared_statements) \
    kimem_main_free(prepared_statements)
#endif /* ENABLE_CONNECTION_TIMEOUT */

static int Connection_attach_from_members(CConnection *con
    #ifdef ENABLE_CONNECTION_TIMEOUT
      , struct _ConnectionTimeoutParams *tp
    #endif
  );
static PyObject *pyob_Connection_x_info(
    boolean for_isc_database_info, isc_tr_handle *trans_handle_p,
    PyObject *self, PyObject *args
  );
static boolean Connection_has_any_open_transaction(CConnection *con);

/* Transaction-related: */
boolean Transaction_is_main(Transaction *self) {
  assert (self != NULL);
  assert (self->con != NULL);
  return (self->con->main_trans == self);
} /* Transaction_is_main */
static PyObject *pyob_Transaction_get_default_tpb(Transaction *self);

typedef enum {
  OP_COMMIT   =  1,
  OP_ROLLBACK =  0
} WhichTransactionOperation;

typedef enum {
  OP_RESULT_OK     =   0,
  OP_RESULT_ERROR  =  -1
} TransactionalOperationResult;

static void Transaction_dist_trans_indicate_resultion(
    Transaction *self, PyObject *group, const boolean is_resolved
  );
static int change_resolution_of_all_con_main_trans(
    PyObject *group, PyObject *cons, const boolean is_resolved
  );

static TransactionalOperationResult Transaction_commit_or_rollback(
    const WhichTransactionOperation op, Transaction *self,
    const boolean retaining, const boolean allowed_to_raise
  );

/* BlobReader-related: */
static int BlobReader_untrack(BlobReader *, boolean);

/* These streaming blob support functions are declared here rather than in
 * _kinterbasdb.h so as to avoid compiler warnings that they're "declared but
 * never defined" when the _kiservices compilation unit is built. */
static int validate_nonstandard_blob_config_dict(PyObject *,
    BlobMode *, boolean *
  );
static int BlobReaderTracker_release(BlobReaderTracker **);

static boolean _check_statement_length(Py_ssize_t);

static PyObject *pyob_Cursor_close(Cursor *self);
static int _Cursor_require_open(Cursor *self, char *failure_message);
static int Cursor_clear(Cursor *, boolean);
static void Cursor_clear_and_leave_open(Cursor *);
static int Cursor_ensure_PSCache(Cursor *self);
static int Cursor_close_prepared_statements(Cursor *self,
    const boolean allowed_to_raise, const boolean clear_ps_superior_refs
  );
static int Cursor_close_without_unlink(Cursor *, boolean);
static int Cursor_close_with_unlink(Cursor *, boolean);

static int CursorTracker_release(CursorTracker **);

typedef int (* CursorTrackerMappedFunction)(CursorTracker *, CursorTracker *);
static int Cursor_clear_ps_description_tuples(Cursor *);

typedef int (* PSTrackerMappedFunction)(PSTracker *, PSTracker *);
static int PSTracker_traverse(PSTracker *, PSTrackerMappedFunction);

/* This header file needs to be included whether connection timeout is enabled
 * or not, because (potentially empty versions of) some macros must be
 * provided: */
#include "_kicore_connection_timeout.h"

#include "_kisupport_time.c"
#include "_kinterbasdb_exception_functions.c"

#include "_kicore_transaction_support.c"
#include "_kicore_transaction_distributed.c"
#include "_kicore_transaction.c"
#include "_kiconversion.c"

#ifdef ENABLE_CONNECTION_TIMEOUT
  static TransactionalOperationResult
    Connection_resolve_all_transactions_from_CTT(
      volatile CConnection *con, const WhichTransactionOperation trans_op
    );

  #include "_kicore_connection_timeout.c"
#endif
#include "_kicore_connection.c"

#include "_kicore_xsqlda.c"
#include "_kicore_preparedstatement.c"
#include "_kicore_cursor.c"
#include "_kicore_create_drop_db.c"

#ifdef ENABLE_DB_EVENT_SUPPORT
  #include "_kinterbasdb_exception_functions_without_python.c"
  #include "_kievents.c"
#endif

static PyObject *pyob_provide_refs_to_python_entities(
    PyObject *self, PyObject *args
  )
{
  if (!PyArg_ParseTuple(args, "OOOOOOO",
       &py_RowMapping_constructor,
       &py__make_output_translator_return_type_dict_from_trans_dict,
       &py_look_up_array_descriptor,
       &py_look_up_array_subtype,
       &pyob_Cursor_execute_exception_type_filter,
       &pyob_validate_tpb,
       &pyob_trans_info
     ))
  { return NULL; }

  #define REQ_CALLABLE(py_func) \
    if (!PyCallable_Check(py_func)) { \
      raise_exception(InternalError, #py_func " is not callable."); \
      py_func = NULL; \
      return NULL; \
    }
  REQ_CALLABLE(py_RowMapping_constructor);
  REQ_CALLABLE(py__make_output_translator_return_type_dict_from_trans_dict);
  REQ_CALLABLE(py_look_up_array_descriptor);
  REQ_CALLABLE(py_look_up_array_subtype);
  REQ_CALLABLE(pyob_Cursor_execute_exception_type_filter);
  REQ_CALLABLE(pyob_validate_tpb);
  REQ_CALLABLE(pyob_trans_info);

  /* We need these objects to live forever: */
  Py_INCREF(py_RowMapping_constructor);
  Py_INCREF(py__make_output_translator_return_type_dict_from_trans_dict);
  Py_INCREF(py_look_up_array_descriptor);
  Py_INCREF(py_look_up_array_subtype);
  Py_INCREF(pyob_Cursor_execute_exception_type_filter);
  Py_INCREF(pyob_validate_tpb);
  Py_INCREF(pyob_trans_info);

  RETURN_PY_NONE;
} /* pyob_provide_refs_to_python_entities */

static PyObject *pyob_concurrency_level_get(PyObject *self) {
  if (global_concurrency_level == UNKNOWN_CONCURRENCY_LEVEL) {
    raise_exception(ProgrammingError, "The concurrency level has not been set;"
        " that must be done explicitly or implicitly via the"
        " kinterbasdb.init function."
      );
    return NULL;
  }

  return PyInt_FromLong(global_concurrency_level);
} /* pyob_concurrency_level_get */

static PyObject *pyob_concurrency_level_set(PyObject *self, PyObject *args) {
  int tentative_level;
  if (!PyArg_ParseTuple(args, "i", &tentative_level)) { return NULL; }

  if (global_concurrency_level != UNKNOWN_CONCURRENCY_LEVEL) {
    raise_exception(ProgrammingError, "The concurrency level cannot be changed"
        " once it has been set.  Use kinterbasdb.init(concurrency_level=?) to"
        " set the concurrency level legally."
      );
    return NULL;
  }

  #ifndef ENABLE_CONCURRENCY
    if (tentative_level != 0) {
  #else
    if (tentative_level != 1 && tentative_level != 2) {
  #endif
      raise_exception(ProgrammingError, "Illegal concurrency level.");
      return NULL;
    }

  global_concurrency_level = tentative_level;

  RETURN_PY_NONE;
} /* pyob_concurrency_level_set */

#ifdef ENABLE_CONCURRENCY
static PyObject *pyob_Thread_current_id(PyObject *self) {
  LONG_LONG thread_id = (LONG_LONG) Thread_current_id();
  return PythonIntOrLongFrom64BitValue(thread_id);
} /* pyob_Thread_current_id */
#endif

static PyObject *pyob_isc_portable_integer(PyObject *self, PyObject *args) {
  char *raw_bytes;
  Py_ssize_t raw_len;
  LONG_LONG result;

  if (!PyArg_ParseTuple(args, "s#", &raw_bytes, &raw_len)) { goto fail; }

  if (raw_len != 8 && raw_len != 4 && raw_len != 2 && raw_len != 1) {
    raise_exception(InternalError,
        "pyob_isc_portable_integer: len(buf) must be in (1,2,4,8)"
      );
    goto fail;
  }

  ENTER_GDAL
  result = isc_portable_integer(
      #ifndef INTERBASE_7_OR_LATER
        (unsigned char *)
      #endif
      raw_bytes,
      (short) raw_len /* Cast is safe b/c already checked val. */
    );
  LEAVE_GDAL

  return PythonIntOrLongFrom64BitValue(result);

  fail:
    assert (PyErr_Occurred());
    return NULL;
} /* pyob_isc_portable_integer */

static PyObject *pyob_raw_timestamp_to_tuple(PyObject *self, PyObject *args) {
  char *raw_bytes;
  Py_ssize_t raw_len;

  if (!PyArg_ParseTuple(args, "s#", &raw_bytes, &raw_len)) { return NULL; }

  if (raw_len != 8) {
    raise_exception(ProgrammingError, "raw_timestamp_to_tuple argument must"
        " be str of length 8."
      );
    return NULL;
  }

  return conv_out_timestamp(raw_bytes);
} /* pyob_raw_timestamp_to_tuple */

/******************** IMPLEMENTATION:END ********************/

/********************** MODULE INFRASTRUCTURE:BEGIN ***********************/
static PyObject *init_kidb_basic_header_constants(PyObject *, PyObject *);

static int init_shared_string_constants(void) {
  #define INIT_SHARED_STRING_CONST(s) \
    shared___s_ ## s = PyString_FromString(#s); \
    if (shared___s_ ## s == NULL) { goto fail; }

  INIT_SHARED_STRING_CONST(_C_con);
  INIT_SHARED_STRING_CONST(_main_trans);
  INIT_SHARED_STRING_CONST(ascii);
  INIT_SHARED_STRING_CONST(charset);
  INIT_SHARED_STRING_CONST(DB_CHAR_SET_NAME_TO_PYTHON_ENCODING_MAP);
  INIT_SHARED_STRING_CONST(execute_immediate);
  INIT_SHARED_STRING_CONST(strict);

  return 0;

  fail:
    assert (PyErr_Occurred());
    return -1;
} /* init_shared_string_constants */

static PyMethodDef kinterbasdb_GlobalMethods[] = {
  { "init_kidb_basic_header_constants", init_kidb_basic_header_constants,
      METH_VARARGS
    },

  { "create_database",      pyob_create_database,         METH_VARARGS      },

 /*********** CConnection methods: ***********/
  { "Connection_drop_database",
                            pyob_Connection_drop_database,METH_VARARGS      },
  { "Connection_connect",   pyob_Connection_connect,      METH_VARARGS      },

  { "Connection_python_wrapper_obj_set",
    pyob_Connection_python_wrapper_obj_set,               METH_VARARGS      },

  { "Connection_close",     pyob_Connection_close,        METH_VARARGS      },

  { "Connection_database_info",
                            pyob_Connection_database_info,METH_VARARGS      },
  { "Connection_transaction_info",
                            pyob_Connection_transaction_info,
                                                          METH_VARARGS      },

  /* Dynamic type trans getters/setters for CConnection: */
  { "set_Connection_type_trans_out",
                            pyob_Connection_set_type_trans_out,
                                                          METH_VARARGS      },
  { "get_Connection_type_trans_out",
                            pyob_Connection_get_type_trans_out,
                                                          METH_VARARGS      },
  { "set_Connection_type_trans_in",
                            pyob_Connection_set_type_trans_in,
                                                          METH_VARARGS      },
  { "get_Connection_type_trans_in",
                            pyob_Connection_get_type_trans_in,
                                                          METH_VARARGS      },

  { "Connection_closed_get",pyob_Connection_closed_get,   METH_VARARGS      },

  #ifdef INTERBASE_6_OR_LATER
  { "Connection_dialect_get",
                            pyob_Connection_dialect_get,  METH_VARARGS      },
  { "Connection_dialect_set",
                            pyob_Connection_dialect_set,  METH_VARARGS      },
  #endif /* INTERBASE_6_OR_LATER */

  { "Connection_main_trans_get",
                            pyob_Connection_main_trans_get,
                                                          METH_VARARGS      },
  { "Connection_transactions_get",
                            pyob_Connection_transactions_get,
                                                          METH_VARARGS      },

  { "Connection_has_active_transaction",
                            pyob_Connection_has_active_transaction,
                                                          METH_VARARGS      },

  #ifdef ENABLE_DB_EVENT_SUPPORT
  { "EventConduit_create",  pyob_EventConduit_create,     METH_VARARGS      },
  #endif /* ENABLE_DB_EVENT_SUPPORT */

  /* Can apply to either a CConnection or a Cursor: */
  { "is_purportedly_open",  pyob_CursorOrConnection_is_purportedly_open,
      METH_VARARGS
    },

  /* StandaloneTransactionHandle methods: */
  { "distributed_begin",    pyob_distributed_begin,       METH_VARARGS      },
  { "distributed_prepare",  pyob_distributed_prepare,     METH_VARARGS      },
  { "distributed_commit",   pyob_distributed_commit,      METH_VARARGS      },
  { "distributed_rollback", pyob_distributed_rollback,    METH_VARARGS      },

  /* Module-level functions that allow the Python layer to manipulate certain
   * global variables within the C layer: */
  { "provide_refs_to_python_entities",
                            pyob_provide_refs_to_python_entities,
                                                          METH_VARARGS      },

  /* Concurrency level: */
  { "concurrency_level_get",(PyCFunction) pyob_concurrency_level_get,
                                                          METH_NOARGS       },
  { "concurrency_level_set", pyob_concurrency_level_set,  METH_VARARGS      },
  #ifdef ENABLE_CONCURRENCY
  { "thread_id",            (PyCFunction) pyob_Thread_current_id,
                                                          METH_NOARGS       },
  #endif

  #ifdef ENABLE_CONNECTION_TIMEOUT
  /* Connection timeouts: */
  { "ConnectionTimeoutThread_main",
                             pyob_ConnectionTimeoutThread_main,
                                                          METH_VARARGS      },
  { "CTM_halt",              (PyCFunction) pyob_CTM_halt, METH_NOARGS       },

  { "Connection__read_activity_stamps",
                             Connection__read_activity_stamps,
                                                          METH_VARARGS      },
  #endif /* ENABLE_CONNECTION_TIMEOUT */

  /* Connection_timeout_enabled should be available whether the feature is
   * compiled in or not: */
  { "Connection_timeout_enabled",
                             pyob_Connection_timeout_enabled,
                                                          METH_VARARGS      },

  /* General utility functions: */
  { "portable_int",          pyob_isc_portable_integer,    METH_VARARGS     },
  { "raw_timestamp_to_tuple",
                             pyob_raw_timestamp_to_tuple,  METH_VARARGS     },

  /* The end: */
  { NULL,                NULL                                               }
};

static int init_kidb_exceptions(PyObject *d) {
  /* Python provides no way to recover from errors encoutered during C
   * extension module import, so the error handling here is lame. */
  #define DEFINE_EXC(targetVar, superType) \
    targetVar = PyErr_NewException("kinterbasdb." #targetVar, superType, \
        NULL \
      ); \
    if (targetVar == NULL) { goto fail; } \
    if (PyDict_SetItemString(d, #targetVar, targetVar) != 0) { goto fail; }

  DEFINE_EXC(Warning, PyExc_StandardError);
  DEFINE_EXC(Error, PyExc_StandardError);
  DEFINE_EXC(InterfaceError, Error);
  DEFINE_EXC(DatabaseError, Error);
  DEFINE_EXC(DataError, DatabaseError);

  DEFINE_EXC(OperationalError, DatabaseError);
  DEFINE_EXC(TransactionConflict, OperationalError);

  #ifdef ENABLE_DB_EVENT_SUPPORT
    DEFINE_EXC(ConduitWasClosed, OperationalError);
  #endif

  #ifdef ENABLE_CONNECTION_TIMEOUT
    DEFINE_EXC(ConnectionTimedOut, OperationalError);
  #endif

  DEFINE_EXC(IntegrityError, DatabaseError);
  DEFINE_EXC(InternalError, DatabaseError);
  DEFINE_EXC(ProgrammingError, DatabaseError);
  DEFINE_EXC(NotSupportedError, DatabaseError);

  return 0;

  fail:
    /* Not much we do due to Python's feature-poor module initialization
     * infrastructure. */
    return -1;
} /* init_kidb_exceptions */

PyTypeObject ConnectionType = {
    PyObject_HEAD_INIT(NULL)

    0,
    "kinterbasdb.ConnectionType",
    sizeof(CConnection),
    0,
    (destructor) pyob_Connection___del__,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0
  };

PyTypeObject BlobReaderType = { /* new-style class */
    PyObject_HEAD_INIT(NULL)
    0,                                  /* ob_size */
    "kinterbasdb.BlobReader",           /* tp_name */
    sizeof(BlobReader),                 /* tp_basicsize */
    0,                                  /* tp_itemsize */
    (destructor) pyob_BlobReader___del__,   /* tp_dealloc */
    0,                                  /* tp_print */
    0,                                  /* tp_getattr */
    0,                                  /* tp_setattr */
    0,                                  /* tp_compare */
    (reprfunc) pyob_BlobReader_repr,    /* tp_repr */
    0,                                  /* tp_as_number */
    0,                                  /* tp_as_sequence */
    0,                                  /* tp_as_mapping */
    0,                                  /* tp_hash */
    0,                                  /* tp_call */
    0,                                  /* tp_str */
    0,                                  /* tp_getattro */
    0,                                  /* tp_setattro */
    0,                                  /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT,                 /* tp_flags */
    0,                                  /* tp_doc */
    0,		                              /* tp_traverse */
    0,		                              /* tp_clear */
    0,		                              /* tp_richcompare */
    0,		                              /* tp_weaklistoffset */
    0,		                              /* tp_iter */
    0,		                              /* tp_iternext */
    BlobReader_methods,                 /* tp_methods */
    NULL,                               /* tp_members */
    BlobReader_getters_setters,         /* tp_getset */
    0,                                  /* tp_base */
    0,                                  /* tp_dict */
    0,                                  /* tp_descr_get */
    0,                                  /* tp_descr_set */
    0,                                  /* tp_dictoffset */

    /* This type does not need to be instantiable from the Python level: */
    0,                                  /* tp_init */

    0,                                  /* tp_alloc */
    0,                                  /* tp_new */
  };

PyTypeObject StandaloneTransactionHandleType = {
    PyObject_HEAD_INIT(NULL)

    0,
    "kinterbasdb.StandaloneTransactionHandle",
    sizeof(StandaloneTransactionHandle),
    0,
    (destructor) StandaloneTransactionHandle___del__,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0
  };

DL_EXPORT(void)
init_kinterbasdb(void) {
  PyObject *m, *d;

  m = Py_InitModule("_kinterbasdb", kinterbasdb_GlobalMethods);
  if (m == NULL) { goto fail; }
  d = PyModule_GetDict(m);
  if (d == NULL) { goto fail; }

  ConnectionType.ob_type = &PyType_Type;
  CursorType.ob_type = &PyType_Type;
  StandaloneTransactionHandleType.ob_type = &PyType_Type;

  #ifdef VERBOSE_DEBUGGING
  if (PyModule_AddIntConstant(m, "VERBOSE_DEBUGGING", 1) != 0) { goto fail; }
  #endif
  if (PyModule_AddIntConstant(m, "FB_API_VER", FB_API_VER) != 0) {
    goto fail;
  }

  #ifdef ENABLE_CONCURRENCY
  {
    /* See documentation in _kilock.h */
    _global_db_client_lock = PyThread_allocate_lock();
    if (_global_db_client_lock == NULL) { goto fail; }

    _global_db_client_lock__python_Wrapper = PyCObject_FromVoidPtr(
        _global_db_client_lock,
        NULL /* No automatic collection. */
      );
    if (_global_db_client_lock__python_Wrapper == NULL) { goto fail; }

    PyObject_SetAttrString(m, "_global_db_client_lock__python_Wrapper",
        _global_db_client_lock__python_Wrapper
      );
  }
  #endif /* def ENABLE_CONCURRENCY */

  if (PyModule_AddIntConstant(m, "DEFAULT_CONCURRENCY_LEVEL",
        DEFAULT_CONCURRENCY_LEVEL
      ) != 0
    ) { goto fail; }

  /* Initialize module-global Python objects to hold min/max values for integer
   * types. */
  #define INITIALIZE_INT_BOUNDARY_CONST(name) \
    py_ ## name = PyInt_FromLong(name); \
    if (py_ ## name == NULL) { goto fail; }

  #define INITIALIZE_LONG_BOUNDARY_CONST(name) \
    py_ ## name = PyLong_FromLongLong(name); \
    if (py_ ## name == NULL) { goto fail; }

  #define INITIALIZE_PY_SSIZE_T_BOUNDARY_CONST(name) \
    py_ ## name = PyInt_FromSsize_t(name); \
    if (py_ ## name == NULL) { goto fail; }

  INITIALIZE_INT_BOUNDARY_CONST(SHRT_MIN);
  INITIALIZE_INT_BOUNDARY_CONST(SHRT_MAX);
  INITIALIZE_INT_BOUNDARY_CONST(INT_MIN);
  INITIALIZE_INT_BOUNDARY_CONST(INT_MAX);
  INITIALIZE_INT_BOUNDARY_CONST(LONG_MIN);
  INITIALIZE_INT_BOUNDARY_CONST(LONG_MAX);
  INITIALIZE_LONG_BOUNDARY_CONST(LONG_LONG_MIN);
  INITIALIZE_LONG_BOUNDARY_CONST(LONG_LONG_MAX);
  INITIALIZE_PY_SSIZE_T_BOUNDARY_CONST(PY_SSIZE_T_MIN);
  INITIALIZE_PY_SSIZE_T_BOUNDARY_CONST(PY_SSIZE_T_MAX);

  /* Expose the just-created integer boundary constants to the Python level
   * (in the private _k namespace) to facilitate bounds checking in Python.
   *
   * PyModule_AddObject steals a reference to the object being added, but we
   * need these to live forever, even if a meddling client programmer comes in
   * and deletes them from the namespace of the _k module, so INCREF. */
  #define EXPOSE_INT_BOUNDARY_CONSTANT(name) \
    if (PyModule_AddObject(m, #name, py_ ## name) != 0) { \
      goto fail; \
    } else { \
      Py_INCREF(py_ ## name); \
    }

  EXPOSE_INT_BOUNDARY_CONSTANT(SHRT_MIN);
  EXPOSE_INT_BOUNDARY_CONSTANT(SHRT_MAX);
  EXPOSE_INT_BOUNDARY_CONSTANT(INT_MIN);
  EXPOSE_INT_BOUNDARY_CONSTANT(INT_MAX);
  EXPOSE_INT_BOUNDARY_CONSTANT(LONG_MIN);
  EXPOSE_INT_BOUNDARY_CONSTANT(LONG_MAX);
  EXPOSE_INT_BOUNDARY_CONSTANT(LONG_LONG_MIN);
  EXPOSE_INT_BOUNDARY_CONSTANT(LONG_LONG_MAX);
  EXPOSE_INT_BOUNDARY_CONSTANT(PY_SSIZE_T_MIN);
  EXPOSE_INT_BOUNDARY_CONSTANT(PY_SSIZE_T_MAX);

  #ifdef ENABLE_CONNECTION_TIMEOUT
    if (init_kidb_connection_timeout(m) != 0) { return; }
  #endif

  if (init_shared_string_constants() != 0) {
    PyErr_SetString(PyExc_ImportError, "Unable to initialize shared strings.");
    return;
  }

  if (init_kidb_exceptions(d) != 0) {
    PyErr_SetString(PyExc_ImportError,
        "Unable to initialize kinterbasdb exceptions."
      );
    return;
  }

  if (init_kidb_exception_support() != 0) {
    PyErr_SetString(PyExc_ImportError,
        "Unable to initialize kinterbasdb exception support code."
      );
    return;
  }

  if (init_kidb_transaction_support() != 0) {
    PyErr_SetString(PyExc_ImportError,
        "Unable to initialize kinterbasdb transaction support code."
      );
    return;
  }

  #define _INIT_C_TYPE_AND_SYS(type_name, init_func) { \
    int status = init_func(); \
    if (status == 0) { \
      /* Expose the class in the namespace of the _k module. \
       * PyModule_AddObject steals a ref, so the artificial INCREF prevents \
       * a client programmer from messing up the refcount with a statement \
       * like 'del kinterbasdb._k.ClassName'. */ \
      Py_INCREF(&type_name ## Type); \
      status = PyModule_AddObject(m, \
          #type_name, (PyObject *) &type_name ## Type \
        ); \
    } \
    if (status != 0) { \
      assert (PyErr_Occurred()); \
      /* Python's module loader doesn't really give us any error handling \
       * options: */ \
      return; \
    } \
  }

  _INIT_C_TYPE_AND_SYS(Transaction, init_kidb_transaction);
  _INIT_C_TYPE_AND_SYS(BlobReader, init_kidb_nonstandard_blob_support);
  _INIT_C_TYPE_AND_SYS(PreparedStatement, init_kidb_preparedstatement);
  _INIT_C_TYPE_AND_SYS(Cursor, init_kidb_cursor);
  #ifdef ENABLE_DB_EVENT_SUPPORT
    _INIT_C_TYPE_AND_SYS(EventConduit, init_kidb_event_system);

    /* Expose the following event-related constants solely for the sake of code
     * that tests boundary conditions: */
    if (PyModule_AddIntConstant(m, "EVENT_BLOCK_SIZE", EVENT_BLOCK_SIZE) != 0) {
      return;
    }
  #endif

  if (init_kidb_type_translation() != 0) {
    PyErr_SetString(PyExc_ImportError,
        "Unable to initialize kinterbasdb type translation."
      );
    return;
  }

  /* DSR added null_connection when moving connection state detection from the
   * Python level to the C level.  From the perspective of the Python-level
   * kinterbasdb code, _kinterbasdb.null_connection is a null value like
   * Python's None, except that it is of type ConnectionType instead of
   * NoneType.
   *   The kinterbasdb.Connection Python class can set its reference to its
   * equivalent C type (self._C_conn) to _kinterbasdb.null_connection to
   * indicate that the underlying connection is no longer valid.  Then the
   * pyob_* functions in this C code that demand an argument of ConnectionType
   * are satisfied by null_connection long enough to detect that it is not open
   * (and that therefore the requested operation is not allowed). */
  null_connection = Connection_create();
  if (null_connection == NULL) {
    PyErr_SetString(PyExc_ImportError, "Unable to create null_connection.");
    return;
  }
  PyDict_SetItemString(d, "null_connection", (PyObject *) null_connection);

  return;

  fail:
    /* Python's module loader doesn't support clean recovery from errors, so
     * there's nothing we can do except attempt to avoid a segfault by
     * returning if a required object cannot be created. */
    return;
} /* init_kinterbasdb */

#include "_kinterbasdb_constants.c"
/********************** MODULE INFRASTRUCTURE:END ***********************/
