/* KInterbasDB Python Package - Implementation of Services Manager Support
 *
 * 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 "_kiservices.h"

#define SPB_BOILERPLATE_SIZE 2
#define INFINITE_TIMEOUT -1

/* Set the following to a very small value if you want to test result buffer
 * truncation handling. */
#define SERVICE_RESULT_BUFFER_INITIAL_SIZE ((unsigned short) 1024)

/* Types of query output handling that pyob_query_base can be instructed to
 * perform (these constants are made accessible to Python in function
 * _init_kiservices_ibase_header_constants): */
#define QUERY_TYPE_PLAIN_STRING          1
#define QUERY_TYPE_PLAIN_INTEGER         2
#define QUERY_TYPE_RAW                   3

/* The following is a version of the ibase.h-standard macro ADD_SPB_NUMERIC
 * that doesn't generate compiler warnings.  This custom version is probably a
 * little slower, but this macro isn't used in performance-intensive areas
 * anyway. */

#define ADD_SPB_NUMERIC_DSR(buf_pos, data) \
  memcpy(buf_pos, &data, sizeof(unsigned long)); \
  buf_pos += sizeof(unsigned long);

/******************** GLOBAL VARIABLES:BEGIN ***********************/
boolean initialized = FALSE;

#include "_kilock.h"
#ifdef ENABLE_CONCURRENCY
  /* Values are transferred from the _kinterbasdb shared lib in
   * pyob_initialize_from before any of kinterbasdb's Services API code is
   * actually executed: */
  int global_concurrency_level = UNKNOWN_CONCURRENCY_LEVEL;
  PyThread_type_lock _global_db_client_lock = NULL;
#endif /* def ENABLE_CONCURRENCY */

/* Global references to the DB API exception objects from _kinterbasdb.
 * References to the exception objects are transferred here by the
 * pyob_initialize_from function so that code in this module can access these
 * exceptions in order to raise them. */
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;
static PyObject *IntegrityError            = NULL;
static PyObject *InternalError             = NULL;
static PyObject *ProgrammingError          = NULL;
static PyObject *NotSupportedError         = NULL;

#include "_kinterbasdb_exception_functions.c"
/******************** GLOBAL VARIABLES:END ***********************/

/******************** PRIVATE FUNCTION PROTOTYPES:BEGIN ********************/
static PyObject *pyob_initialize_from(PyObject *self, PyObject *args);

static PyObject *pyob_SConnection_connect(PyObject *self, PyObject *args);
static PyObject *pyob_SConnection_close(PyObject *self, PyObject *args);
static int SConnection_close(
    ServicesConnectionObject *con, boolean allowed_to_raise
  );
static void pyob_SConnection___del__(PyObject *con);

static PyObject *pyob_isc_vax_integer(PyObject *self, PyObject *args);
static PyObject *pyob_query_base(PyObject *self, PyObject *args);
/******************** PRIVATE FUNCTION PROTOTYPES:END ********************/

/****** SERVICES MANAGER CONNECTION CREATION/DELETION FUNCTIONS:BEGIN *******/
static PyObject *pyob_SConnection_connect(PyObject *self, PyObject *args) {
  ServicesConnectionObject *con = NULL;

  char *service_manager_name = NULL;
  Py_ssize_t service_manager_name_len = -1;
  char *username = NULL;
  Py_ssize_t username_len = -1;
  char *password = NULL;
  Py_ssize_t password_len = -1;

  char *spb = NULL;
  char *spb_walk = NULL;
  size_t spb_length;

  if (!PyArg_ParseTuple(args, "z#z#z#",
         &service_manager_name, &service_manager_name_len,
         &username, &username_len,
         &password, &password_len
       )
     )
  { goto fail; }

  if (service_manager_name_len + username_len + password_len > 118) {
    raise_exception(ProgrammingError, "The combined length of the host, user,"
        " and password cannot exceed 118 bytes."
      );
    goto fail;
  }

  con = PyObject_New(ServicesConnectionObject, &ServicesConnectionType);
  if (con == NULL) { goto fail; }

  con->service_handle = NULL_SVC_HANDLE;

  spb_length =
    SPB_BOILERPLATE_SIZE
      + 1 /* the code isc_spb_user_name */
        + 1 /* the one-byte length username_len */
          + PYTHON_SIZE_TO_SIZE_T(username_len) /* the contents of username */
      + 1 /* the code isc_spb_password */
        + 1 /* the one-byte length password_len */
          + PYTHON_SIZE_TO_SIZE_T(password_len) /* the contents of password */
    ;
  if (spb_length > USHRT_MAX) {
    /* If the size of the username, the password, and a few other bytes exceeds
     * USHRT_MAX, it's certainly due to client programmer error: */
    raise_exception(ProgrammingError, "Service parameter buffer created to"
        " hold username and password were too large."
      );
    goto fail;
  }
  spb = kimem_main_malloc(spb_length);
  if (spb == NULL) { goto fail; }

  spb_walk = spb;

  /* SPB_BOILERPLATE_SIZE refers to the next two entries: */
  *spb_walk++ = isc_spb_version;
  *spb_walk++ = isc_spb_current_version;

  *spb_walk++ = isc_spb_user_name;
  /* Cast is safe b/c already checked val: */
  *spb_walk++ = (char) username_len;
  strncpy(spb_walk, username, PYTHON_SIZE_TO_SIZE_T(username_len));
  spb_walk += PYTHON_SIZE_TO_SIZE_T(username_len);

  *spb_walk++ = isc_spb_password;
  /* Cast is safe b/c already checked val: */
  *spb_walk++ = (char) password_len;
  strncpy(spb_walk, password, PYTHON_SIZE_TO_SIZE_T(password_len));
  spb_walk += PYTHON_SIZE_TO_SIZE_T(password_len);

  assert (spb_length == (size_t) (spb_walk - spb));

  LEAVE_GIL_WITHOUT_AFFECTING_DB
  ENTER_GDAL_WITHOUT_LEAVING_PYTHON
  ENTER_GCDL_WITHOUT_LEAVING_PYTHON

  isc_service_attach(con->status,
      /* Cast is safe b/c already checked val: */
      (unsigned short) service_manager_name_len,
      service_manager_name,
      &con->service_handle,
      /* Cast is safe b/c already checked val: */
      (unsigned short) spb_length,
      spb
    );

  LEAVE_GCDL_WITHOUT_ENTERING_PYTHON
  LEAVE_GDAL_WITHOUT_ENTERING_PYTHON
  ENTER_GIL_WITHOUT_AFFECTING_DB

  if (DB_API_ERROR(con->status)) {
    raise_sql_exception(OperationalError,
        "_kiservices.pyob_SConnection_connect: ", con->status
      );
    goto fail;
  }

  goto cleanup;

  fail:
    assert (PyErr_Occurred());

    Py_XDECREF((PyObject *) con);
    con = NULL;
    /* Fall through to cleanup. */

  cleanup:
    if (spb != NULL) { kimem_main_free(spb); }

    return (PyObject *) con;
} /* pyob_SConnection_connect */

