/* KInterbasDB Python Package - Implementation of Record Fetch ** ** 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 *******************/ /* Technically, isc_info_sql_stmt_exec_procedure can also return a result set, ** but that statement type is handled in a separate clause of pyob_fetch. */ #define COULD_STATEMENT_RETURN_RESULT_SET(statement_type) \ ( statement_type == isc_info_sql_stmt_select \ || statement_type == isc_info_sql_stmt_select_for_upd \ ) /****************** "PRIVATE" DECLARATIONS:END *******************/ static PyObject *pyob_fetch( PyObject *self, PyObject *args ) { CursorObject *cursor; PyObject *row; int statement_type; if ( !PyArg_ParseTuple(args, "O!", &CursorType, &cursor ) ) { return NULL; } CUR_REQUIRE_OPEN(cursor); /* If the result set has been exhausted and the cursor hasn't executed a ** fresh statement since, return None rather than raising an error. This ** behavior is required by the Python DB API. */ if (cursor->last_fetch_status == 100) { RETURN_PY_NONE; } /* 2003.01.26: The statement type is now cached by the cursor; it need not ** be recomputed every time. */ statement_type = cursor->statement_type; /* 2003.02.17: Raise exception if a fetch is attempted before a statement ** has been executed. */ if (statement_type == NULL_STATEMENT_TYPE) { raise_exception( ProgrammingError, "Cannot fetch from this cursor because it has not executed a statement." ); goto FETCH_ERROR_CLEANUP; } debug_printf2( "\n[in pyob_fetch] STATEMENT TYPE: %d, # of output params: %d\n", statement_type, cursor->out_sqlda->sqld ); /* If the cursor's statement type is isc_info_sql_stmt_exec_procedure, ** then use pseudo-fetch (for reasons explained in the comments in ** function pyob_execute regarding the difference between isc_dsql_execute ** and isc_dsql_execute2). */ if (statement_type == isc_info_sql_stmt_exec_procedure) { if (cursor->exec_proc_results != NULL) { row = cursor->exec_proc_results; debug_printf("[in pyob_execute] returning the single cached" " EXECUTE PROCEDURE result row\n"); /* Don't need to change reference count of exec_proc_results because ** we're passing reference ownership to the caller of this function. */ cursor->exec_proc_results = NULL; return row; } else { debug_printf("[in pyob_execute] returning None because cached" " EXECUTE PROCEDURE results already used\n"); RETURN_PY_NONE; } } else if ( !COULD_STATEMENT_RETURN_RESULT_SET(statement_type) ) { /* If the last executed statement couldn't possibly return a result set, ** the client programmer should not be asking for one. ** It is imperative that this situation be detected before the ** isc_dsql_fetch call below. Otherwise, isc_dsql_fetch will claim to ** have succeeded without really having done so, and will leave the ** cursor statement handle in an invalid state that causes the program ** to freeze when isc_dsql_free_st4tement is called (usually by object ** destructors or other cleanup code). */ const char *baseErrMsg = "Attempt to fetch row of results from a statement" " that does not produce a result set. That statement was: "; int baseErrMsg_len = strlen(baseErrMsg); int entireErrorMessage_len = baseErrMsg_len + PyString_Size(cursor->previous_sql); char *entireErrorMessage = (char *) kimem_main_malloc( sizeof(char) * (entireErrorMessage_len + 1) ); strncpy(entireErrorMessage, baseErrMsg, baseErrMsg_len); strncpy(entireErrorMessage + baseErrMsg_len, PyString_AsString(cursor->previous_sql), PyString_Size(cursor->previous_sql)); entireErrorMessage[entireErrorMessage_len] = '\0'; /* Add NULL terminator. */ raise_exception( ProgrammingError, entireErrorMessage ); kimem_main_free(entireErrorMessage); goto FETCH_ERROR_CLEANUP; } ENTER_DB cursor->last_fetch_status = isc_dsql_fetch( cursor->status_vector, &cursor->stmt_handle, cursor->connection->dialect, cursor->out_sqlda ); LEAVE_DB /* isc_dsql_fetch return value meanings: ** 0 -> success ** 100 -> result set exhausted ** anything else -> error */ switch (cursor->last_fetch_status) { case 0: row = XSQLDA2Tuple(cursor, cursor->out_sqlda); if (row == NULL) { goto FETCH_ERROR_CLEANUP; } return row; case 100: RETURN_PY_NONE; } /* default: error */ raise_sql_exception( ProgrammingError, "fetch.isc_dsql_fetch: ", cursor->status_vector ); /* Deliberately fall through into FETCH_ERROR_CLEANUP. */ FETCH_ERROR_CLEANUP: close_cursor_with_error(cursor); /* 2003.02.17c: */ return NULL; } /* pyob_fetch */