diff options
Diffstat (limited to 'src/interfaces/odbc/windev/bind.c')
-rw-r--r-- | src/interfaces/odbc/windev/bind.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/src/interfaces/odbc/windev/bind.c b/src/interfaces/odbc/windev/bind.c new file mode 100644 index 00000000000..e9f5cc34827 --- /dev/null +++ b/src/interfaces/odbc/windev/bind.c @@ -0,0 +1,487 @@ +/*------- + * Module: bind.c + * + * Description: This module contains routines related to binding + * columns and parameters. + * + * Classes: BindInfoClass, ParameterInfoClass + * + * API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams, + * SQLParamOptions(NI) + * + * Comments: See "notice.txt" for copyright and license information. + *------- + */ + +#include "bind.h" + +#include "environ.h" +#include "statement.h" +#include "qresult.h" +#include "pgtypes.h" +#include <stdlib.h> +#include <string.h> + +#include "pgapifunc.h" + + +/* Bind parameters on a statement handle */ +RETCODE SQL_API +PGAPI_BindParameter( + HSTMT hstmt, + UWORD ipar, + SWORD fParamType, + SWORD fCType, + SWORD fSqlType, + UDWORD cbColDef, + SWORD ibScale, + PTR rgbValue, + SDWORD cbValueMax, + SDWORD FAR * pcbValue) +{ + StatementClass *stmt = (StatementClass *) hstmt; + static char *func = "PGAPI_BindParameter"; + + mylog("%s: entering...\n", func); + + if (!stmt) + { + SC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } + SC_clear_error(stmt); + + 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) + { + stmt->errornumber = STMT_NO_MEMORY_ERROR; + stmt->errormsg = "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; + } + } + + /* use zero based column numbers for the below part */ + ipar--; + + /* 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 (pcbValue && (*pcbValue == SQL_DATA_AT_EXEC || + *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)) + stmt->parameters[ipar].data_at_exec = TRUE; + else + stmt->parameters[ipar].data_at_exec = FALSE; + + /* Clear premature result */ + if (stmt->status == STMT_PREMATURE) + SC_recycle_statement(stmt); + + mylog("PGAPI_BindParamater: 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 +PGAPI_BindCol( + HSTMT hstmt, + UWORD icol, + SWORD fCType, + PTR rgbValue, + SDWORD cbValueMax, + SDWORD FAR * pcbValue) +{ + StatementClass *stmt = (StatementClass *) hstmt; + static char *func = "PGAPI_BindCol"; + + mylog("%s: entering...\n", func); + + mylog("**** PGAPI_BindCol: stmt = %u, icol = %d\n", stmt, icol); + mylog("**** : fCType=%d rgb=%x valusMax=%d pcb=%x\n", fCType, rgbValue, cbValueMax, pcbValue); + + if (!stmt) + { + SC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } + + + SC_clear_error(stmt); + + if (stmt->status == STMT_EXECUTING) + { + stmt->errormsg = "Can't bind columns while statement is still executing."; + stmt->errornumber = STMT_SEQUENCE_ERROR; + 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) + { + stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK"; + stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE; + 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) + { + stmt->errormsg = "Could not allocate memory for bindings."; + stmt->errornumber = STMT_NO_MEMORY_ERROR; + SC_log_error(func, "", stmt); + return SQL_ERROR; + } + + /* use zero based col numbers from here out */ + icol--; + + /* 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; +} + + +/* + * 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, + UWORD ipar, + SWORD FAR * pfSqlType, + UDWORD FAR * pcbColDef, + SWORD FAR * pibScale, + SWORD FAR * pfNullable) +{ + StatementClass *stmt = (StatementClass *) hstmt; + static char *func = "PGAPI_DescribeParam"; + + mylog("%s: entering...\n", func); + + if (!stmt) + { + SC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } + SC_clear_error(stmt); + + if ((ipar < 1) || (ipar > stmt->parameters_allocated)) + { + stmt->errormsg = "Invalid parameter number for PGAPI_DescribeParam."; + stmt->errornumber = STMT_BAD_PARAMETER_NUMBER_ERROR; + 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; +} + + +/* Sets multiple values (arrays) for the set of parameter markers. */ +RETCODE SQL_API +PGAPI_ParamOptions( + HSTMT hstmt, + UDWORD crow, + UDWORD FAR * pirow) +{ + static char *func = "PGAPI_ParamOptions"; + StatementClass *stmt = (StatementClass *) hstmt; + + mylog("%s: entering... %d %x\n", func, crow, pirow); + + if (crow == 1) /* temporary solution and must be + * rewritten later */ + { + if (pirow) + *pirow = 1; + return SQL_SUCCESS; + } + stmt->errornumber = CONN_UNSUPPORTED_OPTION; + stmt->errormsg = "Function not implemented"; + 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, + SWORD FAR * pcpar) +{ + StatementClass *stmt = (StatementClass *) hstmt; + char in_quote = FALSE; + unsigned int i; + static char *func = "PGAPI_NumParams"; + + mylog("%s: entering...\n", func); + + if (!stmt) + { + SC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } + SC_clear_error(stmt); + + if (pcpar) + *pcpar = 0; + else + { + SC_log_error(func, "pcpar was null", stmt); + return SQL_ERROR; + } + + + if (!stmt->statement) + { + /* no statement has been allocated */ + stmt->errormsg = "PGAPI_NumParams called with no statement ready."; + stmt->errornumber = STMT_SEQUENCE_ERROR; + SC_log_error(func, "", stmt); + return SQL_ERROR; + } + else + { + for (i = 0; i < strlen(stmt->statement); i++) + { + if (stmt->statement[i] == '?' && !in_quote) + (*pcpar)++; + else + { + if (stmt->statement[i] == '\'') + in_quote = (in_quote ? FALSE : TRUE); + } + } + return SQL_SUCCESS; + } +} + + +/* + * Bindings Implementation + */ +BindInfoClass * +create_empty_bindings(int num_columns) +{ + BindInfoClass *new_bindings; + int i; + + new_bindings = (BindInfoClass *) malloc(num_columns * sizeof(BindInfoClass)); + if (!new_bindings) + return 0; + + for (i = 0; i < num_columns; i++) + { + new_bindings[i].buflen = 0; + new_bindings[i].buffer = NULL; + new_bindings[i].used = NULL; + new_bindings[i].data_left = -1; + new_bindings[i].ttlbuf = NULL; + new_bindings[i].ttlbuflen = 0; + } + + return new_bindings; +} + + +void +extend_bindings(StatementClass *stmt, int num_columns) +{ + static char *func = "extend_bindings"; + BindInfoClass *new_bindings; + int i; + + mylog("%s: entering ... stmt=%u, bindings_allocated=%d, num_columns=%d\n", func, stmt, stmt->bindings_allocated, num_columns); + + /* + * if we have too few, allocate room for more, and copy the old + * entries into the new structure + */ + if (stmt->bindings_allocated < num_columns) + { + new_bindings = create_empty_bindings(num_columns); + if (!new_bindings) + { + mylog("%s: unable to create %d new bindings from %d old bindings\n", func, num_columns, stmt->bindings_allocated); + + if (stmt->bindings) + { + free(stmt->bindings); + stmt->bindings = NULL; + } + stmt->bindings_allocated = 0; + return; + } + + if (stmt->bindings) + { + for (i = 0; i < stmt->bindings_allocated; i++) + new_bindings[i] = stmt->bindings[i]; + + free(stmt->bindings); + } + + stmt->bindings = new_bindings; + stmt->bindings_allocated = num_columns; + } + + /* + * There is no reason to zero out extra bindings if there are more + * than needed. If an app has allocated extra bindings, let it worry + * about it by unbinding those columns. + */ + + /* SQLBindCol(1..) ... SQLBindCol(10...) # got 10 bindings */ + /* SQLExecDirect(...) # returns 5 cols */ + /* SQLExecDirect(...) # returns 10 cols (now OK) */ + + mylog("exit extend_bindings\n"); +} |