static int SConnection_close(
    ServicesConnectionObject *con, boolean allowed_to_raise
  )
{
  if (con->service_handle != NULL_SVC_HANDLE) {
    LEAVE_GIL_WITHOUT_AFFECTING_DB
    ENTER_GDAL_WITHOUT_LEAVING_PYTHON
    ENTER_GCDL_WITHOUT_LEAVING_PYTHON

    isc_service_detach(con->status, &con->service_handle);

    LEAVE_GCDL_WITHOUT_ENTERING_PYTHON
    LEAVE_GDAL_WITHOUT_ENTERING_PYTHON
    ENTER_GIL_WITHOUT_AFFECTING_DB

    /* Set NULL to prevent segfault on "double jeopardy disconnect" (where
     * exception is raised by con.close(), then con.__del__ calls this function
     * again with an invalid service handle).
     * Note:  isc_service_detach apparently follows the model of other
     * detach/close functions in the Firebird C API, i.e. we need not manually
     * free the service handle's memory. */
    con->service_handle = NULL_SVC_HANDLE;

    if (DB_API_ERROR(con->status)) {
      raise_sql_exception(OperationalError, "_kiservices could not cleanly"
          " disconnect from the service manager: ",
          con->status
        );
      if (allowed_to_raise) {
        goto fail;
      } else {
        SUPPRESS_EXCEPTION;
      }
    }
  }

  assert (con->service_handle == NULL_SVC_HANDLE);
  return 0;

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

static PyObject *pyob_SConnection_close(PyObject *self, PyObject *args) {
  ServicesConnectionObject *con = NULL;

  if (!PyArg_ParseTuple(args, "O!", &ServicesConnectionType, &con)) { goto fail; }

  if (SConnection_close(con, TRUE) != 0) { goto fail; }

  RETURN_PY_NONE;

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

static void pyob_SConnection___del__(PyObject *con) {
  ServicesConnectionObject *_con = (ServicesConnectionObject *) con;

  SConnection_close(_con, FALSE); /* FALSE -> Ignore any errors in closing. */

  /* Due to the fact that it was called from this destructor context, where
   * error recovery isn't possible, the SConnection_close function should've set
   * _con->service_handle to NULL regardless of whether the close operation
   * succeeded. */
  assert (_con->service_handle == NULL_SVC_HANDLE);

  /* Free the memory of the ServicesConnectionObject struct itself: */
  PyObject_Del(con);
} /* pyob_SConnection___del__ */
/****** SERVICES MANAGER CONNECTION CREATION/DELETION FUNCTIONS:END *******/

/*********************** ACTION FUNCTIONS:BEGIN *****************************/

static PyObject *pyob_action_thin(PyObject *self, PyObject *args) {
  ServicesConnectionObject *con = NULL;
  char *request_buf = NULL;
  Py_ssize_t req_buf_size = -1;

  if (!PyArg_ParseTuple(args, "O!s#", &ServicesConnectionType, &con,
         &request_buf, &req_buf_size
       )
     )
  { goto fail; }

  if (req_buf_size > USHRT_MAX) {
    PyObject *err_msg = PyString_FromFormat(
        "The size of the request buffer must not exceed %d.", USHRT_MAX
      );
    if (err_msg == NULL) { goto fail; }
    raise_exception(ProgrammingError, PyString_AS_STRING(err_msg));
    Py_DECREF(err_msg);
    goto fail;
  }

  ENTER_GDAL
  isc_service_start(con->status,
      &con->service_handle,
      NULL,
      /* Cast is safe b/c already checked val: */
      (unsigned short) req_buf_size,
      request_buf
    );
  LEAVE_GDAL
  if (DB_API_ERROR(con->status)) {
    raise_sql_exception(OperationalError, "Unable to perform the requested"
        " Services API action: ", con->status
      );
    goto fail;
  }

  RETURN_PY_NONE;

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

/*********************** ACTION FUNCTIONS:END *****************************/

/********* SERVICES MANAGER QUERY FUNCTIONS:BEGIN **********/
static PyObject *pyob_query_base(PyObject *self, PyObject *args) {
  ServicesConnectionObject *con = NULL;
  char req_items[] = " ";
  int req_item;
  #define Q_P_STR_REQ_ITEM_COUNT ((unsigned short) 1)
  int query_return_type;
  long timeout = INFINITE_TIMEOUT;

  char spb[6];
  char *spb_walk = spb;

  char *raw_result = NULL;
  size_t raw_result_size;
  char *raw_result_walk;

  PyObject *py_ret = NULL;

  if (!PyArg_ParseTuple(args, "O!ii|l", &ServicesConnectionType, &con,
         &req_item, &query_return_type, &timeout
       )
     )
  { goto fail; }

  if (req_item < 0 || req_item > UCHAR_MAX) {
    PyObject *err_msg = PyString_FromFormat("The service query request_buf"
        " code must fall between 0 and %d, inclusive.", UCHAR_MAX
      );
    if (err_msg != NULL) {
      raise_exception(ProgrammingError, PyString_AS_STRING(err_msg));
      Py_DECREF(err_msg);
    }
    goto fail;
  }
  req_items[0] = (char) req_item;

  if (timeout != INFINITE_TIMEOUT) {
    *spb_walk++ = isc_info_svc_timeout;
    ADD_SPB_NUMERIC_DSR(spb_walk, timeout);
  }

  raw_result_size = SERVICE_RESULT_BUFFER_INITIAL_SIZE;
  /* Loop, enlarging the raw_result buffer, until the query's results can fit
   * in raw_result. */
  for (;;) {
    if (raw_result_size > USHRT_MAX) {
      raise_exception(InternalError, "Database C API constrains maximum"
          " result buffer size to USHRT_MAX."
        );
      goto fail;
    }

    raw_result = kimem_main_realloc(raw_result, raw_result_size);
    if (raw_result == NULL) { goto fail; }
    memset(raw_result, 0, raw_result_size);

    ENTER_GDAL
    isc_service_query(con->status,
        &con->service_handle,
        NULL,
        (unsigned short) (spb_walk - spb), spb,
        Q_P_STR_REQ_ITEM_COUNT, req_items,
        /* Cast is safe b/c already checked raw_result_size: */
        (unsigned short) raw_result_size, raw_result
      );
    LEAVE_GDAL
    if (DB_API_ERROR(con->status)) {
      raise_sql_exception(OperationalError, "_kiservices could not query: ",
          con->status
        );
      goto fail;
    }

    if (raw_result[0] == isc_info_truncated) {
      /* We need to allocate a bigger buffer because the service results
       * couldn't fit in the one we initially supplied. */
      raw_result_size *= 4;
      continue;
    }

    break; /* raw_result was big enough; move on. */
  } /* ends for loop */

  raw_result_walk = raw_result;
  assert (*raw_result_walk == req_items[0]);
  raw_result_walk++;


  switch (query_return_type) {
    case QUERY_TYPE_PLAIN_STRING:
    {
      /* The database C API currently constrains the size of result strings to
       * USHRT_MAX or less. */
      unsigned short res_len;

      ENTER_GDAL
      res_len = (unsigned short) isc_vax_integer(
          raw_result_walk, sizeof(unsigned short)
        );
      raw_result_walk += sizeof(unsigned short);
      LEAVE_GDAL

      py_ret = PyString_FromStringAndSize(NULL, res_len);
      if (py_ret == NULL) { goto fail; }

      strncpy(PyString_AS_STRING(py_ret), raw_result_walk, res_len);
      raw_result_walk += res_len;

      assert (*raw_result_walk == isc_info_end);
      break;
    }

    case QUERY_TYPE_PLAIN_INTEGER:
    {
      unsigned int return_value_as_uint;
      ENTER_GDAL
      return_value_as_uint = (unsigned int) isc_vax_integer(
          raw_result_walk, sizeof(unsigned int)
        );
      LEAVE_GDAL

      py_ret = PyInt_FromLong(return_value_as_uint);
      if (py_ret == NULL) { goto fail; }

      raw_result_walk += sizeof (unsigned int);

      assert (*raw_result_walk == isc_info_end);
      break;
    }

    case QUERY_TYPE_RAW:
      raw_result_walk = raw_result + (raw_result_size - 1);
      while (*raw_result_walk == '\0') {
        raw_result_walk--;
      }

      /* The return string might contain NULL bytes (Python strings have no
       * problem with that). */
      py_ret = PyString_FromStringAndSize(raw_result,
          SIZE_T_TO_PYTHON_SIZE(raw_result_walk - raw_result)
        );

      break;

    default:
      PyErr_SetString(PyExc_TypeError, "_kiservices.query_base is not equipped"
          " to handle this query type."
        );
      goto fail;
  }

  goto cleanup; /* Success. */

  fail:
    assert (PyErr_Occurred());

    if (py_ret != NULL) {
      Py_DECREF(py_ret);
      py_ret = NULL;
    }

  cleanup:
    if (raw_result != NULL) { kimem_main_free(raw_result); }

    return py_ret;
} /* pyob_query_base */

/********* SERVICES MANAGER QUERY FUNCTIONS:END **********/

/************** SERVICES UTILITIY FUNCTIONS:BEGIN ***************/

static PyObject *pyob_isc_vax_integer(PyObject *self, PyObject *args) {
  /* isc_vax_integer reverses the byte order of an integer.  This Python
   * wrapper is used in services.py when parsing the raw return buffer from a
   * Services Manager query. */
  char *raw_bytes;
  Py_ssize_t raw_len;
  int result;

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

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

  ENTER_GDAL
  result = isc_vax_integer(raw_bytes,
      /* Cast is safe b/c already checked val: */
      (unsigned short) raw_len
    );
  LEAVE_GDAL

  return PyInt_FromLong(result);

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

/************** SERVICES UTILITIY FUNCTIONS:END *****************/

/********* PYTHON TYPE OBJECT SETUP:BEGIN **********/

static PyTypeObject ServicesConnectionType = {
    PyObject_HEAD_INIT(NULL)

    0,
    "_kiservices.ServicesConnection",
    sizeof( ServicesConnectionObject ),
    0,
    pyob_SConnection___del__,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0
  }; /* ServicesConnectionType */

/********* PYTHON TYPE OBJECT SETUP:END **********/

/****************** MODULE ADMINISTRATION FUNCTIONS:BEGIN ********************/

static PyObject *pyob_is_initialized(PyObject *self) {
  return PyBool_FromLong(initialized);
} /* pyob_is_initialized */

static PyObject *pyob_initialize_from(PyObject *self, PyObject *args) {
  /* Makes kinterbasdb's global thread lock and references to its Python DB API
   * exception classes readily available to this C code. */
  PyObject *source = NULL;

  if (!PyArg_ParseTuple(args, "O", &source)) { goto fail; }

  #ifdef ENABLE_CONCURRENCY
  {
    PyObject *cl = PyObject_CallMethod(source, "concurrency_level_get", NULL);
    if (cl == NULL) { goto fail; }
    assert (PyInt_Check(cl));
    global_concurrency_level = (int) PyInt_AS_LONG(cl);
    Py_DECREF(cl);
  }

  {
    PyObject *lock_wrapper = PyObject_GetAttrString(source,
        "_global_db_client_lock__python_Wrapper"
      );
    if (lock_wrapper == NULL) { goto fail; }

    assert (_global_db_client_lock == NULL);
    _global_db_client_lock = PyCObject_AsVoidPtr(lock_wrapper);
    Py_DECREF(lock_wrapper);
    if (_global_db_client_lock == NULL) { goto fail; }
  }
  #endif /* def ENABLE_CONCURRENCY */

  #define LOAD_EXC_PTR(ex_name) \
    /* PyObject_GetAttrString returns a new reference; no need for us to \
     * INCREF. */ \
    ex_name = PyObject_GetAttrString(source, #ex_name); \
    if (ex_name == NULL) { goto fail; }

  LOAD_EXC_PTR(Warning)
  LOAD_EXC_PTR(Error)
  LOAD_EXC_PTR(InterfaceError)
  LOAD_EXC_PTR(DatabaseError)
  LOAD_EXC_PTR(DataError)
  LOAD_EXC_PTR(OperationalError)
  LOAD_EXC_PTR(TransactionConflict)
  LOAD_EXC_PTR(IntegrityError)
  LOAD_EXC_PTR(InternalError)
  LOAD_EXC_PTR(ProgrammingError)
  LOAD_EXC_PTR(NotSupportedError)

  initialized = TRUE;
  RETURN_PY_NONE;

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

int _init_kiservices_ibase_header_constants(PyObject *module) {
  /* Makes a bunch of Services-API-related database engine constants available
   * at the Python level. */

  /* 2005.06.24: I analyzed the potential memory savings of writing this code
   * so that at most one Python int object with a given value is created.
   * Using the following program
   *   from kinterbasdb.services import _ksrv
   *   for x in sorted(
   *       getattr(_ksrv, nm) for nm in dir(_ksrv) if nm.startswith('isc_')
   *     ):
   *       print x
   * I found that the vast majority of the 330 integers loaded when compiling
   * against FB 1.5 are in the range already cached by Python, so there's
   * virtually no memory savings to be gained by manual optimization here. */

  /* SICN is a shortcut for entering header constants into the module's
   * namespace. */
  #define SICN(name, value) \
    if (PyModule_AddIntConstant(module, name, value) == -1) { goto fail; }

  /* SIC is a further shortcut for values that we want to expose under the same
   * name they have in C. */
  #define SIC(token) \
    SICN(#token, token)


  SICN("SIZEOF_SHORT",                   sizeof(short));
  SICN("SIZEOF_SHORT_UNSIGNED",          sizeof(unsigned short));
  SICN("SIZEOF_LONG",                    sizeof(long));
  SICN("SIZEOF_LONG_UNSIGNED",           sizeof(unsigned long));

  /* Locally defined header constants that must be accessible from Python: */
  SIC(QUERY_TYPE_PLAIN_STRING);
  SIC(QUERY_TYPE_PLAIN_INTEGER);
  SIC(QUERY_TYPE_RAW);

/* YYY: Cull these constants.  They've never been accessible to the client
 * programmer, so there's no danger of breaking client code in doing so.  The
 * constants are used only in services.py, and only a small fraction of those
 * listed here are used there. */
  /* Drawn from ibase.h: */
  SIC(isc_spb_version1);
  SIC(isc_spb_current_version);
  SIC(isc_spb_version);
  SIC(isc_spb_current_version);
  SIC(isc_spb_user_name);
  SIC(isc_spb_sys_user_name);
  SIC(isc_spb_sys_user_name_enc);
  SIC(isc_spb_password);
  SIC(isc_spb_password_enc);
  SIC(isc_spb_command_line);
  SIC(isc_spb_dbname);
  SIC(isc_spb_verbose);
  SIC(isc_spb_options);
  SIC(isc_spb_connect_timeout);
  SIC(isc_spb_dummy_packet_interval);
  SIC(isc_spb_sql_role_name);

  SIC(isc_dpb_sys_user_name);
  SIC(isc_dpb_sys_user_name_enc);
  SIC(isc_dpb_password);
  SIC(isc_dpb_password_enc);
  SIC(isc_dpb_connect_timeout);
  SIC(isc_dpb_dummy_packet_interval);
  SIC(isc_dpb_user_name);
  SIC(isc_dpb_sql_role_name);

  SIC(isc_info_end);
  SIC(isc_info_truncated);
  SIC(isc_info_error);
  SIC(isc_info_data_not_ready);
  SIC(isc_info_flag_end);
  SIC(isc_info_db_id);
  SIC(isc_info_reads);
  SIC(isc_info_writes);
  SIC(isc_info_fetches);
  SIC(isc_info_marks);
  SIC(isc_info_implementation);

  SIC(isc_info_base_level);
  SIC(isc_info_page_size);
  SIC(isc_info_num_buffers);
  SIC(isc_info_limbo);
  SIC(isc_info_current_memory);
  SIC(isc_info_max_memory);
  SIC(isc_info_window_turns);
  SIC(isc_info_license);
  SIC(isc_info_allocation);
  SIC(isc_info_attachment_id);
  SIC(isc_info_read_seq_count);
  SIC(isc_info_read_idx_count);
  SIC(isc_info_insert_count);
  SIC(isc_info_update_count);
  SIC(isc_info_delete_count);
  SIC(isc_info_backout_count);
  SIC(isc_info_purge_count);
  SIC(isc_info_expunge_count);
  SIC(isc_info_sweep_interval);
  SIC(isc_info_ods_version);
  SIC(isc_info_ods_minor_version);
  SIC(isc_info_no_reserve);
  SIC(isc_info_logfile);
  SIC(isc_info_cur_logfile_name);
  SIC(isc_info_cur_log_part_offset);
  SIC(isc_info_num_wal_buffers);
  SIC(isc_info_wal_buffer_size);
  SIC(isc_info_wal_ckpt_length);
  SIC(isc_info_wal_cur_ckpt_interval);
  SIC(isc_info_wal_prv_ckpt_fname);
  SIC(isc_info_wal_prv_ckpt_poffset);
  SIC(isc_info_wal_recv_ckpt_fname);
  SIC(isc_info_wal_recv_ckpt_poffset);
  SIC(isc_info_wal_grpc_wait_usecs);
  SIC(isc_info_wal_num_io);
  SIC(isc_info_wal_avg_io_size);
  SIC(isc_info_wal_num_commits);
  SIC(isc_info_wal_avg_grpc_size);
  SIC(isc_info_forced_writes);
  SIC(isc_info_user_names);
  SIC(isc_info_page_errors);
  SIC(isc_info_record_errors);
  SIC(isc_info_bpage_errors);
  SIC(isc_info_dpage_errors);
  SIC(isc_info_ipage_errors);
  SIC(isc_info_ppage_errors);
  SIC(isc_info_tpage_errors);
  SIC(isc_info_set_page_buffers);
  SIC(isc_info_db_sql_dialect);
  SIC(isc_info_db_read_only);
  SIC(isc_info_db_size_in_pages);

  #ifdef isc_info_firebird_version
  SIC(isc_info_db_class);
  SIC(isc_info_firebird_version);
  SIC(isc_info_oldest_transaction);
  SIC(isc_info_oldest_active);
  SIC(isc_info_oldest_snapshot);
  SIC(isc_info_next_transaction);
  SIC(isc_info_db_provider);
  SIC(isc_info_db_last_value);
  SIC(isc_info_version);
  SIC(isc_info_isc_version);
  #endif /* isc_info_firebird_version */

  SIC(isc_info_db_impl_rdb_vms);
  SIC(isc_info_db_impl_rdb_eln);
  SIC(isc_info_db_impl_rdb_eln_dev);
  SIC(isc_info_db_impl_rdb_vms_y);
  SIC(isc_info_db_impl_rdb_eln_y);
  SIC(isc_info_db_impl_jri);
  SIC(isc_info_db_impl_jsv);
  SIC(isc_info_db_impl_isc_apl_68K);
  SIC(isc_info_db_impl_isc_vax_ultr);
  SIC(isc_info_db_impl_isc_vms);
  SIC(isc_info_db_impl_isc_sun_68k);
  SIC(isc_info_db_impl_isc_os2);
  SIC(isc_info_db_impl_isc_sun4);
  SIC(isc_info_db_impl_isc_hp_ux);
  SIC(isc_info_db_impl_isc_sun_386i);
  SIC(isc_info_db_impl_isc_vms_orcl);
  SIC(isc_info_db_impl_isc_mac_aux);
  SIC(isc_info_db_impl_isc_rt_aix);
  SIC(isc_info_db_impl_isc_mips_ult);
  SIC(isc_info_db_impl_isc_xenix);
  SIC(isc_info_db_impl_isc_dg);
  SIC(isc_info_db_impl_isc_hp_mpexl);
  SIC(isc_info_db_impl_isc_hp_ux68K);
  SIC(isc_info_db_impl_isc_sgi);
  SIC(isc_info_db_impl_isc_sco_unix);
  SIC(isc_info_db_impl_isc_cray);
  SIC(isc_info_db_impl_isc_imp);
  SIC(isc_info_db_impl_isc_delta);
  SIC(isc_info_db_impl_isc_next);
  SIC(isc_info_db_impl_isc_dos);

  #ifdef isc_info_firebird_version
  SIC(isc_info_db_impl_m88K);
  SIC(isc_info_db_impl_unixware);
  SIC(isc_info_db_impl_isc_winnt_x86);
  SIC(isc_info_db_impl_isc_epson);
  SIC(isc_info_db_impl_alpha_osf);
  SIC(isc_info_db_impl_alpha_vms);
  SIC(isc_info_db_impl_netware_386);
  SIC(isc_info_db_impl_win_only);
  SIC(isc_info_db_impl_ncr_3000);
  SIC(isc_info_db_impl_winnt_ppc);
  SIC(isc_info_db_impl_dg_x86);
  SIC(isc_info_db_impl_sco_ev);
  SIC(isc_info_db_impl_i386);
  SIC(isc_info_db_impl_freebsd);
  SIC(isc_info_db_impl_netbsd);
  SIC(isc_info_db_impl_darwin);
  SIC(isc_info_db_impl_last_value);
  #endif /* isc_info_firebird_version */

  SIC(isc_info_db_impl_isc_a);
  SIC(isc_info_db_impl_isc_apl_68K);
  SIC(isc_info_db_impl_isc_u);
  SIC(isc_info_db_impl_isc_vax_ultr);
  SIC(isc_info_db_impl_isc_v);
  SIC(isc_info_db_impl_isc_vms);
  SIC(isc_info_db_impl_isc_s);
  SIC(isc_info_db_impl_isc_sun_68k);
  SIC(isc_info_db_class_access);
  SIC(isc_info_db_class_y_valve);
  SIC(isc_info_db_class_rem_int);
  SIC(isc_info_db_class_rem_srvr);
  SIC(isc_info_db_class_pipe_int);
  SIC(isc_info_db_class_pipe_srvr);
  SIC(isc_info_db_class_sam_int);
  SIC(isc_info_db_class_sam_srvr);
  SIC(isc_info_db_class_gateway);
  SIC(isc_info_db_class_cache);

  #ifdef isc_info_firebird_version
  SIC(isc_info_db_class_classic_access);
  SIC(isc_info_db_class_server_access);
  SIC(isc_info_db_class_last_value);
  SIC(isc_info_db_code_rdb_eln);
  SIC(isc_info_db_code_rdb_vms);
  SIC(isc_info_db_code_interbase);
  SIC(isc_info_db_code_firebird);
  SIC(isc_info_db_code_last_value);
  #endif /* isc_info_firebird_version */

  SIC(isc_info_number_messages);
  SIC(isc_info_max_message);
  SIC(isc_info_max_send);
  SIC(isc_info_max_receive);
  SIC(isc_info_state);
  SIC(isc_info_message_number);
  SIC(isc_info_message_size);
  SIC(isc_info_request_cost);
  SIC(isc_info_access_path);
  SIC(isc_info_req_select_count);
  SIC(isc_info_req_insert_count);
  SIC(isc_info_req_update_count);
  SIC(isc_info_req_delete_count);
  SIC(isc_info_rsb_end);
  SIC(isc_info_rsb_begin);
  SIC(isc_info_rsb_type);
  SIC(isc_info_rsb_relation);
  SIC(isc_info_rsb_plan);
  SIC(isc_info_rsb_unknown);
  SIC(isc_info_rsb_indexed);
  SIC(isc_info_rsb_navigate);
  SIC(isc_info_rsb_sequential);
  SIC(isc_info_rsb_cross);
  SIC(isc_info_rsb_sort);
  SIC(isc_info_rsb_first);
  SIC(isc_info_rsb_boolean);
  SIC(isc_info_rsb_union);
  SIC(isc_info_rsb_aggregate);
  SIC(isc_info_rsb_merge);
  SIC(isc_info_rsb_ext_sequential);
  SIC(isc_info_rsb_ext_indexed);
  SIC(isc_info_rsb_ext_dbkey);
  SIC(isc_info_rsb_left_cross);
  SIC(isc_info_rsb_select);
  SIC(isc_info_rsb_sql_join);
  SIC(isc_info_rsb_simulate);
  SIC(isc_info_rsb_sim_cross);
  SIC(isc_info_rsb_once);
  SIC(isc_info_rsb_procedure);
  SIC(isc_info_rsb_and);
  SIC(isc_info_rsb_or);
  SIC(isc_info_rsb_dbkey);
  SIC(isc_info_rsb_index);
  SIC(isc_info_req_active);
  SIC(isc_info_req_inactive);
  SIC(isc_info_req_send);
  SIC(isc_info_req_receive);
  SIC(isc_info_req_select);
  SIC(isc_info_req_sql_stall);
  SIC(isc_info_blob_num_segments);
  SIC(isc_info_blob_max_segment);
  SIC(isc_info_blob_total_length);
  SIC(isc_info_blob_type);
  SIC(isc_info_tra_id);

  SIC(isc_action_svc_backup);
  SIC(isc_action_svc_restore);
  SIC(isc_action_svc_repair);
  SIC(isc_action_svc_add_user);
  SIC(isc_action_svc_delete_user);
  SIC(isc_action_svc_modify_user);
  SIC(isc_action_svc_display_user);
  SIC(isc_action_svc_properties);
  SIC(isc_action_svc_add_license);
  SIC(isc_action_svc_remove_license);
  SIC(isc_action_svc_db_stats);
  SIC(isc_action_svc_get_ib_log);

  SIC(isc_info_svc_svr_db_info);
  SIC(isc_info_svc_get_license);
  SIC(isc_info_svc_get_license_mask);
  SIC(isc_info_svc_get_config);
  SIC(isc_info_svc_version);
  SIC(isc_info_svc_server_version);
  SIC(isc_info_svc_implementation);
  SIC(isc_info_svc_capabilities);
  SIC(isc_info_svc_user_dbpath);
  SIC(isc_info_svc_get_env);
  SIC(isc_info_svc_get_env_lock);
  SIC(isc_info_svc_get_env_msg);
  SIC(isc_info_svc_line);
  SIC(isc_info_svc_to_eof);
  SIC(isc_info_svc_timeout);
  SIC(isc_info_svc_get_licensed_users);
  SIC(isc_info_svc_limbo_trans);
  SIC(isc_info_svc_running);
  SIC(isc_info_svc_get_users);

  SIC(isc_spb_sec_userid);
  SIC(isc_spb_sec_groupid);
  SIC(isc_spb_sec_username);
  SIC(isc_spb_sec_password);
  SIC(isc_spb_sec_groupname);
  SIC(isc_spb_sec_firstname);
  SIC(isc_spb_sec_middlename);
  SIC(isc_spb_sec_lastname);

  /* Won't bother with license-related constants. */

  /* <BACKUP> */
  SIC(isc_action_svc_backup);

  SIC(isc_spb_bkp_file);
  SIC(isc_spb_bkp_factor);
  SIC(isc_spb_bkp_length);
  /* begin bitmask components */
  SIC(isc_spb_bkp_ignore_checksums);
  SIC(isc_spb_bkp_ignore_limbo);
  SIC(isc_spb_bkp_metadata_only);
  SIC(isc_spb_bkp_no_garbage_collect);
  SIC(isc_spb_bkp_old_descriptions);
  SIC(isc_spb_bkp_non_transportable);
  SIC(isc_spb_bkp_convert);
  SIC(isc_spb_bkp_expand);
  /* end bitmask components */
  /* </BACKUP> */

  SIC(isc_action_svc_properties);
  SIC(isc_spb_prp_page_buffers);
  SIC(isc_spb_prp_sweep_interval);
  SIC(isc_spb_prp_shutdown_db);
  SIC(isc_spb_prp_deny_new_attachments);
  SIC(isc_spb_prp_deny_new_transactions);
  SIC(isc_spb_prp_reserve_space);
  SIC(isc_spb_prp_write_mode);
  SIC(isc_spb_prp_access_mode);
  SIC(isc_spb_prp_set_sql_dialect);
  SIC(isc_spb_prp_activate);
  SIC(isc_spb_prp_db_online);
  SIC(isc_spb_prp_reserve_space);
  SIC(isc_spb_prp_res_use_full);
  SIC(isc_spb_prp_res);
  SIC(isc_spb_prp_write_mode);
  SIC(isc_spb_prp_wm_async);
  SIC(isc_spb_prp_wm_sync);
  SIC(isc_spb_prp_access_mode);
  SIC(isc_spb_prp_am_readonly);
  SIC(isc_spb_prp_am_readwrite);

  SIC(isc_action_svc_repair);
  SIC(isc_spb_rpr_commit_trans);
  SIC(isc_spb_rpr_rollback_trans);
  SIC(isc_spb_rpr_recover_two_phase);
  SIC(isc_spb_tra_id);
  SIC(isc_spb_single_tra_id);
  SIC(isc_spb_multi_tra_id);
  SIC(isc_spb_tra_state);
  SIC(isc_spb_tra_state_limbo);
  SIC(isc_spb_tra_state_commit);
  SIC(isc_spb_tra_state_rollback);
  SIC(isc_spb_tra_state_unknown);
  SIC(isc_spb_tra_host_site);
  SIC(isc_spb_tra_remote_site);
  SIC(isc_spb_tra_db_path);
  SIC(isc_spb_tra_advise);
  SIC(isc_spb_tra_advise_commit);
  SIC(isc_spb_tra_advise_rollback);
  SIC(isc_spb_tra_advise_unknown);
  SIC(isc_spb_rpr_validate_db);
  SIC(isc_spb_rpr_sweep_db);
  SIC(isc_spb_rpr_mend_db);
  SIC(isc_spb_rpr_list_limbo_trans);
  SIC(isc_spb_rpr_check_db);
  SIC(isc_spb_rpr_ignore_checksum);
  SIC(isc_spb_rpr_kill_shadows);
  SIC(isc_spb_rpr_full);

  SIC(isc_action_svc_restore);
  SIC(isc_spb_res_buffers);
  SIC(isc_spb_res_page_size);
  SIC(isc_spb_res_length);
  SIC(isc_spb_res_access_mode);
  SIC(isc_spb_res_deactivate_idx);
  SIC(isc_spb_res_no_shadow);
  SIC(isc_spb_res_no_validity);
  SIC(isc_spb_res_one_at_a_time);
  SIC(isc_spb_res_replace);
  SIC(isc_spb_res_create);
  SIC(isc_spb_res_use_all_space);
  SIC(isc_spb_res_access_mode);
  SIC(isc_spb_res_am_readonly);
  SIC(isc_spb_prp_am_readonly);
  SIC(isc_spb_res_am_readwrite);
  SIC(isc_spb_prp_am_readwrite);

  SIC(isc_info_svc_svr_db_info);
  SIC(isc_spb_num_att);
  SIC(isc_spb_num_db);
  SIC(isc_spb_sts_data_pages);
  SIC(isc_spb_sts_db_log);
  SIC(isc_spb_sts_hdr_pages);
  SIC(isc_spb_sts_idx_pages);
  SIC(isc_spb_sts_sys_relations);
  SIC(isc_info_sql_select);
  SIC(isc_info_sql_bind);
  SIC(isc_info_sql_num_variables);
  SIC(isc_info_sql_describe_vars);
  SIC(isc_info_sql_describe_end);
  SIC(isc_info_sql_sqlda_seq);
  SIC(isc_info_sql_message_seq);
  SIC(isc_info_sql_type);
  SIC(isc_info_sql_sub_type);
  SIC(isc_info_sql_scale);
  SIC(isc_info_sql_length);
  SIC(isc_info_sql_null_ind);
  SIC(isc_info_sql_field);
  SIC(isc_info_sql_relation);
  SIC(isc_info_sql_owner);
  SIC(isc_info_sql_alias);
  SIC(isc_info_sql_sqlda_start);
  SIC(isc_info_sql_stmt_type);
  SIC(isc_info_sql_get_plan);
  SIC(isc_info_sql_records);
  SIC(isc_info_sql_batch_fetch);

  SIC(isc_info_sql_stmt_select);
  SIC(isc_info_sql_stmt_insert);
  SIC(isc_info_sql_stmt_update);
  SIC(isc_info_sql_stmt_delete);
  SIC(isc_info_sql_stmt_ddl);
  SIC(isc_info_sql_stmt_get_segment);
  SIC(isc_info_sql_stmt_put_segment);
  SIC(isc_info_sql_stmt_exec_procedure);
  SIC(isc_info_sql_stmt_start_trans);
  SIC(isc_info_sql_stmt_commit);
  SIC(isc_info_sql_stmt_rollback);
  SIC(isc_info_sql_stmt_select_for_upd);
  SIC(isc_info_sql_stmt_set_generator);
  #ifdef isc_info_sql_stmt_savepoint
  SIC(isc_info_sql_stmt_savepoint);
  #endif

  /* The following symbols are no longer available in FB 1.5rc4.  As far as DSR
   * knows, they were included here for completeness, rather than because
   * kinterbasdb itself actually used them. */
  #ifdef ISCCFG_LOCKMEM_KEY
  SIC(ISCCFG_LOCKMEM_KEY);
  SIC(ISCCFG_LOCKSIG_KEY);
  SIC(ISCCFG_EVNTMEM_KEY);
  SIC(ISCCFG_DBCACHE_KEY);
  SIC(ISCCFG_PRIORITY_KEY);
  SIC(ISCCFG_IPCMAP_KEY);
  SIC(ISCCFG_MEMMIN_KEY);
  SIC(ISCCFG_MEMMAX_KEY);
  SIC(ISCCFG_LOCKORDER_KEY);
  SIC(ISCCFG_ANYLOCKMEM_KEY);
  SIC(ISCCFG_ANYLOCKSEM_KEY);
  SIC(ISCCFG_ANYLOCKSIG_KEY);
  SIC(ISCCFG_ANYEVNTMEM_KEY);
  SIC(ISCCFG_LOCKHASH_KEY);
  SIC(ISCCFG_DEADLOCK_KEY);
  SIC(ISCCFG_LOCKSPIN_KEY);
  SIC(ISCCFG_CONN_TIMEOUT_KEY);
  SIC(ISCCFG_DUMMY_INTRVL_KEY);

  /* Since ISCCFG_TRACE_POOLS_KEY is marked "For internal use only" in ibase.h,
   * it is not loaded into the module's namespace. */

  SIC(ISCCFG_REMOTE_BUFFER_KEY);
  #endif /* ISCCFG_LOCKMEM_KEY */

  #ifdef ISCCFG_NO_NAGLE_KEY
  SIC(ISCCFG_NO_NAGLE_KEY);
  #endif
  #ifdef ISCCFG_CPU_AFFINITY_KEY
  SIC(ISCCFG_CPU_AFFINITY_KEY);
  #endif

  return 0;

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


static PyMethodDef _kiservices_GlobalMethods[] = {
    {"is_initialized",      (PyCFunction) pyob_is_initialized, METH_NOARGS},
    {"initialize_from",     pyob_initialize_from,             METH_VARARGS},

    {"connect",             pyob_SConnection_connect,          METH_VARARGS},
    {"close",               pyob_SConnection_close,            METH_VARARGS},

    {"action_thin",         pyob_action_thin,                 METH_VARARGS},

    {"query_base",          pyob_query_base,                  METH_VARARGS},
    {"vax",                 pyob_isc_vax_integer,             METH_VARARGS},

    {NULL, NULL}
  };


DL_EXPORT(void)
init_kiservices(void) {
  /* This function is called automatically when the module is first imported.
   * Python provides no way to recover from errors during C extension module
   * initialization, so error handling here is lax. */
  PyObject *module = Py_InitModule("_kiservices", _kiservices_GlobalMethods);
  if (module == NULL) { goto fail; }

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

  /* Cause a bunch of constants defined at the C level to be loaded into the
   * module dictionary so that they'll be accessible to the services.py module
   * implemented on top of this C module.  The Python-facing API implemented by
   * services.py is high-level enough that client programmers should not need
   * access to these constants). */
  if (_init_kiservices_ibase_header_constants(module) != 0) { goto fail; }

  fail:
    /* There's really nothing we can do. */
    return;
} /* init_kiservices */

/****************** MODULE ADMINISTRATION FUNCTIONS:END ********************/
