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; }
/* Execute a prepared SQL statement */ RETCODE SQL_API PGAPI_Execute(HSTMT hstmt, UWORD flag) { CSTR func = "PGAPI_Execute"; StatementClass *stmt = (StatementClass *) hstmt; RETCODE retval = SQL_SUCCESS; ConnectionClass *conn; APDFields *apdopts; IPDFields *ipdopts; SQLLEN i, start_row, end_row; BOOL exec_end, recycled = FALSE, recycle = TRUE; SQLSMALLINT num_params; mylog("%s: entering...%x\n", func, flag); if (!stmt) { SC_log_error(func, "", NULL); mylog("%s: NULL statement so return SQL_INVALID_HANDLE\n", func); return SQL_INVALID_HANDLE; } conn = SC_get_conn(stmt); apdopts = SC_get_APDF(stmt); /* * If the statement is premature, it means we already executed it from * an SQLPrepare/SQLDescribeCol type of scenario. So just return * success. */ if (stmt->prepare && stmt->status == STMT_PREMATURE) { if (stmt->inaccurate_result) { stmt->exec_current_row = -1; SC_recycle_statement(stmt); } else { stmt->status = STMT_FINISHED; if (NULL == SC_get_errormsg(stmt)) { mylog("%s: premature statement but return SQL_SUCCESS\n", func); retval = SQL_SUCCESS; goto cleanup; } else { SC_set_error(stmt,STMT_INTERNAL_ERROR, "premature statement so return SQL_ERROR", func); retval = SQL_ERROR; goto cleanup; } } } mylog("%s: clear errors...\n", func); SC_clear_error(stmt); if (!stmt->statement) { SC_set_error(stmt, STMT_NO_STMTSTRING, "This handle does not have a SQL statement stored in it", func); mylog("%s: problem with handle\n", func); return SQL_ERROR; } #define return DONT_CALL_RETURN_FROM_HERE??? if (stmt->exec_current_row > 0) { /* * executing an array of parameters. * Don't recycle the statement. */ recycle = FALSE; } else if (PREPARED_PERMANENTLY == stmt->prepared || PREPARED_TEMPORARILY == stmt->prepared) { QResultClass *res; /* * re-executing an prepared statement. * Don't recycle the statement but * discard the old result. */ recycle = FALSE; if (res = SC_get_Result(stmt), res) QR_close_result(res, FALSE); } /* * If SQLExecute is being called again, recycle the statement. Note * this should have been done by the application in a call to * SQLFreeStmt(SQL_CLOSE) or SQLCancel. */ else if (stmt->status == STMT_FINISHED) { mylog("%s: recycling statement (should have been done by app)...\n", func); /******** Is this really NEEDED ? ******/ SC_recycle_statement(stmt); recycled = TRUE; } /* Check if the statement is in the correct state */ else if ((stmt->prepare && stmt->status != STMT_READY) || (stmt->status != STMT_ALLOCATED && stmt->status != STMT_READY)) { SC_set_error(stmt, STMT_STATUS_ERROR, "The handle does not point to a statement that is ready to be executed", func); mylog("%s: problem with statement\n", func); retval = SQL_ERROR; goto cleanup; } if (start_row = stmt->exec_start_row, start_row < 0) start_row = 0; if (end_row = stmt->exec_end_row, end_row < 0) end_row = (SQLINTEGER) apdopts->paramset_size - 1; if (stmt->exec_current_row < 0) stmt->exec_current_row = start_row; ipdopts = SC_get_IPDF(stmt); num_params = stmt->num_params; if (num_params < 0) PGAPI_NumParams(stmt, &num_params); if (stmt->exec_current_row == start_row) { /* We sometimes need to know about the PG type of binding parameters even in case of non-prepared statements. */ int nCallParse = doNothing; if (NOT_YET_PREPARED == stmt->prepared) { switch (nCallParse = HowToPrepareBeforeExec(stmt, TRUE)) { case shouldParse: if (retval = prepareParameters(stmt, TRUE), SQL_ERROR == retval) goto cleanup; break; } } mylog("prepareParameters was %s called, prepare state:%d\n", shouldParse == nCallParse ? "" : "not", stmt->prepare); if (ipdopts->param_processed_ptr) *ipdopts->param_processed_ptr = 0; #if (ODBCVER >= 0x0300) /* * Initialize the param_status_ptr */ if (ipdopts->param_status_ptr) { for (i = 0; i <= end_row; i++) ipdopts->param_status_ptr[i] = SQL_PARAM_UNUSED; } #endif /* ODBCVER */ if (recycle && !recycled) SC_recycle_statement(stmt); if (isSqlServr() && !stmt->internal && 0 != stmt->prepare && PG_VERSION_LT(conn, 8.4) && SC_can_parse_statement(stmt)) parse_sqlsvr(stmt); } /* * Clear any old result sets before executing. The prepare stage might've * created one. */ SC_set_Result(stmt, NULL); next_param_row: #if (ODBCVER >= 0x0300) if (apdopts->param_operation_ptr) { while (apdopts->param_operation_ptr[stmt->exec_current_row] == SQL_PARAM_IGNORE) { if (stmt->exec_current_row >= end_row) { stmt->exec_current_row = -1; retval = SQL_SUCCESS; goto cleanup; } ++stmt->exec_current_row; } } /* * Initialize the current row status */ if (ipdopts->param_status_ptr) ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; #endif /* ODBCVER */ /* * Check if statement has any data-at-execute parameters when it is * not in SC_pre_execute. */ if (!stmt->pre_executing) { /* * The bound parameters could have possibly changed since the last * execute of this statement? Therefore check for params and * re-copy. */ SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0; SQLINTEGER bind_size = apdopts->param_bind_type; SQLLEN current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; Int4 num_p = num_params < apdopts->allocated ? num_params : apdopts->allocated; /* * Increment the number of currently processed rows */ if (ipdopts->param_processed_ptr) (*ipdopts->param_processed_ptr)++; stmt->data_at_exec = -1; for (i = 0; i < num_p; i++) { SQLLEN *pcVal = apdopts->parameters[i].used; apdopts->parameters[i].data_at_exec = FALSE; if (pcVal) { if (bind_size > 0) pcVal = LENADDR_SHIFT(pcVal, offset + bind_size * current_row); else pcVal = LENADDR_SHIFT(pcVal, offset) + current_row; if (*pcVal == SQL_DATA_AT_EXEC || *pcVal <= SQL_LEN_DATA_AT_EXEC_OFFSET) apdopts->parameters[i].data_at_exec = TRUE; } /* Check for data at execution parameters */ if (apdopts->parameters[i].data_at_exec) { mylog("The %dth parameter of %d-row is data at exec(%d)\n", i, current_row, *pcVal); if (stmt->data_at_exec < 0) stmt->data_at_exec = 1; else stmt->data_at_exec++; } } /* * If there are some data at execution parameters, return need * data */ /* * SQLParamData and SQLPutData will be used to send params and * execute the statement. */ if (stmt->data_at_exec > 0) { retval = SQL_NEED_DATA; goto cleanup; } } if (0 != (flag & PODBC_WITH_HOLD)) SC_set_with_hold(stmt); retval = Exec_with_parameters_resolved(stmt, &exec_end); if (!exec_end) { stmt->curr_param_result = 0; goto next_param_row; } cleanup: mylog("retval=%d\n", retval); SC_setInsertedTable(stmt, retval); #undef return if (stmt->internal) retval = DiscardStatementSvp(stmt, retval, FALSE); return retval; }
/* Perform a Prepare on the SQL statement */ RETCODE SQL_API PGAPI_Prepare(HSTMT hstmt, const SQLCHAR FAR * szSqlStr, SQLINTEGER cbSqlStr) { CSTR func = "PGAPI_Prepare"; StatementClass *self = (StatementClass *) hstmt; RETCODE retval = SQL_SUCCESS; mylog("%s: entering...\n", func); #define return DONT_CALL_RETURN_FROM_HERE??? /* StartRollbackState(self); */ if (!self) { SC_log_error(func, "", NULL); retval = SQL_INVALID_HANDLE; goto cleanup; } /* * According to the ODBC specs it is valid to call SQLPrepare multiple * times. In that case, the bound SQL statement is replaced by the new * one */ SC_set_prepared(self, NOT_YET_PREPARED); switch (self->status) { case STMT_PREMATURE: mylog("**** PGAPI_Prepare: STMT_PREMATURE, recycle\n"); SC_recycle_statement(self); /* recycle the statement, but do * not remove parameter bindings */ break; case STMT_FINISHED: mylog("**** PGAPI_Prepare: STMT_FINISHED, recycle\n"); SC_recycle_statement(self); /* recycle the statement, but do * not remove parameter bindings */ break; case STMT_ALLOCATED: mylog("**** PGAPI_Prepare: STMT_ALLOCATED, copy\n"); self->status = STMT_READY; break; case STMT_READY: mylog("**** PGAPI_Prepare: STMT_READY, change SQL\n"); break; case STMT_EXECUTING: mylog("**** PGAPI_Prepare: STMT_EXECUTING, error!\n"); SC_set_error(self, STMT_SEQUENCE_ERROR, "PGAPI_Prepare(): The handle does not point to a statement that is ready to be executed", func); retval = SQL_ERROR; goto cleanup; default: SC_set_error(self, STMT_INTERNAL_ERROR, "An Internal Error has occured -- Unknown statement status.", func); retval = SQL_ERROR; goto cleanup; } SC_initialize_stmts(self, TRUE); if (!szSqlStr) { SC_set_error(self, STMT_NO_MEMORY_ERROR, "the query is NULL", func); retval = SQL_ERROR; goto cleanup; } if (!szSqlStr[0]) self->statement = strdup(""); else self->statement = make_string(szSqlStr, cbSqlStr, NULL, 0); if (!self->statement) { SC_set_error(self, STMT_NO_MEMORY_ERROR, "No memory available to store statement", func); retval = SQL_ERROR; goto cleanup; } self->prepare = PREPARE_STATEMENT; self->statement_type = statement_type(self->statement); /* Check if connection is onlyread (only selects are allowed) */ if (CC_is_onlyread(SC_get_conn(self)) && STMT_UPDATE(self)) { SC_set_error(self, STMT_EXEC_ERROR, "Connection is readonly, only select statements are allowed.", func); retval = SQL_ERROR; goto cleanup; } cleanup: #undef return inolog("SQLPrepare return=%d\n", retval); if (self->internal) retval = DiscardStatementSvp(self, retval, FALSE); return retval; }
/* Bind parameters on a statement handle */ RETCODE SQL_API PGAPI_BindParameter(HSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLULEN cbColDef, SQLSMALLINT ibScale, PTR rgbValue, SQLLEN cbValueMax, SQLLEN FAR * pcbValue) { StatementClass *stmt = (StatementClass *) hstmt; CSTR func = "PGAPI_BindParameter"; APDFields *apdopts; IPDFields *ipdopts; PutDataInfo *pdata_info; mylog("%s: entering...\n", func); if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } SC_clear_error(stmt); apdopts = SC_get_APDF(stmt); if (apdopts->allocated < ipar) extend_parameter_bindings(apdopts, ipar); ipdopts = SC_get_IPDF(stmt); if (ipdopts->allocated < ipar) extend_iparameter_bindings(ipdopts, ipar); pdata_info = SC_get_PDTI(stmt); if (pdata_info->allocated < ipar) extend_putdata_info(pdata_info, ipar, FALSE); /* use zero based column numbers for the below part */ ipar--; /* store the given info */ apdopts->parameters[ipar].buflen = cbValueMax; apdopts->parameters[ipar].buffer = rgbValue; apdopts->parameters[ipar].used = apdopts->parameters[ipar].indicator = pcbValue; apdopts->parameters[ipar].CType = fCType; ipdopts->parameters[ipar].SQLType = fSqlType; ipdopts->parameters[ipar].paramType = fParamType; ipdopts->parameters[ipar].column_size = cbColDef; ipdopts->parameters[ipar].decimal_digits = ibScale; ipdopts->parameters[ipar].precision = 0; ipdopts->parameters[ipar].scale = 0; #if (ODBCVER >= 0x0300) switch (fCType) { case SQL_C_NUMERIC: if (cbColDef > 0) ipdopts->parameters[ipar].precision = (UInt2) cbColDef; if (ibScale > 0) ipdopts->parameters[ipar].scale = ibScale; break; case SQL_C_TYPE_TIMESTAMP: if (ibScale > 0) ipdopts->parameters[ipar].precision = ibScale; break; case SQL_C_INTERVAL_DAY_TO_SECOND: case SQL_C_INTERVAL_HOUR_TO_SECOND: case SQL_C_INTERVAL_MINUTE_TO_SECOND: case SQL_C_INTERVAL_SECOND: ipdopts->parameters[ipar].precision = 6; break; } apdopts->parameters[ipar].precision = ipdopts->parameters[ipar].precision; apdopts->parameters[ipar].scale = ipdopts->parameters[ipar].scale; #endif /* ODBCVER */ /* * If rebinding a parameter that had data-at-exec stuff in it, then * free that stuff */ if (pdata_info->pdata[ipar].EXEC_used) { free(pdata_info->pdata[ipar].EXEC_used); pdata_info->pdata[ipar].EXEC_used = NULL; } if (pdata_info->pdata[ipar].EXEC_buffer) { free(pdata_info->pdata[ipar].EXEC_buffer); pdata_info->pdata[ipar].EXEC_buffer = NULL; } if (pcbValue && apdopts->param_offset_ptr) pcbValue = LENADDR_SHIFT(pcbValue, *apdopts->param_offset_ptr); #ifdef NOT_USED /* evaluation of pcbValue here is dangerous */ /* Data at exec macro only valid for C char/binary data */ if (pcbValue && (*pcbValue == SQL_DATA_AT_EXEC || *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)) apdopts->parameters[ipar].data_at_exec = TRUE; else apdopts->parameters[ipar].data_at_exec = FALSE; #endif /* NOT_USED */ /* Clear premature result */ if (stmt->status == STMT_PREMATURE) SC_recycle_statement(stmt); mylog("%s: ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=%d, ibScale=%d,", func, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale); mylog("rgbValue=%p(%d), pcbValue=%p\n", rgbValue, cbValueMax, pcbValue); return SQL_SUCCESS; }