/*------- * 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. *------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bind.h" #include "environ.h" #include "statement.h" #include "qresult.h" #include "pgtypes.h" #include #include #ifndef WIN32 #include "iodbc.h" #include "isql.h" #include "isqlext.h" #else #include "sql.h" #include "sqlext.h" #endif #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...\n", func); 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; } 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"); }