RETCODE SQL_API PG_SQLFreeStmt(HSTMT hstmt, UWORD fOption) { static char* const func="SQLFreeStmt"; StatementClass *stmt = (StatementClass *) hstmt; mylog("%s: entering...hstmt=%u, fOption=%d\n", func, hstmt, fOption); if ( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } if (fOption == SQL_DROP) { ConnectionClass *conn = stmt->hdbc; /* Remove the statement from the connection's statement list */ if ( conn) { if ( ! CC_remove_statement(conn, stmt)) { SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction."); SC_log_error(func, "", stmt); return SQL_ERROR; /* stmt may be executing a transaction */ } /* Free any cursors and discard any result info */ if (stmt->result) { QR_Destructor(stmt->result); stmt->result = NULL; } } /* Destroy the statement and free any results, cursors, etc. */ SC_Destructor(stmt); } else if (fOption == SQL_UNBIND) { SC_unbind_cols(stmt); } else if (fOption == SQL_CLOSE) { /* this should discard all the results, but leave the statement */ /* itself in place (it can be executed again) */ if (!SC_recycle_statement(stmt)) { /* errormsg passed in above */ SC_log_error(func, "", stmt); return SQL_ERROR; } } else if(fOption == SQL_RESET_PARAMS) { SC_free_params(stmt, STMT_FREE_PARAMS_ALL); } else { SC_set_error(stmt, STMT_OPTION_OUT_OF_RANGE_ERROR, "Invalid option passed to SQLFreeStmt."); SC_log_error(func, "", stmt); return SQL_ERROR; } return SQL_SUCCESS; }
char SC_Destructor(StatementClass *self) { mylog("SC_Destructor: self=%u, self->result=%u, self->hdbc=%u\n", self, self->result, self->hdbc); if (STMT_EXECUTING == self->status) { SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction."); return FALSE; } if (self->result) { if ( ! self->hdbc) self->result->conn = NULL; /* prevent any dbase activity */ QR_Destructor(self->result); } if (self->statement) free(self->statement); SC_free_params(self, STMT_FREE_PARAMS_ALL); /* the memory pointed to by the bindings is not deallocated by the driver */ /* by by the application that uses that driver, so we don't have to care */ /* about that here. */ if (self->bindings) free(self->bindings); /* Free the parsed table information */ if (self->ti) { int i; for (i = 0; i < self->ntab; i++) { free(self->ti[i]); } free(self->ti); } /* Free the parsed field information */ if (self->fi) { int i; for (i = 0; i < self->nfld; i++) { free(self->fi[i]); } free(self->fi); } SC_set_errormsg(self, NULL); free(self); mylog("SC_Destructor: EXIT\n"); return TRUE; }
static char * CC_lookup_cs_new(ConnectionClass *self) { char *encstr = NULL; QResultClass *res; res = CC_send_query(self, "select pg_client_encoding()", NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL); if (QR_command_maybe_successful(res)) { const char *enc = QR_get_value_backend_text(res, 0, 0); if (enc) encstr = strdup(enc); } QR_Destructor(res); return encstr; }
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; }
/* * Must be in a transaction or the subsequent execution * invokes a transaction. */ RETCODE SetStatementSvp(StatementClass *stmt) { CSTR func = "SetStatementSvp"; char esavepoint[32], cmd[64]; ConnectionClass *conn = SC_get_conn(stmt); QResultClass *res; RETCODE ret = SQL_SUCCESS_WITH_INFO; if (CC_is_in_error_trans(conn)) return ret; if (0 == stmt->lock_CC_for_rb) { ENTER_CONN_CS(conn); stmt->lock_CC_for_rb++; } switch (stmt->statement_type) { case STMT_TYPE_SPECIAL: case STMT_TYPE_TRANSACTION: return ret; } if (!SC_accessed_db(stmt)) { BOOL need_savep = FALSE; if (stmt->internal) { if (PG_VERSION_GE(conn, 8.0)) SC_start_rb_stmt(stmt); else SC_start_tc_stmt(stmt); } if (SC_is_rb_stmt(stmt)) { if (CC_is_in_trans(conn)) { need_savep = TRUE; } } if (need_savep) { sprintf(esavepoint, "_EXEC_SVP_%p", stmt); snprintf(cmd, sizeof(cmd), "SAVEPOINT %s", esavepoint); res = CC_send_query(conn, cmd, NULL, 0, NULL); if (QR_command_maybe_successful(res)) { SC_set_accessed_db(stmt); SC_start_rbpoint(stmt); ret = SQL_SUCCESS; } else { SC_set_error(stmt, STMT_INTERNAL_ERROR, "internal SAVEPOINT failed", func); ret = SQL_ERROR; } QR_Destructor(res); } else SC_set_accessed_db(stmt); } inolog("%s:%p->accessed=%d\n", func, stmt, SC_accessed_db(stmt)); return ret; }
/* * 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; }
void CC_lookup_characterset(ConnectionClass *self) { char *encspec = NULL, *currenc = NULL, *tencstr; CSTR func = "CC_lookup_characterset"; mylog("%s: entering...\n", func); if (self->original_client_encoding) encspec = strdup(self->original_client_encoding); if (self->current_client_encoding) currenc = strdup(self->current_client_encoding); else if (PG_VERSION_LT(self, 7.2)) currenc = CC_lookup_cs_old(self); else currenc = CC_lookup_cs_new(self); tencstr = encspec ? encspec : currenc; if (self->original_client_encoding) { if (stricmp(self->original_client_encoding, tencstr)) { char msg[256]; snprintf(msg, sizeof(msg), "The client_encoding '%s' was changed to '%s'", self->original_client_encoding, tencstr); CC_set_error(self, CONN_OPTION_VALUE_CHANGED, msg, func); } free(self->original_client_encoding); } #ifndef UNICODE_SUPPORT else { const char *wenc = get_environment_encoding(self, encspec, currenc, FALSE); if (wenc && (!tencstr || stricmp(tencstr, wenc))) { QResultClass *res; char query[64]; int errnum = CC_get_errornumber(self); BOOL cmd_success; sprintf(query, "set client_encoding to '%s'", wenc); res = CC_send_query(self, query, NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL); cmd_success = QR_command_maybe_successful(res); QR_Destructor(res); CC_set_errornumber(self, errnum); if (cmd_success) { self->original_client_encoding = strdup(wenc); self->ccsc = pg_CS_code(self->original_client_encoding); if (encspec) free(encspec); if (currenc) free(currenc); return; } } } #endif /* UNICODE_SUPPORT */ if (tencstr) { self->original_client_encoding = tencstr; if (encspec && currenc) free(currenc); self->ccsc = pg_CS_code(tencstr); qlog(" [ Client encoding = '%s' (code = %d) ]\n", self->original_client_encoding, self->ccsc); if (self->ccsc < 0) { char msg[256]; snprintf(msg, sizeof(msg), "would handle the encoding '%s' like ASCII", tencstr); CC_set_error(self, CONN_OPTION_VALUE_CHANGED, msg, func); } } else { self->ccsc = SQL_ASCII; self->original_client_encoding = NULL; } self->mb_maxbyte_per_char = pg_mb_maxlen(self->ccsc); }
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; } }
/* Called from SQLPrepare if STMT_PREMATURE, or from SQLExecute if STMT_FINISHED, or from SQLFreeStmt(SQL_CLOSE) */ char SC_recycle_statement(StatementClass *self) { ConnectionClass *conn; mylog("recycle statement: self= %u\n", self); /* This would not happen */ if (self->status == STMT_EXECUTING) { SC_set_error(self, STMT_SEQUENCE_ERROR, "Statement is currently executing a transaction."); return FALSE; } SC_set_errormsg(self, NULL); SC_clear_error(self); switch (self->status) { case STMT_ALLOCATED: /* this statement does not need to be recycled */ return TRUE; case STMT_READY: break; case STMT_PREMATURE: /* Premature execution of the statement might have caused the start of a transaction. If so, we have to rollback that transaction. */ conn = SC_get_conn(self); if ( ! CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) { CC_send_query(conn, "ABORT", NULL); CC_set_no_trans(conn); } break; case STMT_FINISHED: break; default: SC_set_error(self, STMT_INTERNAL_ERROR, "An internal error occured while recycling statements"); return FALSE; } /* Free the parsed table information */ if (self->ti) { int i; for (i = 0; i < self->ntab; i++) { free(self->ti[i]); } free(self->ti); self->ti = NULL; self->ntab = 0; } /* Free the parsed field information */ if (self->fi) { int i; for (i = 0; i < self->nfld; i++) { free(self->fi[i]); } free(self->fi); self->fi = NULL; self->nfld = 0; } self->parse_status = STMT_PARSE_NONE; /* Free any cursors */ if (self->result) { QR_Destructor(self->result); self->result = NULL; } /****************************************************************/ /* Reset only parameters that have anything to do with results */ /****************************************************************/ self->status = STMT_READY; self->manual_result = FALSE; /* very important */ self->currTuple = -1; self->rowset_start = -1; self->current_col = -1; self->bind_row = 0; self->last_fetch_count = 0; SC_set_errormsg(self, NULL); SC_clear_error(self); self->lobj_fd = -1; /* Free any data at exec params before the statement is executed */ /* again. If not, then there will be a memory leak when */ /* the next SQLParamData/SQLPutData is called. */ SC_free_params(self, STMT_FREE_PARAMS_DATA_AT_EXEC_ONLY); return TRUE; }