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; } }
/* 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: case SQL_C_VARBOOKMARK: 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; 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; } 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; }
/* 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; }
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; }
/* * 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"; APDFields *apdopts; IPDFields *ipdopts; RETCODE ret = SQL_SUCCESS; int num_params; mylog("%s: entering...%d\n", func, ipar); if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } SC_clear_error(stmt); apdopts = SC_get_APDF(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--; /* * 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, ipdopts->parameters[ipar].PGType); if (ipdopts->parameters[ipar].SQLType) *pfSqlType = ipdopts->parameters[ipar].SQLType; else if (ipdopts->parameters[ipar].PGType) *pfSqlType = pgtype_to_concise_type(stmt, ipdopts->parameters[ipar].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 && ipdopts->parameters[ipar].PGType) *pcbParamDef = pgtype_column_size(stmt, ipdopts->parameters[ipar].PGType, PG_STATIC, PG_STATIC); } if (pibScale) { *pibScale = 0; if (ipdopts->parameters[ipar].SQLType) *pibScale = ipdopts->parameters[ipar].decimal_digits; else if (ipdopts->parameters[ipar].PGType) *pibScale = pgtype_scale(stmt, ipdopts->parameters[ipar].PGType, -1); } if (pfNullable) *pfNullable = pgtype_nullable(stmt, ipdopts->parameters[ipar].paramType); cleanup: #undef return if (stmt->internal) ret = DiscardStatementSvp(stmt, ret, FALSE); return ret; }
SQLRETURN SQL_API SQLColAttributeW( SQLHSTMT hstmt, SQLUSMALLINT iCol, SQLUSMALLINT iField, SQLPOINTER pCharAttr, SQLSMALLINT cbCharAttrMax, SQLSMALLINT *pcbCharAttr, #if defined(WITH_UNIXODBC) || (defined(WIN32) && ! defined(_WIN64)) SQLPOINTER pNumAttr #else SQLLEN *pNumAttr #endif ) { CSTR func = "SQLColAttributeW"; RETCODE ret; StatementClass *stmt = (StatementClass *) hstmt; SQLSMALLINT *rgbL, blen = 0, bMax; char *rgbD = NULL; mylog("[%s]", func); ENTER_STMT_CS(stmt); SC_clear_error(stmt); StartRollbackState(stmt); switch (iField) { case SQL_DESC_BASE_COLUMN_NAME: case SQL_DESC_BASE_TABLE_NAME: case SQL_DESC_CATALOG_NAME: case SQL_DESC_LABEL: case SQL_DESC_LITERAL_PREFIX: case SQL_DESC_LITERAL_SUFFIX: case SQL_DESC_LOCAL_TYPE_NAME: case SQL_DESC_NAME: case SQL_DESC_SCHEMA_NAME: case SQL_DESC_TABLE_NAME: case SQL_DESC_TYPE_NAME: case SQL_COLUMN_NAME: bMax = cbCharAttrMax * 3 / WCLEN; rgbD = malloc(bMax); rgbL = &blen; for (;; bMax = blen + 1, rgbD = realloc(rgbD, bMax)) { ret = PGAPI_ColAttributes(hstmt, iCol, iField, rgbD, bMax, rgbL, pNumAttr); if (SQL_SUCCESS_WITH_INFO != ret || blen < bMax) break; } if (SQL_SUCCEEDED(ret)) { blen = (SQLSMALLINT) utf8_to_ucs2(rgbD, blen, (SQLWCHAR *) pCharAttr, cbCharAttrMax / WCLEN); if (SQL_SUCCESS == ret && blen * WCLEN >= cbCharAttrMax) { ret = SQL_SUCCESS_WITH_INFO; SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the pCharAttr.", func); } if (pcbCharAttr) *pcbCharAttr = blen * WCLEN; } if (rgbD) free(rgbD); break; default: rgbD = pCharAttr; bMax = cbCharAttrMax; rgbL = pcbCharAttr; ret = PGAPI_ColAttributes(hstmt, iCol, iField, rgbD, bMax, rgbL, pNumAttr); break; } ret = DiscardStatementSvp(stmt, ret, FALSE); LEAVE_STMT_CS(stmt); return ret; }
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; }
/* 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; }
/* 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; BOOL prepared; 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 */ prepared = self->prepared; 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"); if (NOT_YET_PREPARED != prepared) SC_recycle_statement(self); /* recycle the statement */ 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; }