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; }
/* If the statement does not have parameters, it should just return 0. */ RETCODE SQL_API SQLNumParams( HSTMT hstmt, SWORD FAR *pcpar) { StatementClass *stmt = (StatementClass *) hstmt; char in_quote = FALSE; unsigned int i; static char* const func = "SQLNumParams"; mylog( "%s: entering...\n", func); if(!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } if (pcpar) *pcpar = 0; else { SC_log_error(func, "pcpar was null", stmt); return SQL_ERROR; } if(!stmt->statement) { /* no statement has been allocated */ SC_set_error(stmt, STMT_SEQUENCE_ERROR, "SQLNumParams called with no statement ready."); SC_log_error(func, "", stmt); return SQL_ERROR; } else { size_t stlen=strlen(stmt->statement); for(i=0; i < stlen; i++) { if(stmt->statement[i] == '?' && !in_quote) (*pcpar)++; else { if (stmt->statement[i] == '\'') in_quote = (in_quote ? FALSE : TRUE); } } return SQL_SUCCESS; } }
RETCODE SQL_API SQLDescribeParam( HSTMT hstmt, UWORD ipar, SWORD FAR *pfSqlType, SQLULEN *pcbColDef, SWORD FAR *pibScale, SWORD FAR *pfNullable) { StatementClass *stmt = (StatementClass *) hstmt; static char* const func = "SQLDescribeParam"; mylog( "%s: entering...\n", func); if( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } if( (ipar < 1) || (ipar > stmt->parameters_allocated) ) { SC_set_error(stmt, STMT_BAD_PARAMETER_NUMBER_ERROR, "Invalid parameter number for SQLDescribeParam."); SC_log_error(func, "", stmt); return SQL_ERROR; } ipar--; /* This implementation is not very good, since it is supposed to describe */ /* parameter markers, not bound parameters. */ if(pfSqlType) *pfSqlType = stmt->parameters[ipar].SQLType; if(pcbColDef) *pcbColDef = stmt->parameters[ipar].precision; if(pibScale) *pibScale = stmt->parameters[ipar].scale; if(pfNullable) *pfNullable = pgtype_nullable(stmt, stmt->parameters[ipar].paramType); return SQL_SUCCESS; }
RETCODE SQL_API SQLParamOptions( HSTMT hstmt, SQLULEN crow, SQLULEN FAR *pirow) { static char* const func = "SQLParamOptions"; mylog( "%s: entering...\n", func); SC_log_error(func, "Function not implemented", (StatementClass *) hstmt); return SQL_ERROR; }
/* * This function should really talk to the dbms to determine the number of * "parameter markers" (not bound parameters) in the statement. But, since * Postgres doesn't support that, the driver should just count the number of markers * and return that. The reason the driver just can't say this function is unsupported * like it does for SQLDescribeParam is that some applications don't care and try * to call it anyway. * If the statement does not have parameters, it should just return 0. */ RETCODE SQL_API PGAPI_NumParams( HSTMT hstmt, SQLSMALLINT FAR * pcpar) { StatementClass *stmt = (StatementClass *) hstmt; CSTR func = "PGAPI_NumParams"; mylog("%s: entering...\n", func); if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } if (pcpar) *pcpar = 0; else { SC_set_error(stmt, STMT_EXEC_ERROR, "parameter count address is null", func); return SQL_ERROR; } inolog("num_params=%d,%d\n", stmt->num_params, stmt->proc_return); if (stmt->num_params >= 0) *pcpar = stmt->num_params; else if (!stmt->statement) { /* no statement has been allocated */ SC_set_error(stmt, STMT_SEQUENCE_ERROR, "PGAPI_NumParams called with no statement ready.", func); return SQL_ERROR; } else { po_ind_t multi = FALSE, proc_return = 0; stmt->proc_return = 0; SC_scanQueryAndCountParams(stmt->statement, SC_get_conn(stmt), NULL, pcpar, &multi, &proc_return); stmt->num_params = *pcpar; stmt->proc_return = proc_return; stmt->multi_statement = multi; if (multi) SC_no_parse_tricky(stmt); } inolog("num_params=%d,%d\n", stmt->num_params, stmt->proc_return); return SQL_SUCCESS; }
RETCODE SQL_API SQLSetStmtOption( HSTMT hstmt, UWORD fOption, SQLULEN vParam) { static char *func="SQLSetStmtOption"; StatementClass *stmt = (StatementClass *) hstmt; char changed = FALSE; mylog("%s: entering...\n", func); /*// thought we could fake Access out by just returning SQL_SUCCESS */ /*// all the time, but it tries to set a huge value for SQL_MAX_LENGTH */ /*// and expects the driver to reduce it to the real value */ if( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } return set_statement_option(NULL, stmt, fOption, vParam); }
RETCODE SQL_API PGAPI_Cancel( HSTMT hstmt) /* Statement to cancel. */ { CSTR func = "PGAPI_Cancel"; StatementClass *stmt = (StatementClass *) hstmt, *estmt; ConnectionClass *conn; RETCODE ret = SQL_SUCCESS; BOOL entered_cs = FALSE; mylog("%s: entering...\n", func); /* Check if this can handle canceling in the middle of a SQLPutData? */ if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } conn = SC_get_conn(stmt); #define return DONT_CALL_RETURN_FROM_HERE??? /* StartRollbackState(stmt); */ if (stmt->execute_delegate) estmt = stmt->execute_delegate; else estmt = stmt; /* * Not in the middle of SQLParamData/SQLPutData so cancel like a * close. */ if (estmt->data_at_exec < 0) { /* * Tell the Backend that we're cancelling this request */ if (estmt->status == STMT_EXECUTING) { if (!CC_send_cancel_request(conn)) { ret = SQL_ERROR; } goto cleanup; } /* * MAJOR HACK for Windows to reset the driver manager's cursor * state: Because of what seems like a bug in the Odbc driver * manager, SQLCancel does not act like a SQLFreeStmt(CLOSE), as * many applications depend on this behavior. So, this brute * force method calls the driver manager's function on behalf of * the application. */ if (conn->driver_version < 0x0350) { #ifdef WIN32 ConnInfo *ci = &(conn->connInfo); if (ci->drivers.cancel_as_freestmt) { typedef SQLRETURN (SQL_API *SQLAPIPROC)(); HMODULE hmodule; SQLAPIPROC addr; hmodule = GetModuleHandle("ODBC32"); addr = (SQLAPIPROC) GetProcAddress(hmodule, "SQLFreeStmt"); ret = addr((char *) (stmt->phstmt) - 96, SQL_CLOSE); } else #endif /* WIN32 */ { ENTER_STMT_CS(stmt); entered_cs = TRUE; SC_clear_error(hstmt); ret = PGAPI_FreeStmt(hstmt, SQL_CLOSE); } mylog("PGAPI_Cancel: PGAPI_FreeStmt returned %d\n", ret); } goto cleanup; } /* In the middle of SQLParamData/SQLPutData, so cancel that. */ /* * Note, any previous data-at-exec buffers will be freed in the * recycle */ /* if they call SQLExecDirect or SQLExecute again. */ ENTER_STMT_CS(stmt); entered_cs = TRUE; SC_clear_error(stmt); estmt->data_at_exec = -1; estmt->current_exec_param = -1; estmt->put_data = FALSE; cancelNeedDataState(estmt); cleanup: #undef return if (entered_cs) { if (stmt->internal) ret = DiscardStatementSvp(stmt, ret, FALSE); LEAVE_STMT_CS(stmt); } return ret; }
/* 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; }
/* * Returns the description of a parameter marker. * This function is listed as not being supported by SQLGetFunctions() because it is * used to describe "parameter markers" (not bound parameters), in which case, * the dbms should return info on the markers. Since Postgres doesn't support that, * it is best to say this function is not supported and let the application assume a * data type (most likely varchar). */ RETCODE SQL_API PGAPI_DescribeParam(HSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT FAR * pfSqlType, SQLULEN FAR * pcbParamDef, SQLSMALLINT FAR * pibScale, SQLSMALLINT FAR * pfNullable) { StatementClass *stmt = (StatementClass *) hstmt; CSTR func = "PGAPI_DescribeParam"; IPDFields *ipdopts; RETCODE ret = SQL_SUCCESS; int num_params; OID pgtype; mylog("%s: entering...%d\n", func, ipar); if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } SC_clear_error(stmt); ipdopts = SC_get_IPDF(stmt); /*if ((ipar < 1) || (ipar > ipdopts->allocated))*/ num_params = stmt->num_params; if (num_params < 0) { SQLSMALLINT num_p; PGAPI_NumParams(stmt, &num_p); num_params = num_p; } if ((ipar < 1) || (ipar > num_params)) { inolog("num_params=%d\n", stmt->num_params); SC_set_error(stmt, STMT_BAD_PARAMETER_NUMBER_ERROR, "Invalid parameter number for PGAPI_DescribeParam.", func); return SQL_ERROR; } extend_iparameter_bindings(ipdopts, stmt->num_params); #define return DONT_CALL_RETURN_FROM_HERE??? /* StartRollbackState(stmt); */ if (NOT_YET_PREPARED == stmt->prepared) { decideHowToPrepare(stmt, FALSE); inolog("howTo=%d\n", SC_get_prepare_method(stmt)); switch (SC_get_prepare_method(stmt)) { case NAMED_PARSE_REQUEST: case PARSE_TO_EXEC_ONCE: case PARSE_REQ_FOR_INFO: if (ret = prepareParameters(stmt), SQL_ERROR == ret) goto cleanup; } } ipar--; pgtype = PIC_get_pgtype(ipdopts->parameters[ipar]); /* * This implementation is not very good, since it is supposed to * describe */ /* parameter markers, not bound parameters. */ if (pfSqlType) { inolog("[%d].SQLType=%d .PGType=%d\n", ipar, ipdopts->parameters[ipar].SQLType, pgtype); if (ipdopts->parameters[ipar].SQLType) *pfSqlType = ipdopts->parameters[ipar].SQLType; else if (pgtype) *pfSqlType = pgtype_to_concise_type(stmt, pgtype, PG_STATIC); else { ret = SQL_ERROR; SC_set_error(stmt, STMT_EXEC_ERROR, "Unfortunatley couldn't get this paramater's info", func); goto cleanup; } } if (pcbParamDef) { *pcbParamDef = 0; if (ipdopts->parameters[ipar].SQLType) *pcbParamDef = ipdopts->parameters[ipar].column_size; if (0 == *pcbParamDef && pgtype) *pcbParamDef = pgtype_column_size(stmt, pgtype, PG_STATIC, PG_STATIC); } if (pibScale) { *pibScale = 0; if (ipdopts->parameters[ipar].SQLType) *pibScale = ipdopts->parameters[ipar].decimal_digits; else if (pgtype) *pibScale = pgtype_scale(stmt, pgtype, -1); } if (pfNullable) *pfNullable = pgtype_nullable(SC_get_conn(stmt), ipdopts->parameters[ipar].paramType); cleanup: #undef return if (stmt->internal) ret = DiscardStatementSvp(stmt, ret, FALSE); return ret; }
/* Associate a user-supplied buffer with a database column. */ RETCODE SQL_API PGAPI_BindCol(HSTMT hstmt, SQLUSMALLINT icol, SQLSMALLINT fCType, PTR rgbValue, SQLLEN cbValueMax, SQLLEN FAR * pcbValue) { StatementClass *stmt = (StatementClass *) hstmt; CSTR func = "PGAPI_BindCol"; ARDFields *opts; GetDataInfo *gdata_info; BindInfoClass *bookmark; RETCODE ret = SQL_SUCCESS; mylog("%s: entering...\n", func); mylog("**** PGAPI_BindCol: stmt = %p, icol = %d\n", stmt, icol); mylog("**** : fCType=%d rgb=%p valusMax=%d pcb=%p\n", fCType, rgbValue, cbValueMax, pcbValue); if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } opts = SC_get_ARDF(stmt); if (stmt->status == STMT_EXECUTING) { SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't bind columns while statement is still executing.", func); return SQL_ERROR; } #define return DONT_CALL_RETURN_FROM_HERE ??? SC_clear_error(stmt); /* If the bookmark column is being bound, then just save it */ if (icol == 0) { bookmark = opts->bookmark; if (rgbValue == NULL) { if (bookmark) { bookmark->buffer = NULL; bookmark->used = bookmark->indicator = NULL; } } else { /* Make sure it is the bookmark data type */ switch (fCType) { case SQL_C_BOOKMARK: #if (ODBCVER >= 0x0300) case SQL_C_VARBOOKMARK: #endif /* ODBCVER */ break; default: SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE, "Bind column 0 is not of type SQL_C_BOOKMARK", func); inolog("Bind column 0 is type %d not of type SQL_C_BOOKMARK", fCType); ret = SQL_ERROR; goto cleanup; } bookmark = ARD_AllocBookmark(opts); bookmark->buffer = rgbValue; bookmark->used = bookmark->indicator = pcbValue; bookmark->buflen = cbValueMax; bookmark->returntype = fCType; } goto cleanup; } /* * Allocate enough bindings if not already done. Most likely, * execution of a statement would have setup the necessary bindings. * But some apps call BindCol before any statement is executed. */ if (icol > opts->allocated) extend_column_bindings(opts, icol); gdata_info = SC_get_GDTI(stmt); if (icol > gdata_info->allocated) extend_getdata_info(gdata_info, icol, FALSE); /* check to see if the bindings were allocated */ if (!opts->bindings) { SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Could not allocate memory for bindings.", func); ret = SQL_ERROR; goto cleanup; } /* use zero based col numbers from here out */ icol--; /* Reset for SQLGetData */ gdata_info->gdata[icol].data_left = -1; if (rgbValue == NULL) { /* we have to unbind the column */ opts->bindings[icol].buflen = 0; opts->bindings[icol].buffer = NULL; opts->bindings[icol].used = opts->bindings[icol].indicator = NULL; opts->bindings[icol].returntype = SQL_C_CHAR; opts->bindings[icol].precision = 0; opts->bindings[icol].scale = 0; if (gdata_info->gdata[icol].ttlbuf) free(gdata_info->gdata[icol].ttlbuf); gdata_info->gdata[icol].ttlbuf = NULL; gdata_info->gdata[icol].ttlbuflen = 0; gdata_info->gdata[icol].ttlbufused = 0; } else { /* ok, bind that column */ opts->bindings[icol].buflen = cbValueMax; opts->bindings[icol].buffer = rgbValue; opts->bindings[icol].used = opts->bindings[icol].indicator = pcbValue; opts->bindings[icol].returntype = fCType; opts->bindings[icol].precision = 0; #if (ODBCVER >= 0x0300) switch (fCType) { case SQL_C_NUMERIC: opts->bindings[icol].precision = 32; break; case SQL_C_TIMESTAMP: 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: opts->bindings[icol].precision = 6; break; } #endif /* ODBCVER */ opts->bindings[icol].scale = 0; mylog(" bound buffer[%d] = %p\n", icol, opts->bindings[icol].buffer); } cleanup: #undef return if (stmt->internal) ret = DiscardStatementSvp(stmt, ret, FALSE); return ret; }
/* 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; }
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; } }
RETCODE SC_fetch(StatementClass *self) { static char* const func = "SC_fetch"; QResultClass *res = self->result; int retval, result; Int2 num_cols, lf; Oid type; char *value; ColumnInfoClass *ci; /* TupleField *tupleField; */ self->last_fetch_count = 0; ci = QR_get_fields(res); /* the column info */ mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, globals.use_declarefetch); if ( self->manual_result || ! globals.use_declarefetch) { if (self->currTuple >= QR_get_num_tuples(res) -1 || (self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1)) { /* if at the end of the tuples, return "no data found" and set the cursor past the end of the result set */ self->currTuple = QR_get_num_tuples(res); return SQL_NO_DATA_FOUND; } mylog("**** SQLFetch: manual_result\n"); (self->currTuple)++; } else { /* read from the cache or the physical next tuple */ retval = QR_next_tuple(res); if (retval < 0) { mylog("**** SQLFetch: end_tuples\n"); return SQL_NO_DATA_FOUND; } else if (retval > 0) (self->currTuple)++; /* all is well */ else { mylog("SQLFetch: error\n"); SC_set_error(self, STMT_EXEC_ERROR, "Error fetching next row"); SC_log_error(func, "", self); return SQL_ERROR; } } num_cols = QR_NumResultCols(res); result = SQL_SUCCESS; self->last_fetch_count = 1; /* If the bookmark column was bound then return a bookmark. Since this is used with SQLExtendedFetch, and the rowset size may be greater than 1, and an application can use row or column wise binding, use the code in copy_and_convert_field() to handle that. */ if (self->bookmark.buffer) { char buf[32]; sprintf(buf, "%ld", SC_get_bookmark(self)); result = copy_and_convert_field(self, 0, buf, SQL_C_ULONG, self->bookmark.buffer, 0, self->bookmark.used); } for (lf=0; lf < num_cols; lf++) { mylog("fetch: cols=%d, lf=%d, self = %u, self->bindings = %u, buffer[] = %u\n", num_cols, lf, self, self->bindings, self->bindings[lf].buffer); /* reset for SQLGetData */ self->bindings[lf].data_left = -1; if (self->bindings[lf].buffer != NULL) { /* this column has a binding */ /* type = QR_get_field_type(res, lf); */ type = CI_get_oid(ci, lf); /* speed things up */ mylog("type = %d\n", type); if (self->manual_result) { value = QR_get_value_manual(res, self->currTuple, lf); mylog("manual_result\n"); } else if (globals.use_declarefetch) value = QR_get_value_backend(res, lf); else { value = QR_get_value_backend_row(res, self->currTuple, lf); } mylog("value = '%s'\n", (value==NULL)?"<NULL>":value); retval = copy_and_convert_field_bindinfo(self, type, value, lf); mylog("copy_and_convert: retval = %d\n", retval); switch(retval) { case COPY_OK: break; /* OK, do next bound column */ case COPY_UNSUPPORTED_TYPE: SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Received an unsupported type from Postgres."); SC_log_error(func, "", self); result = SQL_ERROR; break; case COPY_UNSUPPORTED_CONVERSION: SC_set_error(self, STMT_RESTRICTED_DATA_TYPE_ERROR, "Couldn't handle the necessary data type conversion."); SC_log_error(func, "", self); result = SQL_ERROR; break; case COPY_RESULT_TRUNCATED: SC_set_error(self, STMT_TRUNCATED, "The buffer was too small for the result."); result = SQL_SUCCESS_WITH_INFO; break; case COPY_GENERAL_ERROR: /* error msg already filled in */ SC_log_error(func, "", self); result = SQL_ERROR; break; /* This would not be meaningful in SQLFetch. */ case COPY_NO_DATA_FOUND: break; default: SC_set_error(self, STMT_INTERNAL_ERROR, "Unrecognized return value from copy_and_convert_field."); SC_log_error(func, "", self); result = SQL_ERROR; break; } } } return result; }
SQLRETURN SQLBindParameter( SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLULEN cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue) { StatementClass *stmt = (StatementClass *) hstmt; static char* const func="SQLBindParameter"; mylog( "%s: entering...\n", func); if( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } if(stmt->parameters_allocated < ipar) { ParameterInfoClass *old_parameters; int i, old_parameters_allocated; old_parameters = stmt->parameters; old_parameters_allocated = stmt->parameters_allocated; stmt->parameters = (ParameterInfoClass *) malloc(sizeof(ParameterInfoClass)*(ipar)); if ( ! stmt->parameters) { SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Could not allocate memory for statement parameters"); SC_log_error(func, "", stmt); return SQL_ERROR; } stmt->parameters_allocated = ipar; /* copy the old parameters over */ for(i = 0; i < old_parameters_allocated; i++) { /* a structure copy should work */ stmt->parameters[i] = old_parameters[i]; } /* get rid of the old parameters, if there were any */ if(old_parameters) free(old_parameters); /* zero out the newly allocated parameters (in case they skipped some, */ /* so we don't accidentally try to use them later) */ for(; i < stmt->parameters_allocated; i++) { stmt->parameters[i].buflen = 0; stmt->parameters[i].buffer = 0; stmt->parameters[i].used = 0; stmt->parameters[i].paramType = 0; stmt->parameters[i].CType = 0; stmt->parameters[i].SQLType = 0; stmt->parameters[i].precision = 0; stmt->parameters[i].scale = 0; stmt->parameters[i].data_at_exec = FALSE; stmt->parameters[i].lobj_oid = 0; stmt->parameters[i].EXEC_used = NULL; stmt->parameters[i].EXEC_buffer = NULL; } } ipar--; /* use zero based column numbers for the below part */ /* store the given info */ stmt->parameters[ipar].buflen = cbValueMax; stmt->parameters[ipar].buffer = rgbValue; stmt->parameters[ipar].used = pcbValue; stmt->parameters[ipar].paramType = fParamType; stmt->parameters[ipar].CType = fCType; stmt->parameters[ipar].SQLType = fSqlType; stmt->parameters[ipar].precision = cbColDef; stmt->parameters[ipar].scale = ibScale; /* If rebinding a parameter that had data-at-exec stuff in it, then free that stuff */ if (stmt->parameters[ipar].EXEC_used) { free(stmt->parameters[ipar].EXEC_used); stmt->parameters[ipar].EXEC_used = NULL; } if (stmt->parameters[ipar].EXEC_buffer) { if (stmt->parameters[ipar].SQLType != SQL_LONGVARBINARY) free(stmt->parameters[ipar].EXEC_buffer); stmt->parameters[ipar].EXEC_buffer = NULL; } /* Data at exec macro only valid for C char/binary data */ if ((fSqlType == SQL_LONGVARBINARY || fSqlType == SQL_LONGVARCHAR) && pcbValue && (*pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET || *pcbValue == SQL_DATA_AT_EXEC )) stmt->parameters[ipar].data_at_exec = TRUE; else stmt->parameters[ipar].data_at_exec = FALSE; mylog("SQLBindParamater: ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=%d, ibScale=%d, rgbValue=%d, *pcbValue = %d, data_at_exec = %d\n", ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, pcbValue ? *pcbValue: -777, stmt->parameters[ipar].data_at_exec); return SQL_SUCCESS; }
/* * Supplies parameter data at execution time. * Used in conjuction with SQLPutData. */ RETCODE SQL_API PGAPI_ParamData( HSTMT hstmt, PTR FAR * prgbValue) { CSTR func = "PGAPI_ParamData"; StatementClass *stmt = (StatementClass *) hstmt, *estmt; APDFields *apdopts; IPDFields *ipdopts; RETCODE retval; int i; Int2 num_p; ConnectionClass *conn = NULL; mylog("%s: entering...\n", func); if (!stmt) { SC_log_error(func, "", NULL); retval = SQL_INVALID_HANDLE; goto cleanup; } conn = SC_get_conn(stmt); estmt = stmt->execute_delegate ? stmt->execute_delegate : stmt; apdopts = SC_get_APDF(estmt); mylog("%s: data_at_exec=%d, params_alloc=%d\n", func, estmt->data_at_exec, apdopts->allocated); #define return DONT_CALL_RETURN_FROM_HERE??? if (SC_AcceptedCancelRequest(stmt)) { SC_set_error(stmt, STMT_OPERATION_CANCELLED, "Cancel the statement, sorry", func); retval = SQL_ERROR; goto cleanup; } if (estmt->data_at_exec < 0) { SC_set_error(stmt, STMT_SEQUENCE_ERROR, "No execution-time parameters for this statement", func); retval = SQL_ERROR; goto cleanup; } if (estmt->data_at_exec > apdopts->allocated) { SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Too many execution-time parameters were present", func); retval = SQL_ERROR; goto cleanup; } /* close the large object */ if (estmt->lobj_fd >= 0) { odbc_lo_close(conn, estmt->lobj_fd); /* commit transaction if needed */ if (!CC_cursor_count(conn) && CC_does_autocommit(conn)) { if (!CC_commit(conn)) { SC_set_error(stmt, STMT_EXEC_ERROR, "Could not commit (in-line) a transaction", func); retval = SQL_ERROR; goto cleanup; } } estmt->lobj_fd = -1; } /* Done, now copy the params and then execute the statement */ ipdopts = SC_get_IPDF(estmt); inolog("ipdopts=%p\n", ipdopts); if (estmt->data_at_exec == 0) { BOOL exec_end; UWORD flag = SC_is_with_hold(stmt) ? PODBC_WITH_HOLD : 0; retval = Exec_with_parameters_resolved(estmt, &exec_end); if (exec_end) { /**SC_reset_delegate(retval, stmt);**/ retval = dequeueNeedDataCallback(retval, stmt); goto cleanup; } if (retval = PGAPI_Execute(estmt, flag), SQL_NEED_DATA != retval) { goto cleanup; } } /* * Set beginning param; if first time SQLParamData is called , start * at 0. Otherwise, start at the last parameter + 1. */ i = estmt->current_exec_param >= 0 ? estmt->current_exec_param + 1 : 0; num_p = estmt->num_params; if (num_p < 0) PGAPI_NumParams(estmt, &num_p); inolog("i=%d allocated=%d num_p=%d\n", i, apdopts->allocated, num_p); if (num_p > apdopts->allocated) num_p = apdopts->allocated; /* At least 1 data at execution parameter, so Fill in the token value */ for (; i < num_p; i++) { inolog("i=%d", i); if (apdopts->parameters[i].data_at_exec) { inolog(" at exec buffer=%p", apdopts->parameters[i].buffer); estmt->data_at_exec--; estmt->current_exec_param = i; estmt->put_data = FALSE; if (prgbValue) { /* returns token here */ if (stmt->execute_delegate) { SQLULEN offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0; SQLLEN perrow = apdopts->param_bind_type > 0 ? apdopts->param_bind_type : apdopts->parameters[i].buflen; inolog(" offset=%d perrow=%d", offset, perrow); *prgbValue = apdopts->parameters[i].buffer + offset + estmt->exec_current_row * perrow; } else *prgbValue = apdopts->parameters[i].buffer; } break; } inolog("\n"); } retval = SQL_NEED_DATA; inolog("return SQL_NEED_DATA\n"); cleanup: #undef return SC_setInsertedTable(stmt, retval); if (stmt->internal) retval = DiscardStatementSvp(stmt, retval, FALSE); mylog("%s: returning %d\n", func, retval); return retval; }
RETCODE SQL_API SQLGetStmtOption( HSTMT hstmt, UWORD fOption, PTR pvParam) { static char *func="SQLGetStmtOption"; StatementClass *stmt = (StatementClass *) hstmt; QResultClass *res; mylog("%s: entering...\n", func); /*// thought we could fake Access out by just returning SQL_SUCCESS */ /*// all the time, but it tries to set a huge value for SQL_MAX_LENGTH */ /*// and expects the driver to reduce it to the real value */ if( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } switch(fOption) { case SQL_GET_BOOKMARK: case SQL_ROW_NUMBER: res = stmt->result; if ( stmt->manual_result || ! globals.use_declarefetch) { /*// make sure we're positioned on a valid row */ if((stmt->currTuple < 0) || (stmt->currTuple >= QR_get_num_tuples(res))) { stmt->errormsg = "Not positioned on a valid row."; stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } } else { if (stmt->currTuple == -1 || ! res || ! res->tupleField) { stmt->errormsg = "Not positioned on a valid row."; stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR; SC_log_error(func, "", stmt); return SQL_ERROR; } } if (fOption == SQL_GET_BOOKMARK && stmt->options.use_bookmarks == SQL_UB_OFF) { stmt->errormsg = "Operation invalid because use bookmarks not enabled."; stmt->errornumber = STMT_OPERATION_INVALID; SC_log_error(func, "", stmt); return SQL_ERROR; } *((UDWORD *) pvParam) = SC_get_bookmark(stmt); break; case SQL_ASYNC_ENABLE: /* NOT SUPPORTED */ *((SDWORD *) pvParam) = SQL_ASYNC_ENABLE_OFF; break; case SQL_BIND_TYPE: *((SDWORD *) pvParam) = stmt->options.bind_size; break; case SQL_CONCURRENCY: /* NOT REALLY SUPPORTED */ mylog("GetStmtOption(): SQL_CONCURRENCY\n"); *((SDWORD *)pvParam) = stmt->options.scroll_concurrency; break; case SQL_CURSOR_TYPE: /* PARTIAL SUPPORT */ mylog("GetStmtOption(): SQL_CURSOR_TYPE\n"); *((SDWORD *)pvParam) = stmt->options.cursor_type; break; case SQL_KEYSET_SIZE: /* NOT SUPPORTED, but saved */ mylog("GetStmtOption(): SQL_KEYSET_SIZE\n"); *((SDWORD *)pvParam) = stmt->options.keyset_size; break; case SQL_MAX_LENGTH: /* NOT SUPPORTED, but saved */ *((SDWORD *)pvParam) = stmt->options.maxLength; break; case SQL_MAX_ROWS: /* NOT SUPPORTED, but saved */ *((SDWORD *)pvParam) = stmt->options.maxRows; mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->options.maxRows); break; case SQL_NOSCAN:/* NOT SUPPORTED */ *((SDWORD *) pvParam) = SQL_NOSCAN_ON; break; case SQL_QUERY_TIMEOUT: /* NOT SUPPORTED */ *((SDWORD *) pvParam) = 0; break; case SQL_RETRIEVE_DATA: /* NOT SUPPORTED, but saved */ *((SDWORD *) pvParam) = stmt->options.retrieve_data; break; case SQL_ROWSET_SIZE: *((SDWORD *) pvParam) = stmt->options.rowset_size; break; case SQL_SIMULATE_CURSOR:/* NOT SUPPORTED */ *((SDWORD *) pvParam) = SQL_SC_NON_UNIQUE; break; case SQL_USE_BOOKMARKS: *((SDWORD *) pvParam) = stmt->options.use_bookmarks; break; /* * added to be nice to StarOffice and OpenOffice */ case /*SQL_ATTR_CURSOR_SENSITIVITY*/ 65534: *((SDWORD *) pvParam) = /*SQL_UNSPECIFIED*/ 0; break; default: { char option[64]; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Unknown statement option (Get)"; sprintf(option, "fOption=%d", fOption); SC_log_error(func, option, stmt); return SQL_ERROR; } } return SQL_SUCCESS; }
/* * 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 set_statement_option(ConnectionClass *conn, StatementClass *stmt, UWORD fOption, UDWORD vParam) { static char *func="set_statement_option"; char changed = FALSE; switch(fOption) { case SQL_ASYNC_ENABLE:/* ignored */ break; case SQL_BIND_TYPE: /* now support multi-column and multi-row binding */ if (conn) conn->stmtOptions.bind_size = vParam; if (stmt) stmt->options.bind_size = vParam; break; case SQL_CONCURRENCY: /* positioned update isn't supported so cursor concurrency is read-only */ if (conn) conn->stmtOptions.scroll_concurrency = vParam; if (stmt) stmt->options.scroll_concurrency = vParam; break; /* if (globals.lie) { if (conn) conn->stmtOptions.scroll_concurrency = vParam; if (stmt) stmt->options.scroll_concurrency = vParam; } else { if (conn) conn->stmtOptions.scroll_concurrency = SQL_CONCUR_READ_ONLY; if (stmt) stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; if (vParam != SQL_CONCUR_READ_ONLY) changed = TRUE; } break; */ case SQL_CURSOR_TYPE: /* if declare/fetch, then type can only be forward. otherwise, it can only be forward or static. */ mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam); if (globals.lie) { if (conn) conn->stmtOptions.cursor_type = vParam; if (stmt) stmt->options.cursor_type = vParam; } else { if (globals.use_declarefetch) { if (conn) conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY; if (stmt) stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY; if (vParam != SQL_CURSOR_FORWARD_ONLY) changed = TRUE; } else { if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC) { if (conn) conn->stmtOptions.cursor_type = vParam; /*// valid type */ if (stmt) stmt->options.cursor_type = vParam; /*// valid type */ } else { if (conn) conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC; if (stmt) stmt->options.cursor_type = SQL_CURSOR_STATIC; changed = TRUE; } } } break; case SQL_KEYSET_SIZE: /* ignored, but saved and returned */ mylog("SetStmtOption(): SQL_KEYSET_SIZE, vParam = %d\n", vParam); if (conn) conn->stmtOptions.keyset_size = vParam; if (stmt) stmt->options.keyset_size = vParam; break; /* if (globals.lie) stmt->keyset_size = vParam; else { stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Driver does not support keyset size option"; SC_log_error(func, "", stmt); return SQL_ERROR; } */ case SQL_MAX_LENGTH:/* ignored, but saved */ mylog("SetStmtOption(): SQL_MAX_LENGTH, vParam = %d\n", vParam); if (conn) conn->stmtOptions.maxLength = vParam; if (stmt) stmt->options.maxLength = vParam; break; case SQL_MAX_ROWS: /* ignored, but saved */ mylog("SetStmtOption(): SQL_MAX_ROWS, vParam = %d\n", vParam); if (conn) conn->stmtOptions.maxRows = vParam; if (stmt) stmt->options.maxRows = vParam; break; case SQL_NOSCAN: /* ignored */ mylog("SetStmtOption: SQL_NOSCAN, vParam = %d\n", vParam); break; case SQL_QUERY_TIMEOUT: /* ignored */ mylog("SetStmtOption: SQL_QUERY_TIMEOUT, vParam = %d\n", vParam); /*// "0" returned in SQLGetStmtOption */ break; case SQL_RETRIEVE_DATA: /* ignored, but saved */ mylog("SetStmtOption(): SQL_RETRIEVE_DATA, vParam = %d\n", vParam); if (conn) conn->stmtOptions.retrieve_data = vParam; if (stmt) stmt->options.retrieve_data = vParam; break; case SQL_ROWSET_SIZE: mylog("SetStmtOption(): SQL_ROWSET_SIZE, vParam = %d\n", vParam); /* Save old rowset size for SQLExtendedFetch purposes If the rowset_size is being changed since the last call to fetch rows. */ if (stmt && stmt->save_rowset_size <= 0 && stmt->last_fetch_count > 0 ) stmt->save_rowset_size = stmt->options.rowset_size; if (vParam < 1) { vParam = 1; changed = TRUE; } if (conn) conn->stmtOptions.rowset_size = vParam; if (stmt) stmt->options.rowset_size = vParam; break; case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */ if (stmt) { stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library."; SC_log_error(func, "", stmt); } if (conn) { conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR; conn->errormsg = "Simulated positioned update/delete not supported. Use the cursor library."; CC_log_error(func, "", conn); } return SQL_ERROR; case SQL_USE_BOOKMARKS: if (stmt) stmt->options.use_bookmarks = vParam; if (conn) conn->stmtOptions.use_bookmarks = vParam; break; /* * added to be nice to OpenOffice */ case /*SQL_ATTR_CURSOR_SENSITIVITY*/ 65534: break; default: { char option[64]; if (stmt) { stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "Unknown statement option (Set)"; sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam); SC_log_error(func, option, stmt); } if (conn) { conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR; conn->errormsg = "Unknown statement option (Set)"; sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam); CC_log_error(func, option, conn); } return SQL_ERROR; } } if (changed) { if (stmt) { stmt->errormsg = "Requested value changed."; stmt->errornumber = STMT_OPTION_VALUE_CHANGED; } if (conn) { conn->errormsg = "Requested value changed."; conn->errornumber = STMT_OPTION_VALUE_CHANGED; } return SQL_SUCCESS_WITH_INFO; } else 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; }
/* Associate a user-supplied buffer with a database column. */ RETCODE SQL_API PG_SQLBindCol( HSTMT hstmt, UWORD icol, SWORD fCType, PTR rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue) { StatementClass *stmt = (StatementClass *) hstmt; static char* const func="SQLBindCol"; mylog( "%s: entering...\n", func); mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol); if ( ! stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } SC_clear_error(stmt); if( stmt->status == STMT_EXECUTING) { SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't bind columns while statement is still executing."); SC_log_error(func, "", stmt); return SQL_ERROR; } /* If the bookmark column is being bound, then just save it */ if (icol == 0) { if (rgbValue == NULL) { stmt->bookmark.buffer = NULL; stmt->bookmark.used = NULL; } else { /* Make sure it is the bookmark data type */ if ( fCType != SQL_C_BOOKMARK && fCType != SQL_C_BINARY ) { SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE, "Column 0 is not of type SQL_C_BOOKMARK"); SC_log_error(func, "", stmt); return SQL_ERROR; } stmt->bookmark.buffer = rgbValue; stmt->bookmark.used = pcbValue; } return SQL_SUCCESS; } /* allocate enough bindings if not already done */ /* Most likely, execution of a statement would have setup the */ /* necessary bindings. But some apps call BindCol before any */ /* statement is executed. */ if ( icol > stmt->bindings_allocated) extend_bindings(stmt, icol); /* check to see if the bindings were allocated */ if ( ! stmt->bindings) { SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Could not allocate memory for bindings."); SC_log_error(func, "", stmt); return SQL_ERROR; } icol--; /* use zero based col numbers from here out */ /* Reset for SQLGetData */ stmt->bindings[icol].data_left = -1; if (rgbValue == NULL) { /* we have to unbind the column */ stmt->bindings[icol].buflen = 0; stmt->bindings[icol].buffer = NULL; stmt->bindings[icol].used = NULL; stmt->bindings[icol].returntype = SQL_C_CHAR; } else { /* ok, bind that column */ stmt->bindings[icol].buflen = cbValueMax; stmt->bindings[icol].buffer = rgbValue; stmt->bindings[icol].used = pcbValue; stmt->bindings[icol].returntype = fCType; mylog(" bound buffer[%d] = %u\n", icol, stmt->bindings[icol].buffer); } return SQL_SUCCESS; }