/* KInterbasDB Python Package - Implementation of Connection ** ** Version 3.1 ** ** The following contributors hold Copyright (C) over their respective ** portions of code (see license.txt for details): ** ** [Original Author (maintained through version 2.0-0.3.1):] ** 1998-2001 [alex] Alexander Kuznetsov ** [Maintainers (after version 2.0-0.3.1):] ** 2001-2002 [maz] Marek Isalski ** 2002-2004 [dsr] David Rushby ** [Contributors:] ** 2001 [eac] Evgeny A. Cherkashin ** 2001-2002 [janez] Janez Jere */ static int close_connection( ConnectionObject *con, boolean allowed_to_raise_exception ); /* Creates and returns new connection object. */ static ConnectionObject *new_connection(void) { ConnectionObject *con = PyObject_New(ConnectionObject, &ConnectionType); if (con == NULL) { return (ConnectionObject *) PyErr_NoMemory(); } con->dialect = SQL_DIALECT_DEFAULT; con->db_handle = NULL; con->trans_handle = NULL; con->group = NULL; /* 2003.04.27 */ con->_state = CONNECTION_STATE_CLOSED; #ifdef DETERMINE_FIELD_PRECISION con->desc_cache = NULL; #endif /* 2003.03.30: */ con->type_trans_in = NULL; con->type_trans_out = NULL; /* 2003.10.16: */ con->output_type_trans_return_type_dict = NULL; return con; } /* new_connection */ static void delete_connection(ConnectionObject *con) { /* Detaches from the database and cleans up the resources used by an existing ** connection (but does not free the ConnectionObject structure itself; ** that's the job of pyob_connection_del). */ if (con->db_handle != NULL) { if ( 0 != close_connection(con, FALSE) ) { /* close_connection failed, but since we're garbage collecting and can't ** reasonably raise an exception, ignore the failure to close, and ** discard the db_handle anyway. */ con->db_handle = NULL; con->_state = CONNECTION_STATE_CLOSED; } assert(con->db_handle == NULL); assert(CON_GET_TRANS_HANDLE(con) == NULL); /* 2003.10.15a:OK */ #ifdef DETERMINE_FIELD_PRECISION assert(con->desc_cache == NULL); /* 2003.10.06 */ #endif assert(con->_state == CONNECTION_STATE_CLOSED); } Py_XDECREF(con->group); /* 2003.04.27 */ /* 2003.03.30: */ Py_XDECREF(con->type_trans_in); Py_XDECREF(con->type_trans_out); /* 2003.10.16: */ Py_XDECREF(con->output_type_trans_return_type_dict); } /* delete_connection */ static void pyob_connection_del( PyObject *con ) { delete_connection( (ConnectionObject *) con ); PyObject_Del(con); } /* pyob_connection_del */ /* 2002.02.24: ** pyob_close_connection is necessary to comply with the Python DB API spec. ** Previously, the kinterbasdb.py Connection class simply deleted its ** reference to the ConnectionObject; there was no explicit call to a ** _kinterbasdb function to close the underlying Firebird database handle. ** The problem with that behavior is revealed by a closer reading of the DB ** API spec's description of Connection.close: ** """ ** Close the connection now (rather than whenever __del__ is called). The ** connection will be unusable from this point forward; an Error (or subclass) ** exception will be raised if any operation is attempted with the connection. ** The same applies to all cursor objects trying to use the connection. ** """ ** The kinterbasdb.py Connection class previously just deleted its reference ** to the ConnectionObject, but any cursors open on the ConnectionObject ** retained references to it, preventing it from being garbage collected. ** Therefore, the cursors never realized they were working with an "officially ** closed" connection. ** ** pyob_close_connection was added so that CursorObjects will immediately ** know when they check their ->connection->_state that the connection has ** been explicitly closed. The cursors will therefore refuse to work as soon ** as the close method has been called on the Connection, "rather than ** whenever __del__ is called" (to quote the DB API spec). ** ** 2003.02.17: Fixed bug in which isc_detach_database didn't get called until ** the connection was garbage collected (the previous code just set ->_state ** to CONNECTION_STATE_CLOSED, and left it at that until __del__). */ static PyObject *pyob_close_connection( PyObject *self, PyObject *args ) { char *conn_open_failure_message = "Attempt to reclose a closed connection."; ConnectionObject *con; if ( !PyArg_ParseTuple( args, "O!", &ConnectionType, &con ) ) { return NULL; } if (con->_state != CONNECTION_STATE_OPEN) { raise_exception(ProgrammingError, conn_open_failure_message); return NULL; } if ( 0 != close_connection(con, TRUE) ) { return NULL; } RETURN_PY_NONE; } /* pyob_close_connection */ /* 2003.02.17: Factored out connection-closing code so that it can be used ** from both pyob_close_connection and pyob_connection_del. */ static int close_connection( ConnectionObject *con, boolean allowed_to_raise_exception ) { /* If the parameter $allowed_to_raise_exception is FALSE, this function ** will refrain from raising a Python exception, and will indicate its status ** solely via the return code. */ /* A Connection that's part of a ConnectionGroup must not be close()d ** directly; instead, the containing group must be close()d. An exception ** will be raised at the Python level to prevent the assertion below from ** failing. */ assert (con->group == NULL); /* 2003.10.06: Normally, free_field_precision_cache will involve calls to ** isc_dsql_free_statement, so it *must* be performed before the database ** handle is invalidated. */ #ifdef DETERMINE_FIELD_PRECISION free_field_precision_cache( con->desc_cache, (boolean) (con->_state == CONNECTION_STATE_OPEN && con->db_handle != NULL), con->status_vector ); con->desc_cache = NULL; #endif /* If there is an associated transaction and it is still active (has not been ** commit()ed or rollback()ed), it is now rollback()ed. The DB API Spec says: ** "Closing a connection without committing the changes first will cause an ** implicit rollback to be performed." */ if (con->trans_handle != NULL) { /* No need for CON_GET_TRANS_HANDLE here. */ /* It isn't strictly necessary to use rollback_ANY_transaction here instead ** of a naked rollback_transaction, since we already know that the ** connection is not a group member. However, calling ** rollback_ANY_transaction helps (slightly) to keep the rollback logic ** centralized. */ if ( OP_RESULT_OK == rollback_ANY_transaction(con, allowed_to_raise_exception) ) { /* Success; the transaction is dead. */ con->trans_handle = NULL; } else { /* The rollback attempt failed. If we're *not* allowed to raise Python ** exception, continue as though the rollback attempt had succeeded. ** Otherwise, return -1 (rollback_transaction will already have raised a ** Python exception) and leave the trans_handle unchanged. */ if (!allowed_to_raise_exception) { con->trans_handle = NULL; } else { return -1; } } } /* 2003.10.06: With the new scheme in which connections keep track of their ** cursors and call their own close() method at the Python level, it's ** possible for this method to be called with a NULL con->db_handle. */ if (con->db_handle != NULL) { ENTER_DB isc_detach_database( con->status_vector, &con->db_handle ); LEAVE_DB if ( DB_API_ERROR(con->status_vector) ) { if (allowed_to_raise_exception) { raise_sql_exception(OperationalError, "close_connection: ", con->status_vector); } return -1; } } assert (con->db_handle == NULL); con->_state = CONNECTION_STATE_CLOSED; return 0; } /* close_connection */ /* 2002.02.24:_conn_require_open ** If self is not an open connection, raises the supplied error message ** (or a default if no error message is supplied). ** Returns 0 if the connection was open; -1 if it failed the test. */ static int _conn_require_open( ConnectionObject *self, char *failure_message ) { if ( self != NULL && self->_state == CONNECTION_STATE_OPEN ) { return 0; } if (failure_message == NULL) { failure_message = "Invalid connection state. The connection must be" " open to perform this operation."; } raise_exception(ProgrammingError, failure_message); return -1; } /* _conn_require_open */ /* Begin macros related to pyob_attach_db: */ /* The raise_sql_exception function attempts to acquire the DB lock, so while ** executing pyob_attach_db (throughout almost all of which the DB lock is ** held), we must release the DB lock before calling raise_sql_exception, then ** reacquire the DB lock upon returning from raise_sql_exception. */ #define ATTACH_DB__RAISE_SQL_EX(ex_type, ex_message, isc_statvec) \ LEAVE_DB_WITHOUT_ENTERING_PYTHON \ raise_sql_exception(ex_type, ex_message, isc_statvec); \ ENTER_DB_WITHOUT_LEAVING_PYTHON \ goto ATTACH_EXIT_ERROR; /* The next macro is defined for consistency with ATTACH_DB__RAISE_SQL_EX, but ** it's less compelling because raise_exception, when called from the context ** of the pyob_attach_db function, requires no lock management. */ #define ATTACH_DB__RAISE_EX(ex_type, ex_message) \ raise_exception(ex_type, ex_message); \ goto ATTACH_EXIT_ERROR; #define ATTACH_DB__EXPAND(dpb_code, value) \ isc_expand_dpb(&dpb, (short *) &dpb_length, dpb_code, value, NULL); /* End macros related to pyob_attach_db. */ static PyObject *pyob_attach_db( PyObject *self, PyObject *args ) { ConnectionObject *con = NULL; ENTER_DB_WITHOUT_LEAVING_PYTHON { /* We will receive pre-rendered DSN and DPB buffers from the Python level. */ char *dsn = NULL; int dsn_len = 0; char *dpb = NULL; int dpb_len = 0; short dialect = 0; /* Parse and validate the arguments. */ if ( !PyArg_ParseTuple(args, "z#z#h", &dsn, &dsn_len, &dpb, &dpb_len, &dialect) ) { goto ATTACH_EXIT_ERROR; } if (dsn_len > MAX_DSN_SIZE) { char *err_msg = NULL; #if PYTHON_2_2_OR_LATER PyObject *buffer = PyString_FromFormat( "DSN too long (%d bytes out of %d allowed).", dsn_len, MAX_DSN_SIZE ); if (buffer == NULL) { goto ATTACH_EXIT_ERROR; } err_msg = PyString_AS_STRING(buffer); #else err_msg = "DSN longer than maximum legal size."; #endif /* PYTHON_2_2_OR_LATER */ assert (err_msg != NULL); raise_exception(ProgrammingError, err_msg); #if PYTHON_2_2_OR_LATER Py_DECREF(buffer); #endif goto ATTACH_EXIT_ERROR; } if (dpb_len > MAX_DPB_SIZE) { char *err_msg = NULL; #if PYTHON_2_2_OR_LATER PyObject *buffer = PyString_FromFormat( "Database parameter buffer too large (%d bytes out of %d allowed).", dpb_len, MAX_DPB_SIZE ); if (buffer == NULL) { goto ATTACH_EXIT_ERROR; } err_msg = PyString_AS_STRING(buffer); #else err_msg = "Database parameter buffer larger than maximum legal size."; #endif /* PYTHON_2_2_OR_LATER */ assert (err_msg != NULL); raise_exception(ProgrammingError, err_msg); #if PYTHON_2_2_OR_LATER Py_DECREF(buffer); #endif goto ATTACH_EXIT_ERROR; } /* A negative value for the dialect is not acceptable because the IB/FB API ** requires an UNSIGNED SHORT. */ if (dialect < 0) { ATTACH_DB__RAISE_EX(ProgrammingError, "connection dialect must be >= 0"); } /* Create a ConnectionObject struct (this is essentially a memory allocation ** operation, not the actual point of connection to the database server): */ con = new_connection(); if (con == NULL) { goto ATTACH_EXIT_ERROR; } /* con->dialect is set to a default value in the new_connection ** function, so we only need to change it if we received a dialect argument ** to this function. */ if (dialect > 0) { con->dialect = (unsigned short) dialect; } assert (con->dialect > 0); #ifndef ENABLE_DB_EVENT_SUPPORT LEAVE_PYTHON_WITHOUT_ENTERING_DB /* We already hold the DB lock. */ #endif /* ENABLE_DB_EVENT_SUPPORT */ /* The MEAT of this entire function: */ /* The casts to short are quite safe b/c the vals have been validated. */ assert(con->db_handle == NULL); isc_attach_database( con->status_vector, (short) dsn_len, dsn, &(con->db_handle), (short) dpb_len, dpb ); #ifndef ENABLE_DB_EVENT_SUPPORT ENTER_PYTHON_WITHOUT_LEAVING_DB /* We did not release the DB lock. */ #endif /* ENABLE_DB_EVENT_SUPPORT */ if ( DB_API_ERROR(con->status_vector) ) { ATTACH_DB__RAISE_SQL_EX( OperationalError, "isc_attach_database: ", con->status_vector ); } /* Sweet success: */ con->_state = CONNECTION_STATE_OPEN; goto ATTACH_EXIT; } ATTACH_EXIT_ERROR: /* A Python exception must already have been set. */ if (con != NULL) { /* con's destructor (pyob_connection_del) sometimes tries to acquire the ** (non-reentrant) DB lock, which we currently hold, so we must ** release/reacquire the DB lock around the implicit destructor call. */ LEAVE_DB_WITHOUT_ENTERING_PYTHON Py_DECREF((PyObject *) con); /* 2005.04.26: Don't call pyob_connection_del explicitly. */ ENTER_DB_WITHOUT_LEAVING_PYTHON con = NULL; } ATTACH_EXIT: LEAVE_DB_WITHOUT_ENTERING_PYTHON return (PyObject *) con; } /* pyob_attach_db */ /* 2003.02.17: Eliminated the pyob_detach_db function (in favor of ** pyob_close_connection). */ #ifdef INTERBASE6_OR_LATER static PyObject *pyob_get_dialect( PyObject *self, PyObject *args) { ConnectionObject *con; if ( !PyArg_ParseTuple( args, "O!", &ConnectionType, &con ) ) return NULL; CONN_REQUIRE_OPEN(con); return PyInt_FromLong( (long) con->dialect ); } /* get_dialect */ static PyObject *pyob_set_dialect( PyObject *self, PyObject *args ) { ConnectionObject *con; short dialect; if ( !PyArg_ParseTuple( args, "O!h", &ConnectionType, &con, &dialect ) ) { return NULL; } CONN_REQUIRE_OPEN(con); /* A negative value for the dialect is not acceptable because the IB/FB API ** requires an UNSIGNED SHORT. */ if (dialect < 0) { raise_exception(ProgrammingError, "connection dialect must be >= 0"); return NULL; } con->dialect = (unsigned short) dialect; RETURN_PY_NONE; } /* set_dialect */ #endif /* INTERBASE6_OR_LATER */ /* 2003.04.27:begin block: */ static PyObject *pyob_get_group( PyObject *self, PyObject *args ) { ConnectionObject *con; PyObject *group; if ( !PyArg_ParseTuple( args, "O!", &ConnectionType, &con ) ) { return NULL; } group = con->group; if (group == NULL) { RETURN_PY_NONE; } else { Py_INCREF(group); return group; } } /* pyob_get_group */ static PyObject *pyob_set_group( PyObject *self, PyObject *args ) { ConnectionObject *con; PyObject *group; if ( !PyArg_ParseTuple( args, "O!O", &ConnectionType, &con, &group ) ) { return NULL; } Py_XDECREF(con->group); if (group == Py_None) { con->group = NULL; } else { Py_INCREF(group); con->group = group; } RETURN_PY_NONE; } /* pyob_set_group */ /* 2003.04.27:end block */ static PyObject *pyob_database_info(PyObject *self, PyObject *args) { ConnectionObject *con; char requestBuffer[] = {isc_info_end, isc_info_end}; char resultType; /* 'i'-short integer, 's'-string */ /* The fixed size of resultBuffer is not an overflow risk because the ** database's C API will indicate if the result buffer is too small. In ** version 3.2, I plan to switch to auto-enlargement of the buffer if it's ** too small (see "YYY: Enhancement planned for 3.2" note below). */ char resultBuffer[ISC_INFO_RESULT_BUFFER_SIZE]; long resultBufferLen; long i; PyObject *res = NULL; memset(resultBuffer, '\0', ISC_INFO_RESULT_BUFFER_SIZE); if ( !PyArg_ParseTuple( args, "O!bc", &ConnectionType, &con, &(requestBuffer[0]), &resultType ) ) { return NULL; } CONN_REQUIRE_OPEN(con); ENTER_DB isc_database_info( con->status_vector, &(con->db_handle), sizeof(requestBuffer), requestBuffer, sizeof(resultBuffer), resultBuffer ); LEAVE_DB if (DB_API_ERROR(con->status_vector)) { raise_sql_exception( OperationalError, "pyob_database_info.isc_database_info: ", con->status_vector ); return NULL; } /* 2004.12.12: ** Some info codes, such as isc_info_user_names, don't represent the length ** of their results in a standard manner, so we scan backward from the end of ** the result buffer. This scan will become less inefficient in 3.2 when I ** implement an auto-sized result buffer, since the initial size of the ** buffer can become more conservative. */ for (i = ISC_INFO_RESULT_BUFFER_SIZE - 1; i >= 0; i--) { if (resultBuffer[i] != '\0') { break; } } if (resultBuffer[i] != isc_info_end) { /* YYY: Enhancement planned for 3.2: ** If the last byte in the result buffer is isc_info_truncated, enlarge the ** buffer and retry. */ raise_exception(InternalError, "Size of return buffer from" " isc_database_info exceeds compile-time limit" " ISC_INFO_RESULT_BUFFER_SIZE." ); return NULL; } /* The result buffer's length is logically i + 1 rather than i, but since we ** know that the last byte is isc_info_end, we needn't include it. */ resultBufferLen = i; if (resultBuffer[0] != requestBuffer[0]) { raise_exception(InternalError, "resultBuffer[0] != requestBuffer[0]"); return NULL; } switch (resultType) { case 'i': case 'I': { int vax_int; /* The cluster length will differ depending on whether the integer result ** is returned as a byte, a short, or an int. */ short clusterLength = (short) isc_vax_integer(resultBuffer + 1, 2); ENTER_DB vax_int = isc_vax_integer(resultBuffer + 3, (short) clusterLength); LEAVE_DB res = PyInt_FromLong(vax_int); } break; case 's': case 'S': /* 2004.12.12: */ /* Some info codes, such as isc_info_user_names, don't represent the length ** of their results in a manner compatible with the previous parsing logic, ** so we need to return the entire buffer to the Python level. */ res = PyString_FromStringAndSize(resultBuffer, resultBufferLen); break; default: raise_exception(InterfaceError, "Unknown result type in pyob_database_info"); res = NULL; } return res; } /* pyob_database_info */