void QR_Destructor(QResultClass *self) { mylog("QResult: in DESTRUCTOR\n"); /* manual result set tuples */ if (self->manual_tuples) TL_Destructor(self->manual_tuples); /* If conn is defined, then we may have used "backend_tuples", */ /* so in case we need to, free it up. Also, close the cursor. */ if (self->conn && self->conn->sock && CC_is_in_trans(self->conn)) QR_close(self); /* close the cursor if there is one */ QR_free_memory(self); /* safe to call anyway */ /* Should have been freed in the close() but just in case... */ if (self->cursor) free(self->cursor); /* Free up column info */ if (self->fields) CI_Destructor(self->fields); /* Free command info (this is from strdup()) */ if (self->command) free(self->command); /* Free notice info (this is from strdup()) */ if (self->notice) free(self->notice); free(self); mylog("QResult: exit DESTRUCTOR\n"); }
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; }
static int HowToPrepareBeforeExec(StatementClass *stmt, BOOL checkOnly) { SQLSMALLINT num_params = stmt->num_params; ConnectionClass *conn = SC_get_conn(stmt); ConnInfo *ci = &(conn->connInfo); int nCallParse = doNothing, how_to_prepare = 0; BOOL bNeedsTrans = FALSE; if (num_params < 0) PGAPI_NumParams(stmt, &num_params); how_to_prepare = decideHowToPrepare(stmt, checkOnly); if (checkOnly) { if (num_params <= 0) return doNothing; } else { switch (how_to_prepare) { case USING_PREPARE_COMMAND: return checkOnly ? doNothing : usingCommand; case NAMED_PARSE_REQUEST: return shouldParse; case PARSE_TO_EXEC_ONCE: switch (stmt->prepared) { case PREPARED_TEMPORARILY: nCallParse = preferParse; break; default: if (num_params <= 0) nCallParse = NOPARAM_ONESHOT_CALL_PARSE; else nCallParse = ONESHOT_CALL_PARSE; } break; default: return doNothing; } } if (PG_VERSION_LE(conn, 7.3) || !PROTOCOL_74(ci)) return nCallParse; if (num_params > 0) { int param_number = -1; ParameterInfoClass *apara; ParameterImplClass *ipara; OID pgtype; while (TRUE) { SC_param_next(stmt, ¶m_number, &apara, &ipara); if (!ipara || !apara) break; pgtype = PIC_get_pgtype(*ipara); if (checkOnly) { switch (ipara->SQLType) { case SQL_LONGVARBINARY: if (0 == pgtype) { if (ci->bytea_as_longvarbinary && 0 != conn->lobj_type) nCallParse = shouldParse; } break; case SQL_CHAR: if (ci->cvt_null_date_string) nCallParse = shouldParse; break; case SQL_VARCHAR: if (ci->drivers.bools_as_char && PG_WIDTH_OF_BOOLS_AS_CHAR == ipara->column_size) nCallParse = shouldParse; break; } } else { BOOL bBytea = FALSE; switch (ipara->SQLType) { case SQL_LONGVARBINARY: if (conn->lobj_type == pgtype || PG_TYPE_OID == pgtype) bNeedsTrans = TRUE; else if (PG_TYPE_BYTEA == pgtype) bBytea = TRUE; else if (0 == pgtype) { if (ci->bytea_as_longvarbinary) bBytea = TRUE; else bNeedsTrans = TRUE; } if (bBytea) if (nCallParse < preferParse) nCallParse = preferParse; break; } } } } if (bNeedsTrans && PARSE_TO_EXEC_ONCE == how_to_prepare) { if (!CC_is_in_trans(conn) && CC_does_autocommit(conn)) nCallParse = doNothing; } return nCallParse; }
/* * Supplies parameter data at execution time. * Used in conjunction with SQLParamData. */ RETCODE SQL_API PGAPI_PutData( HSTMT hstmt, PTR rgbValue, SQLLEN cbValue) { CSTR func = "PGAPI_PutData"; StatementClass *stmt = (StatementClass *) hstmt, *estmt; ConnectionClass *conn; RETCODE retval = SQL_SUCCESS; APDFields *apdopts; IPDFields *ipdopts; PutDataInfo *pdata; SQLLEN old_pos; ParameterInfoClass *current_param; ParameterImplClass *current_iparam; PutDataClass *current_pdata; char *buffer, *putbuf, *allocbuf = NULL; Int2 ctype; SQLLEN putlen; BOOL lenset = FALSE, handling_lo = FALSE; mylog("%s: entering...\n", func); #define return DONT_CALL_RETURN_FROM_HERE??? if (!stmt) { SC_log_error(func, "", NULL); retval = SQL_INVALID_HANDLE; goto cleanup; } if (SC_AcceptedCancelRequest(stmt)) { SC_set_error(stmt, STMT_OPERATION_CANCELLED, "Cancel the statement, sorry.", func); retval = SQL_ERROR; goto cleanup; } estmt = stmt->execute_delegate ? stmt->execute_delegate : stmt; apdopts = SC_get_APDF(estmt); if (estmt->current_exec_param < 0) { SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Previous call was not SQLPutData or SQLParamData", func); retval = SQL_ERROR; goto cleanup; } current_param = &(apdopts->parameters[estmt->current_exec_param]); ipdopts = SC_get_IPDF(estmt); current_iparam = &(ipdopts->parameters[estmt->current_exec_param]); pdata = SC_get_PDTI(estmt); current_pdata = &(pdata->pdata[estmt->current_exec_param]); ctype = current_param->CType; conn = SC_get_conn(estmt); if (ctype == SQL_C_DEFAULT) { ctype = sqltype_to_default_ctype(conn, current_iparam->SQLType); if (SQL_C_WCHAR == ctype && CC_default_is_c(conn)) ctype = SQL_C_CHAR; } if (SQL_NTS == cbValue) { #ifdef UNICODE_SUPPORT if (SQL_C_WCHAR == ctype) { putlen = WCLEN * ucs2strlen((SQLWCHAR *) rgbValue); lenset = TRUE; } else #endif /* UNICODE_SUPPORT */ if (SQL_C_CHAR == ctype) { putlen = strlen(rgbValue); lenset = TRUE; } } if (!lenset) { if (cbValue < 0) putlen = cbValue; else #ifdef UNICODE_SUPPORT if (ctype == SQL_C_CHAR || ctype == SQL_C_BINARY || ctype == SQL_C_WCHAR) #else if (ctype == SQL_C_CHAR || ctype == SQL_C_BINARY) #endif /* UNICODE_SUPPORT */ putlen = cbValue; else putlen = ctype_length(ctype); } putbuf = rgbValue; handling_lo = (PIC_dsp_pgtype(conn, *current_iparam) == conn->lobj_type); if (handling_lo && SQL_C_CHAR == ctype) { allocbuf = malloc(putlen / 2 + 1); if (allocbuf) { pg_hex2bin(rgbValue, allocbuf, putlen); putbuf = allocbuf; putlen /= 2; } } if (!estmt->put_data) { /* first call */ mylog("PGAPI_PutData: (1) cbValue = %d\n", cbValue); estmt->put_data = TRUE; current_pdata->EXEC_used = (SQLLEN *) malloc(sizeof(SQLLEN)); if (!current_pdata->EXEC_used) { SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in PGAPI_PutData (1)", func); retval = SQL_ERROR; goto cleanup; } *current_pdata->EXEC_used = putlen; if (cbValue == SQL_NULL_DATA) { retval = SQL_SUCCESS; goto cleanup; } /* Handle Long Var Binary with Large Objects */ /* if (current_iparam->SQLType == SQL_LONGVARBINARY) */ if (handling_lo) { /* begin transaction if needed */ if (!CC_is_in_trans(conn)) { if (!CC_begin(conn)) { SC_set_error(stmt, STMT_EXEC_ERROR, "Could not begin (in-line) a transaction", func); retval = SQL_ERROR; goto cleanup; } } /* store the oid */ current_pdata->lobj_oid = odbc_lo_creat(conn, INV_READ | INV_WRITE); if (current_pdata->lobj_oid == 0) { SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt create large object.", func); retval = SQL_ERROR; goto cleanup; } /* * major hack -- to allow convert to see somethings there have * to modify convert to handle this better */ /***current_param->EXEC_buffer = (char *) ¤t_param->lobj_oid;***/ /* store the fd */ estmt->lobj_fd = odbc_lo_open(conn, current_pdata->lobj_oid, INV_WRITE); if (estmt->lobj_fd < 0) { SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt open large object for writing.", func); retval = SQL_ERROR; goto cleanup; } retval = odbc_lo_write(conn, estmt->lobj_fd, putbuf, (Int4) putlen); mylog("lo_write: cbValue=%d, wrote %d bytes\n", putlen, retval); } else { current_pdata->EXEC_buffer = malloc(putlen + 1); if (!current_pdata->EXEC_buffer) { SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in PGAPI_PutData (2)", func); retval = SQL_ERROR; goto cleanup; } memcpy(current_pdata->EXEC_buffer, putbuf, putlen); current_pdata->EXEC_buffer[putlen] = '\0'; } } else { /* calling SQLPutData more than once */ mylog("PGAPI_PutData: (>1) cbValue = %d\n", cbValue); /* if (current_iparam->SQLType == SQL_LONGVARBINARY) */ if (handling_lo) { /* the large object fd is in EXEC_buffer */ retval = odbc_lo_write(conn, estmt->lobj_fd, putbuf, (Int4) putlen); mylog("lo_write(2): cbValue = %d, wrote %d bytes\n", putlen, retval); *current_pdata->EXEC_used += putlen; } else { buffer = current_pdata->EXEC_buffer; old_pos = *current_pdata->EXEC_used; if (putlen > 0) { SQLLEN used = *current_pdata->EXEC_used + putlen, allocsize; for (allocsize = (1 << 4); allocsize <= used; allocsize <<= 1) ; mylog(" cbValue = %d, old_pos = %d, *used = %d\n", putlen, old_pos, used); /* dont lose the old pointer in case out of memory */ buffer = realloc(current_pdata->EXEC_buffer, allocsize); if (!buffer) { SC_set_error(stmt, STMT_NO_MEMORY_ERROR,"Out of memory in PGAPI_PutData (3)", func); retval = SQL_ERROR; goto cleanup; } memcpy(&buffer[old_pos], putbuf, putlen); buffer[used] = '\0'; /* reassign buffer incase realloc moved it */ *current_pdata->EXEC_used = used; current_pdata->EXEC_buffer = buffer; } else { SC_set_error(stmt, STMT_INTERNAL_ERROR, "bad cbValue", func); retval = SQL_ERROR; goto cleanup; } } } retval = SQL_SUCCESS; cleanup: #undef return if (allocbuf) free(allocbuf); if (stmt->internal) retval = DiscardStatementSvp(stmt, retval, TRUE); return retval; }
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; }
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; }