/*------- * Module: bind.c * * Description: This module contains routines related to binding * columns and parameters. * * Classes: BindInfoClass, ParameterInfoClass * * API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams, * SQLParamOptions * * Comments: See "notice.txt" for copyright and license information. *------- */ #include "bind.h" #include "environ.h" #include "statement.h" #include "descriptor.h" #include "qresult.h" #include "pgtypes.h" #include #include #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"; APDFields *opts; mylog("%s: entering...\n", func); if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } SC_clear_error(stmt); opts = SC_get_APD(stmt); if (opts->allocated < ipar) extend_parameter_bindings(opts, ipar); /* use zero based column numbers for the below part */ ipar--; /* store the given info */ opts->parameters[ipar].buflen = cbValueMax; opts->parameters[ipar].buffer = rgbValue; opts->parameters[ipar].used = pcbValue; opts->parameters[ipar].paramType = fParamType; opts->parameters[ipar].CType = fCType; opts->parameters[ipar].SQLType = fSqlType; opts->parameters[ipar].column_size = cbColDef; opts->parameters[ipar].decimal_digits = ibScale; opts->parameters[ipar].precision = 0; opts->parameters[ipar].scale = 0; #if (ODBCVER >= 0x0300) switch (fCType) { case SQL_C_NUMERIC: if (cbColDef > 0) opts->parameters[ipar].precision = (UInt2) cbColDef; if (ibScale > 0) opts->parameters[ipar].scale = ibScale; break; case SQL_C_TYPE_TIMESTAMP: if (ibScale > 0) opts->parameters[ipar].precision = ibScale; break; } #endif /* ODBCVER */ /* * If rebinding a parameter that had data-at-exec stuff in it, then * free that stuff */ if (opts->parameters[ipar].EXEC_used) { free(opts->parameters[ipar].EXEC_used); opts->parameters[ipar].EXEC_used = NULL; } if (opts->parameters[ipar].EXEC_buffer) { if (opts->parameters[ipar].SQLType != SQL_LONGVARBINARY) free(opts->parameters[ipar].EXEC_buffer); opts->parameters[ipar].EXEC_buffer = NULL; } if (pcbValue && opts->param_offset_ptr) pcbValue += (*opts->param_offset_ptr >> 2); /* 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)) opts->parameters[ipar].data_at_exec = TRUE; else opts->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, opts->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"; ARDFields *opts; 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; } opts = SC_get_ARD(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; } SC_clear_error(stmt); /* If the bookmark column is being bound, then just save it */ if (icol == 0) { if (rgbValue == NULL) { opts->bookmark->buffer = NULL; opts->bookmark->used = NULL; } else { /* Make sure it is the bookmark data type */ if (fCType == SQL_C_BOOKMARK) switch (fCType) { case SQL_C_BOOKMARK: #if (ODBCVER >= 0x0300) case SQL_C_VARBOOKMARK: #endif /* ODBCVER */ break; default: stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK"; inolog("Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE; SC_log_error(func, "", stmt); return SQL_ERROR; } opts->bookmark->buffer = rgbValue; opts->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 > opts->allocated) extend_column_bindings(opts, icol); /* check to see if the bindings were allocated */ if (!opts->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 */ opts->bindings[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 = NULL; opts->bindings[icol].returntype = SQL_C_CHAR; if (opts->bindings[icol].ttlbuf) free(opts->bindings[icol].ttlbuf); opts->bindings[icol].ttlbuf = NULL; opts->bindings[icol].ttlbuflen = 0; opts->bindings[icol].precision = 0; opts->bindings[icol].scale = 0; } else { /* ok, bind that column */ opts->bindings[icol].buflen = cbValueMax; opts->bindings[icol].buffer = rgbValue; opts->bindings[icol].used = pcbValue; opts->bindings[icol].returntype = fCType; #if (ODBCVER >= 0x0300) if (SQL_C_NUMERIC == fCType) opts->bindings[icol].precision = 32; else #endif /* ODBCVER */ opts->bindings[icol].precision = 0; opts->bindings[icol].scale = 0; mylog(" bound buffer[%d] = %u\n", icol, opts->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"; APDFields *opts; mylog("%s: entering...\n", func); if (!stmt) { SC_log_error(func, "", NULL); return SQL_INVALID_HANDLE; } SC_clear_error(stmt); opts = SC_get_APD(stmt); if ((ipar < 1) || (ipar > opts->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 = opts->parameters[ipar].SQLType; if (pcbColDef) *pcbColDef = opts->parameters[ipar].column_size; if (pibScale) *pibScale = opts->parameters[ipar].decimal_digits; if (pfNullable) *pfNullable = pgtype_nullable(stmt, opts->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; APDFields *opts; mylog("%s: entering... %d %x\n", func, crow, pirow); opts = SC_get_APD(stmt); opts->paramset_size = crow; SC_get_IPD(stmt)->param_processed_ptr = (UInt4 *) pirow; return SQL_SUCCESS; } /* * 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_parameter_bindings(APDFields *self, int num_params) { static char *func = "extend_parameter_bindings"; ParameterInfoClass *new_bindings; mylog("%s: entering ... self=%u, parameters_allocated=%d, num_params=%d\n", func, self, self->allocated, num_params); /* * if we have too few, allocate room for more, and copy the old * entries into the new structure */ if (self->allocated < num_params) { new_bindings = (ParameterInfoClass *) realloc(self->parameters, sizeof(ParameterInfoClass) * num_params); if (!new_bindings) { mylog("%s: unable to create %d new bindings from %d old bindings\n", func, num_params, self->allocated); self->parameters = NULL; self->allocated = 0; return; } memset(&new_bindings[self->allocated], 0, sizeof(ParameterInfoClass) * (num_params - self->allocated)); self->parameters = new_bindings; self->allocated = num_params; } mylog("exit extend_parameter_bindings\n"); } void reset_a_parameter_binding(APDFields *self, int ipar) { static char *func = "reset_a_parameter_binding"; mylog("%s: entering ... self=%u, parameters_allocated=%d, ipar=%d\n", func, self, self->allocated, ipar); if (ipar < 1 || ipar > self->allocated) return; ipar--; self->parameters[ipar].buflen = 0; self->parameters[ipar].buffer = 0; self->parameters[ipar].used = 0; self->parameters[ipar].paramType = 0; self->parameters[ipar].CType = 0; if (self->parameters[ipar].EXEC_used) { free(self->parameters[ipar].EXEC_used); self->parameters[ipar].EXEC_used = NULL; } if (self->parameters[ipar].EXEC_buffer) { if (self->parameters[ipar].SQLType != SQL_LONGVARBINARY) free(self->parameters[ipar].EXEC_buffer); self->parameters[ipar].EXEC_buffer = NULL; } self->parameters[ipar].SQLType = 0; self->parameters[ipar].column_size = 0; self->parameters[ipar].decimal_digits = 0; self->parameters[ipar].precision = 0; self->parameters[ipar].scale = 0; self->parameters[ipar].data_at_exec = FALSE; self->parameters[ipar].lobj_oid = 0; } /* * Free parameters and free the memory. */ void APD_free_params(APDFields *self, char option) { int i; mylog("APD_free_params: ENTER, self=%d\n", self); if (!self->parameters) return; for (i = 0; i < self->allocated; i++) { if (self->parameters[i].data_at_exec) { if (self->parameters[i].EXEC_used) { free(self->parameters[i].EXEC_used); self->parameters[i].EXEC_used = NULL; } if (self->parameters[i].EXEC_buffer) { if (self->parameters[i].SQLType != SQL_LONGVARBINARY) free(self->parameters[i].EXEC_buffer); self->parameters[i].EXEC_buffer = NULL; } } } if (option == STMT_FREE_PARAMS_ALL) { if (self->parameters); free(self->parameters); self->parameters = NULL; self->allocated = 0; } mylog("APD_free_params: EXIT\n"); } void extend_column_bindings(ARDFields *self, int num_columns) { static char *func = "extend_column_bindings"; BindInfoClass *new_bindings; int i; mylog("%s: entering ... self=%u, bindings_allocated=%d, num_columns=%d\n", func, self, self->allocated, num_columns); /* * if we have too few, allocate room for more, and copy the old * entries into the new structure */ if (self->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, self->allocated); if (self->bindings) { free(self->bindings); self->bindings = NULL; } self->allocated = 0; return; } if (self->bindings) { for (i = 0; i < self->allocated; i++) new_bindings[i] = self->bindings[i]; free(self->bindings); } self->bindings = new_bindings; self->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_column_bindings\n"); } void reset_a_column_binding(ARDFields *self, int icol) { static char *func = "reset_a_column_binding"; mylog("%s: entering ... self=%u, bindings_allocated=%d, icol=%d\n", func, self, self->allocated, icol); if (icol > self->allocated) return; /* use zero based col numbers from here out */ if (0 == icol) { self->bookmark->buffer = NULL; self->bookmark->used = NULL; } else { icol--; /* we have to unbind the column */ self->bindings[icol].buflen = 0; self->bindings[icol].buffer = NULL; self->bindings[icol].used = NULL; self->bindings[icol].data_left = -1; self->bindings[icol].returntype = SQL_C_CHAR; if (self->bindings[icol].ttlbuf) free(self->bindings[icol].ttlbuf); self->bindings[icol].ttlbuf = NULL; self->bindings[icol].ttlbuflen = 0; } } void ARD_unbind_cols(ARDFields *self, BOOL freeall) { Int2 lf; for (lf = 1; lf <= self->allocated; lf++) reset_a_column_binding(self, lf); if (freeall) { if (self->bindings) free(self->bindings); self->bindings = NULL; self->allocated = 0; } }