RETCODE DiscardStatementSvp(StatementClass *stmt, RETCODE ret, BOOL errorOnly) { CSTR func = "DiscardStatementSvp"; char esavepoint[32], cmd[64]; ConnectionClass *conn = SC_get_conn(stmt); QResultClass *res; BOOL cmd_success, start_stmt = FALSE; inolog("%s:%p->accessed=%d is_in=%d is_rb=%d is_tc=%d\n", func, stmt, SC_accessed_db(stmt), CC_is_in_trans(conn), SC_is_rb_stmt(stmt), SC_is_tc_stmt(stmt)); switch (ret) { case SQL_NEED_DATA: break; case SQL_ERROR: start_stmt = TRUE; break; default: if (!errorOnly) start_stmt = TRUE; break; } if (!SC_accessed_db(stmt) || !CC_is_in_trans(conn)) goto cleanup; if (!SC_is_rb_stmt(stmt) && !SC_is_tc_stmt(stmt)) goto cleanup; sprintf(esavepoint, "_EXEC_SVP_%p", stmt); if (SQL_ERROR == ret) { if (SC_started_rbpoint(stmt)) { snprintf(cmd, sizeof(cmd), "ROLLBACK to %s", esavepoint); res = CC_send_query(conn, cmd, NULL, IGNORE_ABORT_ON_CONN, NULL); cmd_success = QR_command_maybe_successful(res); QR_Destructor(res); if (!cmd_success) { SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal ROLLBACK failed", func); CC_abort(conn); goto cleanup; } } else { CC_abort(conn); goto cleanup; } } else if (errorOnly) return ret; inolog("ret=%d\n", ret); if (SQL_NEED_DATA != ret && SC_started_rbpoint(stmt)) { snprintf(cmd, sizeof(cmd), "RELEASE %s", esavepoint); res = CC_send_query(conn, cmd, NULL, IGNORE_ABORT_ON_CONN, NULL); cmd_success = QR_command_maybe_successful(res); QR_Destructor(res); if (!cmd_success) { SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal RELEASE failed", func); CC_abort(conn); ret = SQL_ERROR; } } cleanup: if (SQL_NEED_DATA != ret) SC_forget_unnamed(stmt); /* unnamed plan is no longer reliable */ if (!SC_is_prepare_statement(stmt) && ONCE_DESCRIBED == stmt->prepared) SC_set_prepared(stmt, NOT_YET_PREPARED); if (start_stmt || SQL_ERROR == ret) { if (stmt->lock_CC_for_rb > 0) { LEAVE_CONN_CS(conn); stmt->lock_CC_for_rb--; } SC_start_stmt(stmt); } return ret; }
RETCODE SQL_API PGAPI_Transact( HENV henv, HDBC hdbc, SQLUSMALLINT fType) { CSTR func = "PGAPI_Transact"; ConnectionClass *conn; char ok; int lf; mylog("entering %s: hdbc=%p, henv=%p\n", func, hdbc, henv); if (hdbc == SQL_NULL_HDBC && henv == SQL_NULL_HENV) { CC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } /* * If hdbc is null and henv is valid, it means transact all * connections on that henv. */ if (hdbc == SQL_NULL_HDBC && henv != SQL_NULL_HENV) { ConnectionClass * const *conns = getConnList(); const int conn_count = getConnCount(); for (lf = 0; lf < conn_count; lf++) { conn = conns[lf]; if (conn && CC_get_env(conn) == henv) if (PGAPI_Transact(henv, (HDBC) conn, fType) != SQL_SUCCESS) return SQL_ERROR; } return SQL_SUCCESS; } conn = (ConnectionClass *) hdbc; if (fType != SQL_COMMIT && fType != SQL_ROLLBACK) { CC_set_error(conn, CONN_INVALID_ARGUMENT_NO, "PGAPI_Transact can only be called with SQL_COMMIT or SQL_ROLLBACK as parameter", func); return SQL_ERROR; } /* If manual commit and in transaction, then proceed. */ if (CC_loves_visible_trans(conn) && CC_is_in_trans(conn)) { mylog("PGAPI_Transact: sending on conn %p '%d'\n", conn, fType); ok = (SQL_COMMIT == fType) ? CC_commit(conn) : CC_abort(conn); if (!ok) { /* error msg will be in the connection */ CC_on_abort(conn, NO_TRANS); CC_log_error(func, "", conn); return SQL_ERROR; } } return SQL_SUCCESS; }
/* * The execution after all parameters were resolved. */ static RETCODE Exec_with_parameters_resolved(StatementClass *stmt, BOOL *exec_end) { CSTR func = "Exec_with_parameters_resolved"; RETCODE retval; SQLLEN end_row; SQLINTEGER cursor_type, scroll_concurrency; ConnectionClass *conn; QResultClass *res; APDFields *apdopts; IPDFields *ipdopts; BOOL prepare_before_exec = FALSE; *exec_end = FALSE; conn = SC_get_conn(stmt); mylog("%s: copying statement params: trans_status=%d, len=%d, stmt='%s'\n", func, conn->transact_status, strlen(stmt->statement), stmt->statement); /* save the cursor's info before the execution */ cursor_type = stmt->options.cursor_type; scroll_concurrency = stmt->options.scroll_concurrency; /* Prepare the statement if possible at backend side */ if (!stmt->inaccurate_result) { if (HowToPrepareBeforeExec(stmt, FALSE) >= allowParse) prepare_before_exec = TRUE; } inolog("prepare_before_exec=%d srv=%d\n", prepare_before_exec, conn->connInfo.use_server_side_prepare); /* Create the statement with parameters substituted. */ retval = copy_statement_with_parameters(stmt, prepare_before_exec); stmt->current_exec_param = -1; if (retval != SQL_SUCCESS) { stmt->exec_current_row = -1; *exec_end = TRUE; return retval; /* error msg is passed from the above */ } mylog(" stmt_with_params = '%s'\n", stmt->stmt_with_params); /* * Dummy exection to get the column info. */ if (stmt->inaccurate_result && SC_is_parse_tricky(stmt)) { BOOL in_trans = CC_is_in_trans(conn); BOOL issued_begin = FALSE; QResultClass *curres; stmt->exec_current_row = -1; *exec_end = TRUE; if (!SC_is_pre_executable(stmt)) return SQL_SUCCESS; if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0) { /* do nothing */ } else if (!in_trans) { if (issued_begin = CC_begin(conn), !issued_begin) { SC_set_error(stmt, STMT_EXEC_ERROR, "Handle prepare error", func); return SQL_ERROR; } } /* we are now in a transaction */ res = CC_send_query(conn, stmt->stmt_with_params, NULL, 0, SC_get_ancestor(stmt)); if (!QR_command_maybe_successful(res)) { #ifndef _LEGACY_MODE_ if (PG_VERSION_LT(conn, 8.0)) CC_abort(conn); #endif /* LEGACY_MODE_ */ SC_set_error(stmt, STMT_EXEC_ERROR, "Handle prepare error", func); QR_Destructor(res); return SQL_ERROR; } SC_set_Result(stmt, res); for (curres = res; !curres->num_fields; curres = curres->next) ; SC_set_Curres(stmt, curres); if (CC_does_autocommit(conn)) { if (issued_begin) CC_commit(conn); } stmt->status = STMT_FINISHED; return SQL_SUCCESS; } /* * The real execution. */ mylog("about to begin SC_execute\n"); retval = SC_execute(stmt); if (retval == SQL_ERROR) { stmt->exec_current_row = -1; *exec_end = TRUE; return retval; } res = SC_get_Result(stmt); /* special handling of result for keyset driven cursors */ if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type && SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency) { QResultClass *kres; if (kres = res->next, kres) { if (kres->fields) CI_Destructor(kres->fields); kres->fields = res->fields; res->fields = NULL; kres->num_fields = res->num_fields; res->next = NULL; SC_set_Result(stmt, kres); res = kres; } } #ifdef NOT_USED else if (SC_is_concat_prepare_exec(stmt)) { if (res && QR_command_maybe_successful(res)) { QResultClass *kres; kres = res->next; inolog("res->next=%p\n", kres); res->next = NULL; SC_set_Result(stmt, kres); res = kres; SC_set_prepared(stmt, PREPARED_PERMANENTLY); } else { retval = SQL_ERROR; if (stmt->execute_statement) free(stmt->execute_statement); stmt->execute_statement = NULL; } } #endif /* NOT_USED */ #if (ODBCVER >= 0x0300) ipdopts = SC_get_IPDF(stmt); if (ipdopts->param_status_ptr) { switch (retval) { case SQL_SUCCESS: ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS; break; case SQL_SUCCESS_WITH_INFO: ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO; break; default: ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; break; } } #endif /* ODBCVER */ if (end_row = stmt->exec_end_row, end_row < 0) { apdopts = SC_get_APDF(stmt); end_row = (SQLINTEGER) apdopts->paramset_size - 1; } if (stmt->inaccurate_result || stmt->exec_current_row >= end_row) { *exec_end = TRUE; stmt->exec_current_row = -1; } else stmt->exec_current_row++; if (res) { #if (ODBCVER >= 0x0300) EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn); const char *cmd = QR_get_command(res); SQLLEN start_row; if (start_row = stmt->exec_start_row, start_row < 0) start_row = 0; if (retval == SQL_SUCCESS && NULL != cmd && start_row >= end_row && NULL != env && EN_is_odbc3(env)) { int count; if (sscanf(cmd , "UPDATE %d", &count) == 1) ; else if (sscanf(cmd , "DELETE %d", &count) == 1) ; else count = -1; if (0 == count) retval = SQL_NO_DATA; } #endif /* ODBCVER */ stmt->diag_row_count = res->recent_processed_row_count; } /* * The cursor's info was changed ? */ if (retval == SQL_SUCCESS && (stmt->options.cursor_type != cursor_type || stmt->options.scroll_concurrency != scroll_concurrency)) { SC_set_error(stmt, STMT_OPTION_VALUE_CHANGED, "cursor updatability changed", func); retval = SQL_SUCCESS_WITH_INFO; } return retval; }
RETCODE SC_execute(StatementClass *self) { static char* const func="SC_execute"; ConnectionClass *conn; QResultClass *res; char ok, was_ok, was_nonfatal; Int2 oldstatus, numcols; QueryInfo qi; conn = SC_get_conn(self); /* Begin a transaction if one is not already in progress */ /* * Basically we don't have to begin a transaction in autocommit mode * because Postgres backend runs in autocomit mode. We issue "BEGIN" * in the following cases. 1) we use declare/fetch and the statement * is SELECT (because declare/fetch must be called in a transaction). * 2) we are in autocommit off state and the statement isn't of type * OTHER. */ if (!self->internal && !CC_is_in_trans(conn) && ((globals.use_declarefetch && self->statement_type == STMT_TYPE_SELECT) || (!CC_is_in_autocommit(conn) && self->statement_type != STMT_TYPE_OTHER))) { mylog(" about to begin a transaction on statement = %u\n", self); res = CC_send_query(conn, "BEGIN", NULL); if (QR_aborted(res)) { SC_set_error(self, STMT_EXEC_ERROR, "Could not begin a transaction"); SC_log_error(func, "", self); return SQL_ERROR; } ok = QR_command_successful(res); mylog("SQLExecute: ok = %d, status = %d\n", ok, QR_get_status(res)); QR_Destructor(res); if (!ok) { SC_set_error(self, STMT_EXEC_ERROR, "Could not begin a transaction"); SC_log_error(func, "", self); return SQL_ERROR; } else CC_set_in_trans(conn); } oldstatus = conn->status; conn->status = CONN_EXECUTING; self->status = STMT_EXECUTING; /* If it's a SELECT statement, use a cursor. */ /* Note that the declare cursor has already been prepended to the statement */ /* in copy_statement... */ if (self->statement_type == STMT_TYPE_SELECT) { char fetch[128]; mylog(" Sending SELECT statement on stmt=%u, cursor_name='%s'\n", self, self->cursor_name); /* send the declare/select */ self->result = CC_send_query(conn, self->stmt_with_params, NULL); if (globals.use_declarefetch && self->result != NULL && QR_command_successful(self->result)) { QR_Destructor(self->result); /* That worked, so now send the fetch to start getting data back */ qi.result_in = NULL; qi.cursor = self->cursor_name; qi.row_size = globals.fetch_max; /* Most likely the rowset size will not be set by the application until after the statement is executed, so might as well use the cache size. The qr_next_tuple() function will correct for any discrepancies in sizes and adjust the cache accordingly. */ sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name); self->result = CC_send_query( conn, fetch, &qi); } mylog(" done sending the query:\n"); } else { /* not a SELECT statement so don't use a cursor */ mylog(" it's NOT a select statement: stmt=%u\n", self); self->result = CC_send_query(conn, self->stmt_with_params, NULL); /* We shouldn't send COMMIT. Postgres backend does the autocommit if neccessary. (Zoltan, 04/26/2000) */ /* Above seems wrong. Even in case of autocommit, started transactions must be committed. (Hiroshi, 02/11/2001) */ if ( ! self->internal && CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) { res = CC_send_query(conn, "COMMIT", NULL); QR_Destructor(res); CC_set_no_trans(conn); } } conn->status = oldstatus; self->status = STMT_FINISHED; /* Check the status of the result */ if (self->result) { was_ok = QR_command_successful(self->result); was_nonfatal = QR_command_nonfatal(self->result); if ( was_ok) SC_set_errornumber(self, STMT_OK); else SC_set_errornumber(self, was_nonfatal ? STMT_INFO_ONLY : STMT_ERROR_TAKEN_FROM_BACKEND); self->currTuple = -1; /* set cursor before the first tuple in the list */ self->current_col = -1; self->rowset_start = -1; /* see if the query did return any result columns */ numcols = QR_NumResultCols(self->result); /* now allocate the array to hold the binding info */ if (numcols > 0) { extend_bindings(self, numcols); if (self->bindings == NULL) { SC_set_error(self, STMT_NO_MEMORY_ERROR, "Could not get enough free memory to store the binding information"); SC_log_error(func, "", self); return SQL_ERROR; } } /* issue "ABORT" when query aborted */ if (QR_get_aborted(self->result) && ! self->internal ) CC_abort(conn); } else { /* Bad Error -- The error message will be in the Connection */ if (self->statement_type == STMT_TYPE_CREATE) { SC_set_error(self, STMT_CREATE_TABLE_ERROR, "Error creating the table"); /* This would allow the table to already exists, thus appending rows to it. BUT, if the table didn't have the same attributes, it would fail. return SQL_SUCCESS_WITH_INFO; */ } else { SC_set_error(self, STMT_EXEC_ERROR, "Error while executing the query"); } if ( ! self->internal) CC_abort(conn); } if (SC_get_errornumber(self) == STMT_OK) return SQL_SUCCESS; else { /* Modified, 2000-04-29, Zoltan */ if (SC_get_errornumber(self) == STMT_INFO_ONLY) SC_set_errormsg(self, "Error while executing the query (non-fatal)"); else SC_set_errormsg(self, "Unknown error"); SC_log_error(func, "", self); return SQL_ERROR; } }