|
| 1 | + |
| 2 | +/* Module: bind.c |
| 3 | + * |
| 4 | + * Description: This module contains routines related to binding |
| 5 | + * columns and parameters. |
| 6 | + * |
| 7 | + * Classes: BindInfoClass, ParameterInfoClass |
| 8 | + * |
| 9 | + * API functions: SQLBindParameter, SQLBindCol, SQLDescribeParam, SQLNumParams, |
| 10 | + * SQLParamOptions(NI) |
| 11 | + * |
| 12 | + * Comments: See "notice.txt" for copyright and license information. |
| 13 | + * |
| 14 | + */ |
| 15 | +#include "bind.h" |
| 16 | +#include "environ.h" |
| 17 | +#include "statement.h" |
| 18 | +#include "qresult.h" |
| 19 | +#include "pgtypes.h" |
| 20 | +#include <stdlib.h> |
| 21 | +#include <malloc.h> |
| 22 | +#include <sql.h> |
| 23 | +#include <sqlext.h> |
| 24 | + |
| 25 | +// Bind parameters on a statement handle |
| 26 | + |
| 27 | +RETCODE SQL_API SQLBindParameter( |
| 28 | + HSTMT hstmt, |
| 29 | + UWORD ipar, |
| 30 | + SWORD fParamType, |
| 31 | + SWORD fCType, |
| 32 | + SWORD fSqlType, |
| 33 | + UDWORD cbColDef, |
| 34 | + SWORD ibScale, |
| 35 | + PTR rgbValue, |
| 36 | + SDWORD cbValueMax, |
| 37 | + SDWORD FAR *pcbValue) |
| 38 | +{ |
| 39 | +StatementClass *stmt = (StatementClass *) hstmt; |
| 40 | + |
| 41 | + if( ! stmt) |
| 42 | + return SQL_INVALID_HANDLE; |
| 43 | + |
| 44 | + if(stmt->parameters_allocated < ipar) { |
| 45 | + ParameterInfoClass *old_parameters; |
| 46 | + int i, old_parameters_allocated; |
| 47 | + |
| 48 | + old_parameters = stmt->parameters; |
| 49 | + old_parameters_allocated = stmt->parameters_allocated; |
| 50 | + |
| 51 | + stmt->parameters = (ParameterInfoClass *) malloc(sizeof(ParameterInfoClass)*(ipar)); |
| 52 | + if ( ! stmt->parameters) { |
| 53 | + stmt->errornumber = STMT_NO_MEMORY_ERROR; |
| 54 | + stmt->errormsg = "Could not allocate memory for statement parameters"; |
| 55 | + return SQL_ERROR; |
| 56 | + } |
| 57 | + |
| 58 | + stmt->parameters_allocated = ipar; |
| 59 | + |
| 60 | + // copy the old parameters over |
| 61 | + for(i = 0; i < old_parameters_allocated; i++) { |
| 62 | + // a structure copy should work |
| 63 | + stmt->parameters[i] = old_parameters[i]; |
| 64 | + } |
| 65 | + |
| 66 | + // get rid of the old parameters, if there were any |
| 67 | + if(old_parameters) |
| 68 | + free(old_parameters); |
| 69 | + |
| 70 | + // zero out the newly allocated parameters (in case they skipped some, |
| 71 | + // so we don't accidentally try to use them later) |
| 72 | + for(; i < stmt->parameters_allocated; i++) { |
| 73 | + stmt->parameters[i].buflen = 0; |
| 74 | + stmt->parameters[i].buffer = 0; |
| 75 | + stmt->parameters[i].used = 0; |
| 76 | + stmt->parameters[i].paramType = 0; |
| 77 | + stmt->parameters[i].CType = 0; |
| 78 | + stmt->parameters[i].SQLType = 0; |
| 79 | + stmt->parameters[i].precision = 0; |
| 80 | + stmt->parameters[i].scale = 0; |
| 81 | + stmt->parameters[i].data_at_exec = FALSE; |
| 82 | + stmt->parameters[i].EXEC_used = NULL; |
| 83 | + stmt->parameters[i].EXEC_buffer = NULL; |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + ipar--; /* use zero based column numbers for the below part */ |
| 88 | + |
| 89 | + // store the given info |
| 90 | + stmt->parameters[ipar].buflen = cbValueMax; |
| 91 | + stmt->parameters[ipar].buffer = rgbValue; |
| 92 | + stmt->parameters[ipar].used = pcbValue; |
| 93 | + stmt->parameters[ipar].paramType = fParamType; |
| 94 | + stmt->parameters[ipar].CType = fCType; |
| 95 | + stmt->parameters[ipar].SQLType = fSqlType; |
| 96 | + stmt->parameters[ipar].precision = cbColDef; |
| 97 | + stmt->parameters[ipar].scale = ibScale; |
| 98 | + |
| 99 | + /* If rebinding a parameter that had data-at-exec stuff in it, |
| 100 | + then free that stuff |
| 101 | + */ |
| 102 | + if (stmt->parameters[ipar].EXEC_used) { |
| 103 | + free(stmt->parameters[ipar].EXEC_used); |
| 104 | + stmt->parameters[ipar].EXEC_used = NULL; |
| 105 | + } |
| 106 | + |
| 107 | + if (stmt->parameters[ipar].EXEC_buffer) { |
| 108 | + free(stmt->parameters[ipar].EXEC_buffer); |
| 109 | + stmt->parameters[ipar].EXEC_buffer = NULL; |
| 110 | + } |
| 111 | + |
| 112 | + if (pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET) |
| 113 | + stmt->parameters[ipar].data_at_exec = TRUE; |
| 114 | + else |
| 115 | + stmt->parameters[ipar].data_at_exec = FALSE; |
| 116 | + |
| 117 | + |
| 118 | + return SQL_SUCCESS; |
| 119 | +} |
| 120 | + |
| 121 | +// - - - - - - - - - |
| 122 | + |
| 123 | +// Associate a user-supplied buffer with a database column. |
| 124 | +RETCODE SQL_API SQLBindCol( |
| 125 | + HSTMT hstmt, |
| 126 | + UWORD icol, |
| 127 | + SWORD fCType, |
| 128 | + PTR rgbValue, |
| 129 | + SDWORD cbValueMax, |
| 130 | + SDWORD FAR *pcbValue) |
| 131 | +{ |
| 132 | +StatementClass *stmt = (StatementClass *) hstmt; |
| 133 | +Int2 numcols; |
| 134 | + |
| 135 | +mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol); |
| 136 | + |
| 137 | + if ( ! stmt) |
| 138 | + return SQL_INVALID_HANDLE; |
| 139 | + |
| 140 | + if (icol < 1) { |
| 141 | + /* currently we do not support bookmarks */ |
| 142 | + stmt->errormsg = "Bookmarks are not currently supported."; |
| 143 | + stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; |
| 144 | + return SQL_ERROR; |
| 145 | + } |
| 146 | + |
| 147 | + icol--; /* use zero based col numbers */ |
| 148 | + |
| 149 | + SC_clear_error(stmt); |
| 150 | + |
| 151 | + if( ! stmt->result) { |
| 152 | + stmt->errormsg = "Can't bind columns with a NULL query result structure."; |
| 153 | + stmt->errornumber = STMT_SEQUENCE_ERROR; |
| 154 | + return SQL_ERROR; |
| 155 | + } |
| 156 | + |
| 157 | + if( stmt->status == STMT_EXECUTING) { |
| 158 | + stmt->errormsg = "Can't bind columns while statement is still executing."; |
| 159 | + stmt->errornumber = STMT_SEQUENCE_ERROR; |
| 160 | + return SQL_ERROR; |
| 161 | + } |
| 162 | + |
| 163 | + numcols = QR_NumResultCols(stmt->result); |
| 164 | + |
| 165 | + mylog("SQLBindCol: numcols = %d\n", numcols); |
| 166 | + |
| 167 | + if (icol >= numcols) { |
| 168 | + stmt->errornumber = STMT_COLNUM_ERROR; |
| 169 | + stmt->errormsg = "Column number too big"; |
| 170 | + return SQL_ERROR; |
| 171 | + } |
| 172 | + |
| 173 | + if ( ! stmt->bindings) { |
| 174 | + stmt->errormsg = "Bindings were not allocated properly."; |
| 175 | + stmt->errornumber = STMT_SEQUENCE_ERROR; |
| 176 | + return SQL_ERROR; |
| 177 | + } |
| 178 | + |
| 179 | + if ((cbValueMax == 0) || (rgbValue == NULL)) { |
| 180 | + /* we have to unbind the column */ |
| 181 | + stmt->bindings[icol].buflen = 0; |
| 182 | + stmt->bindings[icol].buffer = NULL; |
| 183 | + stmt->bindings[icol].used = NULL; |
| 184 | + stmt->bindings[icol].returntype = SQL_C_CHAR; |
| 185 | + } else { |
| 186 | + /* ok, bind that column */ |
| 187 | + stmt->bindings[icol].buflen = cbValueMax; |
| 188 | + stmt->bindings[icol].buffer = rgbValue; |
| 189 | + stmt->bindings[icol].used = pcbValue; |
| 190 | + stmt->bindings[icol].returntype = fCType; |
| 191 | + } |
| 192 | + |
| 193 | + return SQL_SUCCESS; |
| 194 | +} |
| 195 | + |
| 196 | +// - - - - - - - - - |
| 197 | + |
| 198 | +// Returns the description of a parameter marker. |
| 199 | + |
| 200 | +RETCODE SQL_API SQLDescribeParam( |
| 201 | + HSTMT hstmt, |
| 202 | + UWORD ipar, |
| 203 | + SWORD FAR *pfSqlType, |
| 204 | + UDWORD FAR *pcbColDef, |
| 205 | + SWORD FAR *pibScale, |
| 206 | + SWORD FAR *pfNullable) |
| 207 | +{ |
| 208 | +StatementClass *stmt = (StatementClass *) hstmt; |
| 209 | + |
| 210 | + if( ! stmt) |
| 211 | + return SQL_INVALID_HANDLE; |
| 212 | + |
| 213 | + if( (ipar < 1) || (ipar > stmt->parameters_allocated) ) { |
| 214 | + stmt->errormsg = "Invalid parameter number for SQLDescribeParam."; |
| 215 | + stmt->errornumber = STMT_BAD_PARAMETER_NUMBER_ERROR; |
| 216 | + return SQL_ERROR; |
| 217 | + } |
| 218 | + |
| 219 | + ipar--; |
| 220 | + |
| 221 | + if(pfSqlType) |
| 222 | + *pfSqlType = stmt->parameters[ipar].SQLType; |
| 223 | + |
| 224 | + if(pcbColDef) |
| 225 | + *pcbColDef = stmt->parameters[ipar].precision; |
| 226 | + |
| 227 | + if(pibScale) |
| 228 | + *pibScale = stmt->parameters[ipar].scale; |
| 229 | + |
| 230 | + if(pfNullable) |
| 231 | + *pfNullable = pgtype_nullable(stmt->parameters[ipar].paramType); |
| 232 | + |
| 233 | + return SQL_SUCCESS; |
| 234 | +} |
| 235 | + |
| 236 | +// - - - - - - - - - |
| 237 | + |
| 238 | +// Sets multiple values (arrays) for the set of parameter markers. |
| 239 | + |
| 240 | +RETCODE SQL_API SQLParamOptions( |
| 241 | + HSTMT hstmt, |
| 242 | + UDWORD crow, |
| 243 | + UDWORD FAR *pirow) |
| 244 | +{ |
| 245 | + return SQL_ERROR; |
| 246 | +} |
| 247 | + |
| 248 | +// - - - - - - - - - |
| 249 | + |
| 250 | +// Returns the number of parameter markers. |
| 251 | + |
| 252 | +RETCODE SQL_API SQLNumParams( |
| 253 | + HSTMT hstmt, |
| 254 | + SWORD FAR *pcpar) |
| 255 | +{ |
| 256 | +StatementClass *stmt = (StatementClass *) hstmt; |
| 257 | +unsigned int i; |
| 258 | + |
| 259 | + // I guess this is the number of actual parameter markers |
| 260 | + // in the statement, not the number of parameters that are bound. |
| 261 | + // why does this have to be driver-specific? |
| 262 | + |
| 263 | + if(!stmt) |
| 264 | + return SQL_INVALID_HANDLE; |
| 265 | + |
| 266 | + if(!stmt->statement) { |
| 267 | + // no statement has been allocated |
| 268 | + *pcpar = 0; |
| 269 | + stmt->errormsg = "SQLNumParams called with no statement ready."; |
| 270 | + stmt->errornumber = STMT_SEQUENCE_ERROR; |
| 271 | + return SQL_ERROR; |
| 272 | + } else { |
| 273 | + *pcpar = 0; |
| 274 | + for(i=0; i < strlen(stmt->statement); i++) { |
| 275 | + if(stmt->statement[i] == '?') |
| 276 | + (*pcpar)++; |
| 277 | + } |
| 278 | + |
| 279 | + return SQL_SUCCESS; |
| 280 | + } |
| 281 | +} |
| 282 | + |
| 283 | +/******************************************************************** |
| 284 | + * Bindings Implementation |
| 285 | + */ |
| 286 | +BindInfoClass * |
| 287 | +create_empty_bindings(int num_columns) |
| 288 | +{ |
| 289 | +BindInfoClass *new_bindings; |
| 290 | +int i; |
| 291 | + |
| 292 | + new_bindings = (BindInfoClass *)malloc(num_columns * sizeof(BindInfoClass)); |
| 293 | + if(!new_bindings) { |
| 294 | + return 0; |
| 295 | + } |
| 296 | + |
| 297 | + for(i=0; i < num_columns; i++) { |
| 298 | + new_bindings[i].buflen = 0; |
| 299 | + new_bindings[i].buffer = NULL; |
| 300 | + new_bindings[i].used = NULL; |
| 301 | + } |
| 302 | + |
| 303 | + return new_bindings; |
| 304 | +} |
| 305 | + |
| 306 | +void |
| 307 | +extend_bindings(StatementClass *stmt, int num_columns) |
| 308 | +{ |
| 309 | +BindInfoClass *new_bindings; |
| 310 | +int i; |
| 311 | + |
| 312 | + mylog("in extend_bindings\n"); |
| 313 | + |
| 314 | + /* if we have too few, allocate room for more, and copy the old */ |
| 315 | + /* entries into the new structure */ |
| 316 | + if(stmt->bindings_allocated < num_columns) { |
| 317 | + |
| 318 | + new_bindings = create_empty_bindings(num_columns); |
| 319 | + |
| 320 | + if(stmt->bindings) { |
| 321 | + for(i=0; i<stmt->bindings_allocated; i++) |
| 322 | + new_bindings[i] = stmt->bindings[i]; |
| 323 | + |
| 324 | + free(stmt->bindings); |
| 325 | + } |
| 326 | + |
| 327 | + stmt->bindings = new_bindings; // null indicates error |
| 328 | + |
| 329 | + } else { |
| 330 | + /* if we have too many, make sure the extra ones are emptied out */ |
| 331 | + /* so we don't accidentally try to use them for anything */ |
| 332 | + for(i = num_columns; i < stmt->bindings_allocated; i++) { |
| 333 | + stmt->bindings[i].buflen = 0; |
| 334 | + stmt->bindings[i].buffer = NULL; |
| 335 | + stmt->bindings[i].used = NULL; |
| 336 | + } |
| 337 | + } |
| 338 | + |
| 339 | + mylog("exit extend_bindings\n"); |
| 340 | +} |
0 commit comments