/* KInterbasDB Python Package - Implementation of SQL Statement Execution, etc. ** ** Version 3.1 ** ** The following contributors hold Copyright (C) over their respective ** portions of code (see license.txt for details): ** ** [Original Author (maintained through version 2.0-0.3.1):] ** 1998-2001 [alex] Alexander Kuznetsov ** [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 */ /****************** "PRIVATE" DECLARATIONS:BEGIN *******************/ static int _prepare_statement_if_necessary(CursorObject *cursor, PyObject *sql); static boolean _check_statement_length(long length); static int determine_statement_type( isc_stmt_handle *statementHandle, ISC_STATUS statusVector[] ); /****************** "PRIVATE" DECLARATIONS:END *******************/ /* For an explanation of this function's purpose, see the documentation for ** Python function kinterbasdb.create_database. */ static PyObject *pyob_create_database( PyObject *self, PyObject *args ) { ConnectionObject *con; char *sql = NULL; int sql_len = -1; short dialect = 0; if ( !PyArg_ParseTuple(args, "s#|h", &sql, &sql_len, &dialect) ) { return NULL; } if (!_check_statement_length(sql_len)) { return NULL; } /* A negative value for the dialect is not acceptable because the IB/FB API ** requires an UNSIGNED SHORT. */ if (dialect < 0) { raise_exception(ProgrammingError, "con dialect must be > 0"); return NULL; } con = new_connection(); if (con == NULL) { /* The new_connection function will already have set an exception. */ return NULL; } /* conn->dialect is set to a default value in the new_connection ** function, so we only need to change it if we received a dialect argument ** to this function. */ if (dialect > 0) { con->dialect = (unsigned short) dialect; } assert (con->dialect > 0); ENTER_DB isc_dsql_execute_immediate( con->status_vector, /* 2003.04.27: CREATE DATABASE will never be issued from a distributed ** transaction, so we do not use CON_GET_TRANS_HANDLE_ADDR below. */ &con->db_handle, &con->trans_handle, (unsigned short) sql_len, sql, con->dialect, NULL ); LEAVE_DB if ( DB_API_ERROR(con->status_vector) ) { raise_sql_exception(ProgrammingError, "pyob_create_database: ", con->status_vector ); Py_DECREF((PyObject *) con); /* 2005.04.26: Don't call pyob_connection_del explicitly. */ return NULL; } else { con->_state = CONNECTION_STATE_OPEN; return (PyObject *) con; } } /* pyob_create_database */ /* For an explanation of this function's purpose, see the documentation for ** Python function kinterbasdb.drop_database. */ static PyObject *pyob_drop_database( PyObject *self, PyObject *args ) { ConnectionObject *con; if ( !PyArg_ParseTuple(args, "O!", &ConnectionType, &con) ) { return NULL; } CONN_REQUIRE_OPEN(con); /* CONN_REQUIRE_OPEN should enforce non-null db_handle, but assert anyway: */ assert (con->db_handle != NULL); /* Already enforced at Python level: */ assert (con->group == NULL); /* If there's an unresolved transaction, roll it back before dropping the ** database. Otherwise, the database would be dropped and then the ** connection would attempt to roll back the transaction in close_connection ** (called by the destructor delete_connection), resulting in subtle memory ** corruption. */ if (OP_RESULT_OK != rollback_transaction(con->trans_handle, FALSE, TRUE, con->status_vector) ) { return NULL; } con->trans_handle = NULL; /* 2003.10.14 */ /* 2003.10.15: begin block */ /* Normally, free_field_precision_cache will involve calls to ** isc_dsql_free_statement, so it *must* be performed before the database ** handle is invalidated. */ #ifdef DETERMINE_FIELD_PRECISION free_field_precision_cache( con->desc_cache, TRUE, /* Yes, it *should* try to free the statement handles. */ con->status_vector ); con->desc_cache = NULL; #endif /* 2003.10.15: end block */ ENTER_DB isc_drop_database( con->status_vector, &(con->db_handle) ); LEAVE_DB if ( DB_API_ERROR(con->status_vector) ) { raise_sql_exception(OperationalError, "pyob_drop_database: ", con->status_vector); return NULL; } con->db_handle = NULL; /* 2003.10.08 */ con->_state = CONNECTION_STATE_CLOSED; /* 2003.10.14 */ RETURN_PY_NONE; } /* pyob_drop_database */ /* 2002.02.25: ** execute_immediate in the context of a con. ** Like isc_dsql_execute_immediate (which it wraps), this function cannot ** execute SQL statements that return result sets. ** ** It would be easy to separately wrap isc_dsql_exec_immed2, which can return ** up to one row of output, but I don't see the point. Note that ** isc_dsql_exec_immed2 would have to be SEPARATELY wrapped, because here ** (unlike in pyob_execute), we don't have a prepared representation of ** the query, and therefore CANNOT do a similar dynamic selection of execution ** function, for we don't know the statement type. */ static PyObject *pyob_execute_immediate ( PyObject *self, PyObject *args ) { ConnectionObject *con; char *sql = NULL; int sql_len = -1; if ( !PyArg_ParseTuple( args, "O!s#", &ConnectionType, &con, &sql, &sql_len ) ) { return NULL; } CONN_REQUIRE_OPEN(con); /* 2003.02.17b: */ if (CON_GET_TRANS_HANDLE(con) == NULL) { /* 2003.10.15a:OK */ /* The Python layer of kinterbasdb should have prevented this from happening. */ raise_exception(InternalError, "pyob_execute_immediate: null transaction"); return NULL; } if (!_check_statement_length(sql_len)) { return NULL; } { /* 2003.10.15a: Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */ isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(con); ENTER_DB isc_dsql_execute_immediate( con->status_vector, &con->db_handle, trans_handle_addr, (unsigned short) sql_len, sql, con->dialect, NULL ); LEAVE_DB } if ( DB_API_ERROR(con->status_vector) ) { raise_sql_exception( ProgrammingError, "isc_dsql_execute_immediate: ", con->status_vector ); return NULL; } RETURN_PY_NONE; } /* pyob_execute_immediate */ static PyObject *pyob_execute( PyObject *self, PyObject *args ) { /* This function returns a Python DB API description tuple upon successful ** execution of a result-set-returning statement (that is, a query), or ** None upon successful execution of any other statement. It sets a ** Python exception and returns NULL in case of failure. */ PyObject *cursor_description; CursorObject *cursor; ConnectionObject *conn; /* Just a lookup cache. */ PyObject *sql = NULL; /* Either PyString or PyUnicode. */ PyObject *params = NULL; int statementType; if ( !PyArg_ParseTuple( args, "O!OO", &CursorType, &cursor, &sql, ¶ms ) ) { return NULL; } CUR_REQUIRE_OPEN(cursor); conn = cursor->connection; /* For $params, accept any sequence except a string. */ if ( !PySequence_Check(params) ) { raise_exception( InterfaceError, "input parameter container is not a sequence" ); return NULL; } if ( PyString_Check(params) ) { raise_exception( InterfaceError, "input parameter sequence cannot be a string" ); return NULL; } #ifdef KIDB_DEBUGGERING /* 2003.09.06 */ { PyObject *sql_str = PyObject_Str(sql); PyObject *params_str = PyObject_Str(params); fprintf(stderr, "{SQL TRACE:\n [%s]\n with params\n %s\n}\n", PyString_AS_STRING(sql_str), PyString_AS_STRING(params_str) ); Py_DECREF(sql_str); Py_DECREF(params_str); } #endif /* KIDB_DEBUGGERING */ /* If this cursor was associated with a previous statement, ** "freshen" it up so that it can deal can deal with another statement ** execution (possibly of the same SQL statement, with different input ** parameters) and perhaps also retrieve another result set. */ clear_cursor(cursor, sql); /* 2003.02.17b: */ if (CON_GET_TRANS_HANDLE(conn) == NULL) { /* 2003.10.15a:OK */ /* The Python layer of kinterbasdb should have prevented this from happening. */ raise_exception(InternalError, "pyob_execute: null transaction"); return NULL; } if ( 0 != _prepare_statement_if_necessary(cursor, sql) ) { /* Note that this cleanup route excludes the ** free_XSQLVAR_dynamically_allocated_memory call. */ goto EXECUTE_ERROR_CLEANUP_EXCEPT_DYNVARALLOC; } /* Convert the Python input arguments to their XSQLVAR equivalents. */ if ( PyObject2XSQLDA(cursor, cursor->in_sqlda, params) < 0 ) { goto EXECUTE_ERROR_CLEANUP; } /* 2002.08.19: allocate_output_buffer call was originally here; I moved ** it to within _prepare_statement_if_necessary. */ /* 2002.01.01: ** It looks like the original author was dealing with some similar ** EXECUTE PROCEDURE troubles, but they weren't the same as the one I'm ** here to fix. ** ** The fact that there are output fields (that is, ** cursors[cur].in_sqlda->sqld is >= 1) does not guarantee that the ** statement is compatible with a standard SELECT-fetch approach; it ** could be an EXECUTE on a stored procedure with output params. ** ** Special case: ** The cursor's statement type is isc_info_sql_stmt_exec_procedure ** and it has at least one output parameter. ** ** Unlike a SELECT statement whose target is a stored procedure, an ** EXECUTE PROCEDURE statement must not return more than one row. ** This is because such a procedure must be executed with ** isc_dsql_execute2, which returns its results immediately and is ** therefore not compatible with a standard execute-fetch approach. ** ** However, I later (2002.02.21) imposed the "appearance of execute-fetch" ** on even this special case by caching the results in the cursor object ** and returning the ONE *cached* row if/when fetch is called. ** This behavior was set up in order to satisfy the Python DB API spec. ** ** kinterbasdb up to and including version 2.0-0.3.1 would choke with an ** exception instead of behaving in the standard way. It did so because ** it tried to execute the procedure in this special case with ** isc_dsql_execute rather than isc_dsql_execute2. That appeared to work ** fine during the execution step, but gagged if/when fetch was ** subsequently called. */ /* 2003.01.26: The statement type is now cached by the cursor; it need not ** be recomputed every time. */ statementType = cursor->statement_type; assert (statementType != NULL_STATEMENT_TYPE); debug_printf1( "[in pyob_execute] STATEMENT TYPE: %d\n", statementType ); if ( statementType == isc_info_sql_stmt_exec_procedure && cursor->out_sqlda->sqld > 0 ) { /* 2002.01.01: ** The crucial difference between isc_dsql_execute and ** isc_dsql_execute2 is that the latter loads information about the ** first output row into the output structures immediately, without ** waiting for a call to isc_dsql_fetch(). It is IMPORTANT to prevent ** isc_dsql_fetch from being called on a cursor that has been executed ** with isc_dsql_execute2. ** 2002.02.21: ** Although it is true that isc_dsql_fetch must not be called on a cursor ** that has been executed with isc_dsql_execute2, we must impose the ** appearance of that behavior in order to satisfy the Python DB API ** Specification 2.0, which does not allow a direct return of stored ** procedure results unless they are output parameters or ** input/output parameters (in the sense that a client variables is passed ** in and its memory space if filled with the output value). IB and FB ** lack such "shared memory space" parameters, so even though we have ** the result row immediately after calling isc_dsql_execute2, we do not ** return it directly but instead save it and wait for a possible fetch ** call at some later point. ** The current implementation caches the single result row from ** isc_dsql_execute2 in cursor->exec_proc_results. */ { /* 2003.10.15a: Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */ isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(conn); ENTER_DB isc_dsql_execute2( cursor->status_vector, trans_handle_addr, &(cursor->stmt_handle), conn->dialect, cursor->in_sqlda, cursor->out_sqlda ); LEAVE_DB } debug_dump_status_vector(cursor->status_vector); if ( DB_API_ERROR(cursor->status_vector) ) { raise_sql_exception( ProgrammingError, "isc_dsql_execute2: ", cursor->status_vector ); goto EXECUTE_ERROR_CLEANUP; } /* First, cache the result of the procedure call so that it is available ** if and when fetch is called in the future. */ cursor->exec_proc_results = XSQLDA2Tuple(cursor, cursor->out_sqlda); if (cursor->exec_proc_results == NULL) { goto EXECUTE_ERROR_CLEANUP; } /* Next, return the description (but not the results, as the initial ** fix from circa 2002.01.01 was doing), just as would be done in the ** case of a normal result-returning query (which we are crudely ** simulating via the cursor->exec_proc_results cache variable). */ cursor_description = XSQLDA2Description(cursor->out_sqlda, cursor); if (cursor_description == NULL) goto EXECUTE_ERROR_CLEANUP; return cursor_description; } else { /* Everything except the special case(s) defined above. */ { /* 2003.10.15a: Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */ isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(conn); ENTER_DB isc_dsql_execute( cursor->status_vector, trans_handle_addr, &cursor->stmt_handle, conn->dialect, cursor->in_sqlda ); LEAVE_DB } if ( DB_API_ERROR(cursor->status_vector) ) { raise_sql_exception( ProgrammingError, "isc_dsql_execute: ", cursor->status_vector ); goto EXECUTE_ERROR_CLEANUP; } } assert ( DB_API_ERROR(cursor->status_vector) == FALSE ); /* YYY:2002.02.21: ** (as part of fix for bug #520793) ** Conceptually, there should be an error in the status vector at this ** point in cases where ** a SELECT statement whose target is ** a stored procedure ** that raises an IB user-defined EXCEPTION ** has just been executed. However, said exception is present ONLY ** conceptually, not really. ** ** Apparently, the IB API only "realizes" that a user-defined EXCEPTION has ** been raised when it tries to fetch the first row of output from the ** stored procedure in question. Because of the way IB stored procedures ** work (generate... suspend; generate... suspend;), I can understand why ** the IB API works the way it does. ** ** This quirk explains why the branch of this function that uses ** isc_dsql_execute2 works as it conceptually should (i.e., it "realizes" ** that a user-defined exception has been raised as soon as it is ** conceptually raised), and why the branch that uses isc_dsql_execute-- ** the variant that does not immediately fetch the first row of output-- ** does not raise an error until fetch is called (and fetch may never be ** called!). ** ** Here is the basic problem: suppose a user executes a statement like: ** SELECT * FROM PROCEDURE_THAT_RAISES_EXCEPTION ** , but the user happens not to call fetch (i.e., decides to ignore the ** result set). In such a case, the user would never know that an ** exception had been raised, because the IB API itself would never ** register the fact that an exception had taken place. ** ** I've searched extensively for a way to fix this, but haven't found one. */ if (cursor->out_sqlda->sqld == 0) { /* The DB API spec says of Cursor.description: "This attribute will be ** None for operations that do not return rows...". */ Py_INCREF(Py_None); cursor_description = Py_None; } else { cursor_description = XSQLDA2Description(cursor->out_sqlda, cursor); if (cursor_description == NULL) goto EXECUTE_ERROR_CLEANUP; } /* In case of success, deliberately fall through into EXECUTE_NORMAL_RETURN. */ /* EXECUTE_NORMAL_RETURN: */ if ( free_XSQLVAR_dynamically_allocated_memory(cursor) != 0 ) { goto EXECUTE_ERROR_CLEANUP_EXCEPT_DYNVARALLOC; } return cursor_description; EXECUTE_ERROR_CLEANUP: free_XSQLVAR_dynamically_allocated_memory(cursor); EXECUTE_ERROR_CLEANUP_EXCEPT_DYNVARALLOC: /* Note that the cursor is closed AFTER the call to ** free_XSQLVAR_dynamically_allocated_memory. */ close_cursor_with_error(cursor); /* 2003.02.17c: */ return NULL; } /* pyob_execute */ static int _prepare_statement_if_necessary( CursorObject *cursor, PyObject *sql ) { ConnectionObject *conn = cursor->connection; assert (CON_GET_TRANS_HANDLE(conn) != NULL); /* 2003.10.15a:OK */ assert (cursor->_state == CURSOR_STATE_CLOSED); /* If this is another execution of the previous statement, no new preparation ** is necessary. */ { PyObject *prev_sql = cursor->previous_sql; if (prev_sql != NULL) { /* If the PyObject pointers point to the same memory location, the two ** objects are certainly equal--in fact, they're IDentical ** (id(prev_sql) == id(sql)). If the pointers refer to different memory ** locations, the two objects are still equal if their contents match. */ if (sql == prev_sql || PyObject_Compare(sql, prev_sql) == 0) { cursor->_state = CURSOR_STATE_OPEN; return 0; } } } if ( !( PyString_Check(sql) || PyUnicode_Check(sql) ) ) { raise_exception(PyExc_TypeError, "SQL must be string or unicode object."); return -1; } /* Free the old statement (if any) and any resources the cursor was caching ** in association with the old statement, including its output buffer. */ close_cursor(cursor); /* Moved statement allocation here from new_cursor in order to defer it. ** (The deferment makes cursor cleanup code much cleaner.) */ /* Allocate new statement handle. */ assert(cursor->stmt_handle == NULL); ENTER_DB isc_dsql_allocate_statement( cursor->status_vector, &(conn->db_handle), &(cursor->stmt_handle) ); LEAVE_DB if ( DB_API_ERROR(cursor->status_vector) ) { raise_sql_exception( OperationalError, "pyob_execute.isc_dsql_allocate_statement: ", cursor->status_vector ); return -1; } assert(cursor->stmt_handle != NULL); { /* translated_sql will only become non-NULL if $sql is a Unicode object ** rather than a string (and therefore must be translated to ASCII before ** being passed to isc_dsql_prepare). ** Since translated_sql, when non-NULL, refers to a *new* PyStringObject, it ** is unconditionally Py_XDECREFed at the end of this block. */ PyObject *translated_sql = NULL; char *sql_raw_buffer = NULL; int sql_len = -1; if ( PyString_Check(sql) ) { sql_raw_buffer = PyString_AS_STRING(sql); sql_len = PyString_GET_SIZE(sql); } else { /* At this point, $sql is certain to be a unicode object, because its ** type was constrained to either string or unicode at the beginning of ** this function. */ /* 2003.10.14: ** Ideally, we would pass the database engine's C API the incoming SQL ** statement without converting it to ASCII (i.e., via ** PyUnicode_AsWideChar or something similar), but it seems that the C ** API doesn't accept anything but ASCII. */ /* PyUnicode_AsASCIIString creates a *new* PyStringObject. */ translated_sql = PyUnicode_AsASCIIString(sql); if (translated_sql == NULL) { return -1; } sql_raw_buffer = PyString_AS_STRING(translated_sql); sql_len = PyString_GET_SIZE(translated_sql); } assert (sql_raw_buffer != NULL && sql_len >= 0); if (!_check_statement_length(sql_len)) { Py_XDECREF(translated_sql); return -1; } /* Ask the database engine to compile the statement. */ { /* 2003.10.15a: Don't call CON_GET_TRANS_HANDLE[_ADDR] without GIL. */ isc_tr_handle *trans_handle_addr = CON_GET_TRANS_HANDLE_ADDR(conn); ENTER_DB isc_dsql_prepare( cursor->status_vector, trans_handle_addr, &(cursor->stmt_handle), (unsigned short) sql_len, sql_raw_buffer, conn->dialect, cursor->out_sqlda ); LEAVE_DB } Py_XDECREF(translated_sql); } /* Ends block in which sql_raw_buffer is dealt with. */ if ( DB_API_ERROR(cursor->status_vector) ) { raise_sql_exception( ProgrammingError, "isc_dsql_prepare: ", cursor->status_vector ); return -1; } /* Ensure that the output XSQLDA has enough XSQLVAR slots for the statement's ** output variables. */ if ( reallocate_sqlda( &(cursor->out_sqlda), FALSE ) >= 0 ) { /* Reallocation succeeded, so bind information about the OUTput XSQLDA's ** variables. */ ENTER_DB isc_dsql_describe( cursor->status_vector, &(cursor->stmt_handle), conn->dialect, cursor->out_sqlda /* OUTPUT */ ); LEAVE_DB if ( DB_API_ERROR(cursor->status_vector) ) { raise_sql_exception( OperationalError, "isc_dsql_describe for output params: ", cursor->status_vector ); return -1; } } else { /* Reallocation failed. */ return -1; } /* Bind information about the INput XSQLDA's variables. */ ENTER_DB isc_dsql_describe_bind( cursor->status_vector, &(cursor->stmt_handle), conn->dialect, cursor->in_sqlda /* INPUT */ ); LEAVE_DB if ( DB_API_ERROR(cursor->status_vector) ) { raise_sql_exception( OperationalError, "isc_dsql_describe_bind for input params: ", cursor->status_vector ); return -1; } { int input_sqlda_reallocation_result = reallocate_sqlda( &(cursor->in_sqlda), TRUE ); #ifdef KIDB_DEBUGGERING printf("[in _prepare_statement_if_necessary] XSQLDA has: %d; needs: %d;" " reallocation result: %d\n", cursor->in_sqlda->sqln, cursor->in_sqlda->sqld, input_sqlda_reallocation_result ); #endif if (input_sqlda_reallocation_result == 0) { /* No actual reallocation was necessary, so there's no need to rebind. */ } else if (input_sqlda_reallocation_result == 1) { /* Reallocation was necessary, so the XSQLDA's parameter information must ** be rebound. */ ENTER_DB isc_dsql_describe_bind( cursor->status_vector, &(cursor->stmt_handle), conn->dialect, cursor->in_sqlda /* INPUT */ ); LEAVE_DB if ( DB_API_ERROR(cursor->status_vector) ) { raise_sql_exception( OperationalError, "isc_dsql_describe_bind[2] for input params: ", cursor->status_vector ); return -1; } } else { /* Reallocation failed. */ return -1; } } /* The statement needed to be prepared anew, so we must now update the ** cursor's cache. ** IMPORTANT: The cache is not updated until this point, near the END of ** this function, because all of the compilation preliminaries must first be ** hashed out. */ free_cursor_cache(cursor); { XSQLVAR *sqlvar; OriginalXSQLVARSpecificationCache *spec_cache; short var_no, column_count; column_count = cursor->in_sqlda->sqld; /* 2003.03.31: */ cursor->in_var_orig_spec = kimem_plain_malloc( sizeof(OriginalXSQLVARSpecificationCache) * column_count ); for ( sqlvar = cursor->in_sqlda->sqlvar, var_no = 0, spec_cache = cursor->in_var_orig_spec; var_no < column_count; sqlvar++, var_no++, spec_cache++ ) { spec_cache->sqltype = sqlvar->sqltype; spec_cache->sqllen = sqlvar->sqllen; } } Py_XDECREF(cursor->previous_sql); /* 2003.02.13 */ Py_INCREF(sql); cursor->previous_sql = sql; /* 2003.01.26: Determine the database API's internal type code for this ** statement and cache that code in the cursor. */ cursor->statement_type = determine_statement_type( &(cursor->stmt_handle), cursor->status_vector ); /* If this is a query statement, allocate a buffer for the output values. ** Ensure that this function freed the output buffer (via close_cursor) ** before it prepared the new statement. */ assert (cursor->out_buffer == NULL); if (cursor->out_sqlda->sqld > 0) { cursor->out_buffer = allocate_output_buffer(cursor->out_sqlda); if (cursor->out_buffer == NULL) { return -1; } } /* Done updating cursor's cache. */ /* The cursor was closed before we prepared the new statement; it is now ** open, and must be flagged accordingly. */ cursor->_state = CURSOR_STATE_OPEN; return 0; } /* _prepare_statement_if_necessary */ static int determine_statement_type( isc_stmt_handle *statementHandle, ISC_STATUS statusVector[] ) { /* Dynamically determine the statement type. Follows p. 343 of IB Beta 6 ** API Guide and IB example apifull.c. */ int statementType; int statementTypeLength; static char sqlInfoStatementTypeRequest[] = { isc_info_sql_stmt_type }; char sqlInfoResultBuffer[ ISC_INFO_BUFFER_SIZE ]; ENTER_DB isc_dsql_sql_info( statusVector, statementHandle, sizeof(sqlInfoStatementTypeRequest), sqlInfoStatementTypeRequest, sizeof(sqlInfoResultBuffer), sqlInfoResultBuffer ); /* Next two lines follow the Interbase example apifull.c rather than the ** API Guide. */ statementTypeLength = (short)( isc_vax_integer( (char *) sqlInfoResultBuffer + 1, 2 ) ); statementType = (int) ( isc_vax_integer( (char *) sqlInfoResultBuffer + 3, (short)statementTypeLength ) ); LEAVE_DB return statementType; } /* determine_statement_type */ /* 2003.05.15: */ static PyObject *pyob_rowcount( PyObject *self, PyObject *args ) { CursorObject *cur; int cursor_stmt_type; char request_params[] = {isc_info_sql_records, isc_info_end}; /* YYY: The fixed size of res_buf introduces the possibility of a buffer ** overflow, but it's extremely unlikely because we know quite a bit about ** the size requirements (they're not affected by input from the client ** programmer, or anything like that). */ char res_buf[256]; char *res_walk; long cur_count = -1; char cur_count_type; /* What type of statement does this count concern? */ short length_of_cur_count_in_buffer; if ( !PyArg_ParseTuple( args, "O!", &CursorType, &cur ) ) { return NULL; } cursor_stmt_type = cur->statement_type; if (cursor_stmt_type == NULL_STATEMENT_TYPE) { /* Python DB API Spec requires us to return -1 rather than raise exception. */ return PyInt_FromLong(-1); } assert(cur->stmt_handle != NULL); if ( cursor_stmt_type != isc_info_sql_stmt_select && cursor_stmt_type != isc_info_sql_stmt_insert && cursor_stmt_type != isc_info_sql_stmt_update && cursor_stmt_type != isc_info_sql_stmt_delete ) { /* Python DB API Spec requires us to return -1 rather than raise exception. */ return PyInt_FromLong(-1); } ENTER_DB isc_dsql_sql_info( cur->status_vector, &cur->stmt_handle, sizeof(request_params), request_params, sizeof(res_buf), res_buf ); LEAVE_DB if ( DB_API_ERROR(cur->status_vector) ) { raise_sql_exception(OperationalError, "pyob_rowcount: ", cur->status_vector); return NULL; } /* res_buf[0] indicates what type of information is being returned (in this ** situation, it never varies). */ assert (res_buf[0] == isc_info_sql_records); /* Start res_walk after the first 3 bytes, which are infrastructural. */ res_walk = res_buf + 3; while ( (cur_count_type = *res_walk) != isc_info_end ) { res_walk += 1; length_of_cur_count_in_buffer = (short) isc_vax_integer(res_walk, sizeof(short)); res_walk += sizeof(short); cur_count = isc_vax_integer(res_walk, length_of_cur_count_in_buffer); res_walk += length_of_cur_count_in_buffer; /* If the count that we've just extracted from the result buffer concerns ** the same statement type as the last statement executed by the cursor, ** immediately return that count to the Python level. ** All temporary storage in this function is allocated on the stack, so ** there's nothing for us to manually release. */ if ( ( cur_count_type == isc_info_req_select_count && cursor_stmt_type == isc_info_sql_stmt_select ) || ( cur_count_type == isc_info_req_insert_count && cursor_stmt_type == isc_info_sql_stmt_insert ) || ( cur_count_type == isc_info_req_update_count && cursor_stmt_type == isc_info_sql_stmt_update ) || ( cur_count_type == isc_info_req_delete_count && cursor_stmt_type == isc_info_sql_stmt_delete ) ) { return PyInt_FromLong(cur_count); } } /* end of "while not at end of result buffer..." loop */ /* Because of the "guards" near the beginning of this function, it is not ** expected that this code will ever be reached. However, a future version ** of the database engine may throw a curveball at us, so we'll behave as ** the Python DB API requires us to behave in cases where the row count ** cannot be determined (that is, return -1). */ return PyInt_FromLong(-1); } /* pyob_rowcount */ /* 2003.02.20: Although the sql-statement-length parameter to such Firebird ** API functions as isc_dsql_prepare and isc_dsql_execute_immediate is an ** unsigned short, the documentation says that the length can be left zero for ** null-terminated strings, in which case the database engine will figure out ** the length itself. ** As of 2003.02.13, Firebird cannot handle SQL statements longer than the ** maximum value of an unsigned short even if zero is passed as the length. */ static boolean _check_statement_length(long length) { /* Test the length and raise an exception if it's too long for safe passage ** to isc_* functions. Return TRUE if OK; FALSE otherwise. */ if (length > (long) USHRT_MAX) { char *err_msg = NULL; #if PYTHON_2_2_OR_LATER PyObject *buffer = PyString_FromFormat( "SQL statement of %ld bytes is too long (max %d allowed). Consider" " using parameters to shorten the SQL code, rather than passing large" " values as part of the SQL string.", length, USHRT_MAX ); if (buffer == NULL) { return FALSE; } err_msg = PyString_AS_STRING(buffer); #else err_msg = "Length of SQL statement must be <= USHRT_MAX"; #endif /* PYTHON_2_2_OR_LATER */ assert (err_msg != NULL); raise_exception(ProgrammingError, err_msg); #if PYTHON_2_2_OR_LATER Py_DECREF(buffer); #endif return FALSE; } return TRUE; } /* _validate_statement_length */