Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998, 1999, 2000, 2001 Brian Bruns
3 : * Copyright (C) 2002, 2003, 2004, 2005 James K. Lowden
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Library General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2 of the License, or (at your option) any later version.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Library General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Library General Public
16 : * License along with this library; if not, write to the
17 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 : * Boston, MA 02111-1307, USA.
19 : */
20 :
21 : #include <config.h>
22 :
23 : #include <stdarg.h>
24 : #include <stdio.h>
25 :
26 : #ifdef HAVE_UNISTD_H
27 : #include <unistd.h>
28 : #endif /* HAVE_UNISTD_H */
29 :
30 : #ifdef HAVE_STDLIB_H
31 : #include <stdlib.h>
32 : #endif /* HAVE_STDLIB_H */
33 :
34 : #ifdef HAVE_STRING_H
35 : #include <string.h>
36 : #endif /* HAVE_STRING_H */
37 :
38 : #if HAVE_ERRNO_H
39 : # include <errno.h>
40 : #endif /* HAVE_ERRNO_H */
41 :
42 : #include <assert.h>
43 :
44 : #include <freetds/tds.h>
45 : #include <freetds/convert.h>
46 : #include <freetds/utils/string.h>
47 : #include <freetds/replacements.h>
48 : #include <sybfront.h>
49 : #include <sybdb.h>
50 : #include <dblib.h>
51 :
52 : static void rpc_clear(DBREMOTE_PROC * rpc);
53 : static void param_clear(DBREMOTE_PROC_PARAM * pparam);
54 :
55 : static TDSPARAMINFO *param_info_alloc(TDSSOCKET * tds, DBREMOTE_PROC * rpc);
56 :
57 : /**
58 : * \ingroup dblib_rpc
59 : * \brief Initialize a remote procedure call.
60 : *
61 : * \param dbproc contains all information needed by db-lib to manage communications with the server.
62 : * \param rpcname name of the stored procedure to be run.
63 : * \param options Only supported option would be DBRPCRECOMPILE,
64 : * which causes the stored procedure to be recompiled before executing.
65 : * \remark The RPC functions are the only way to get back OUTPUT parameter data with db-lib
66 : * from modern Microsoft servers.
67 : * \retval SUCCEED normal.
68 : * \retval FAIL on error
69 : * \sa dbrpcparam(), dbrpcsend()
70 : */
71 : RETCODE
72 204 : dbrpcinit(DBPROCESS * dbproc, const char rpcname[], DBSMALLINT options)
73 : {
74 : DBREMOTE_PROC **rpc;
75 :
76 204 : tdsdump_log(TDS_DBG_FUNC, "dbrpcinit(%p, %s, %d)\n", dbproc, rpcname, options);
77 204 : CHECK_CONN(FAIL);
78 204 : CHECK_NULP(rpcname, "dbrpcinit", 2, FAIL);
79 :
80 : /*
81 : * TODO: adhere to docs. Only Microsoft supports DBRPCRESET. They say:
82 : * "Cancels a single stored procedure or a batch of stored procedures.
83 : * If rpcname is specified, that new stored procedure is initialized after the cancellation is complete."
84 : */
85 204 : if (options & DBRPCRESET) {
86 0 : rpc_clear(dbproc->rpc);
87 0 : dbproc->rpc = NULL;
88 0 : return SUCCEED;
89 : }
90 :
91 : /* any bits we want from the options argument */
92 : /* dbrpcrecompile = options & DBRPCRECOMPILE; */
93 204 : options &= ~DBRPCRECOMPILE; /* turn that one off, now that we've extracted it */
94 :
95 : /* all other options except DBRPCRECOMPILE are invalid */
96 204 : DBPERROR_RETURN3(options, SYBEIPV, (int) options, "options", "dbrpcinit");
97 :
98 : /* find a free node */
99 204 : for (rpc = &dbproc->rpc; *rpc != NULL; rpc = &(*rpc)->next) {
100 : /* check existing nodes for name match (there shouldn't be one) */
101 0 : if ((*rpc)->name == NULL || strcmp((*rpc)->name, rpcname) == 0) {
102 0 : tdsdump_log(TDS_DBG_INFO1, "error: dbrpcinit called twice for procedure \"%s\"\n", rpcname);
103 : return FAIL;
104 : }
105 : }
106 :
107 : /* rpc now contains the address of the dbproc's first empty (null) DBREMOTE_PROC* */
108 :
109 : /* allocate */
110 204 : if ((*rpc = tds_new0(DBREMOTE_PROC, 1)) == NULL) {
111 0 : dbperror(dbproc, SYBEMEM, errno);
112 0 : return FAIL;
113 : }
114 :
115 204 : if (((*rpc)->name = strdup(rpcname)) == NULL) {
116 0 : free(*rpc);
117 0 : *rpc = NULL;
118 0 : dbperror(dbproc, SYBEMEM, errno);
119 0 : return FAIL;
120 : }
121 :
122 : /* store */
123 204 : (*rpc)->options = options & DBRPCRECOMPILE;
124 204 : (*rpc)->param_list = NULL;
125 :
126 : /* completed */
127 204 : tdsdump_log(TDS_DBG_INFO1, "dbrpcinit() added rpcname \"%s\"\n", rpcname);
128 :
129 : return SUCCEED;
130 : }
131 :
132 : /**
133 : * \ingroup dblib_rpc
134 : * \brief Add a parameter to a remote procedure call.
135 : *
136 : * Call between dbrpcinit() and dbrpcsend()
137 : * \param dbproc contains all information needed by db-lib to manage communications with the server.
138 : * \param paramname literal name of the parameter, according to the stored procedure (starts with '@'). Optional.
139 : * If not used, parameters will be passed in order instead of by name.
140 : * \param status must be DBRPCRETURN, if this parameter is a return parameter, else 0.
141 : * \param type datatype of the value parameter e.g., SYBINT4, SYBCHAR.
142 : * \param maxlen Maximum output size of the parameter's value to be returned by the stored procedure,
143 : * usually the size of your host variable.
144 : * Fixed-length datatypes take -1 (NULL or not).
145 : * Non-OUTPUT parameters also use -1.
146 : * Use 0 to send a NULL value for a variable length datatype.
147 : * \param datalen For variable-length datatypes, the byte size of the data to be sent, exclusive of any null terminator.
148 : * For fixed-length datatypes use -1. To send a NULL value, use 0.
149 : * \param value Address of your host variable.
150 : * \retval SUCCEED normal.
151 : * \retval FAIL on error
152 : * \sa dbrpcinit(), dbrpcsend()
153 : */
154 : RETCODE
155 264 : dbrpcparam(DBPROCESS * dbproc, const char paramname[], BYTE status, int db_type, DBINT maxlen, DBINT datalen, BYTE * value)
156 : {
157 264 : char *name = NULL;
158 : DBREMOTE_PROC *rpc;
159 : DBREMOTE_PROC_PARAM **pparam;
160 : DBREMOTE_PROC_PARAM *param;
161 : TDS_SERVER_TYPE type;
162 :
163 264 : tdsdump_log(TDS_DBG_FUNC, "dbrpcparam(%p, %s, 0x%x, %d, %d, %d, %p)\n",
164 : dbproc, paramname, status, db_type, maxlen, datalen, value);
165 264 : CHECK_CONN(FAIL);
166 264 : CHECK_PARAMETER(dbproc->rpc, SYBERPCS, FAIL);
167 :
168 264 : DBPERROR_RETURN(!is_tds_type_valid(db_type), SYBEUDTY);
169 264 : type = (TDS_SERVER_TYPE) db_type;
170 :
171 : /* validate datalen parameter */
172 :
173 264 : if (is_fixed_type(type)) {
174 18 : if (datalen != 0)
175 8 : datalen = -1;
176 : } else { /* Sybooks: "Passing datalen as -1 for any of these [non-fixed] datatypes results
177 : * in the DBPROCESS referenced by dbproc being marked as "dead," or unusable."
178 : */
179 246 : DBPERROR_RETURN(datalen < 0, SYBERPIL);
180 : }
181 :
182 : /* "value parameter for dbprcparam() can be NULL, only if the datalen parameter is 0." */
183 264 : DBPERROR_RETURN(value == NULL && datalen != 0, SYBERPNULL);
184 :
185 : /* nullable types must provide a data length */
186 264 : DBPERROR_RETURN(is_nullable_type(type) && datalen < 0, SYBERPUL);
187 :
188 : /* validate maxlen parameter */
189 :
190 264 : if (status & DBRPCRETURN) {
191 48 : if (is_fixed_type(type)) {
192 : maxlen = -1;
193 : } else {
194 30 : if (maxlen == -1)
195 8 : maxlen = 255;
196 : }
197 : } else {
198 : /*
199 : * Well, maxlen should be used only for output parameter however it seems
200 : * that ms implementation wrongly require this 0 for NULL variable
201 : * input parameters, so fix it
202 : */
203 216 : DBPERROR_RETURN3(maxlen != -1 && maxlen != 0, SYBEIPV, (int) maxlen, "maxlen", "dbrpcparam");
204 : maxlen = -1;
205 : }
206 :
207 : /* end validation */
208 :
209 : /* This trick is to allow for client using utf8 to insert any character into a NVARCHAR parameter
210 : * The 4000 check is to allow varchar with more then 4000 characters (varchar is limited to 8000
211 : * characters) which can't be converted to nvarchar (which is limited to 4000 character)
212 : */
213 264 : if (type == SYBVARCHAR && IS_TDS7_PLUS(dbproc->tds_socket->conn)
214 26 : && maxlen <= 4000 && datalen <= 4000)
215 20 : type = XSYBNVARCHAR;
216 :
217 : /* allocate */
218 264 : param = tds_new(DBREMOTE_PROC_PARAM, 1);
219 264 : if (param == NULL) {
220 0 : dbperror(dbproc, SYBEMEM, 0);
221 0 : return FAIL;
222 : }
223 :
224 264 : if (paramname) {
225 252 : name = strdup(paramname);
226 252 : if (name == NULL) {
227 0 : free(param);
228 0 : dbperror(dbproc, SYBEMEM, 0);
229 0 : return FAIL;
230 : }
231 : }
232 :
233 : /* initialize */
234 264 : param->next = NULL; /* NULL signifies end of linked list */
235 264 : param->name = name;
236 264 : param->status = status;
237 264 : param->type = type;
238 264 : param->maxlen = maxlen;
239 264 : param->datalen = datalen;
240 :
241 : /*
242 : * If datalen = 0, value parameter is ignored.
243 : * This is one way to specify a NULL input parameter.
244 : */
245 264 : if (datalen == 0)
246 48 : param->value = NULL;
247 : else
248 216 : param->value = value;
249 :
250 : /*
251 : * Add a parameter to the current rpc.
252 : *
253 : * Traverse the dbproc's procedure list to find the current rpc,
254 : * then traverse the parameter linked list until its end,
255 : * then tack on our parameter's address.
256 : */
257 264 : for (rpc = dbproc->rpc; rpc->next != NULL; rpc = rpc->next) /* find "current" procedure */
258 0 : continue;
259 474 : for (pparam = &rpc->param_list; *pparam != NULL; pparam = &(*pparam)->next)
260 210 : continue;
261 :
262 : /* pparam now contains the address of the end of the rpc's parameter list */
263 :
264 264 : *pparam = param; /* add to the end of the list */
265 :
266 264 : tdsdump_log(TDS_DBG_INFO1, "dbrpcparam() added parameter \"%s\"\n", (paramname) ? paramname : "");
267 :
268 : return SUCCEED;
269 : }
270 :
271 : /**
272 : * \ingroup dblib_rpc
273 : * \brief Execute the procedure and free associated memory
274 : *
275 : * \param dbproc contains all information needed by db-lib to manage communications with the server.
276 : * \retval SUCCEED normal.
277 : * \retval FAIL on error
278 : * \sa dbrpcinit(), dbrpcparam()
279 : */
280 : RETCODE
281 204 : dbrpcsend(DBPROCESS * dbproc)
282 : {
283 : DBREMOTE_PROC *rpc;
284 :
285 204 : tdsdump_log(TDS_DBG_FUNC, "dbrpcsend(%p)\n", dbproc);
286 204 : CHECK_CONN(FAIL);
287 204 : CHECK_PARAMETER(dbproc->rpc, SYBERPCS, FAIL); /* dbrpcinit should allocate pointer */
288 :
289 : /* sanity */
290 204 : if (dbproc->rpc->name == NULL) { /* can't be ready without a name */
291 0 : tdsdump_log(TDS_DBG_INFO1, "returning FAIL: name is NULL\n");
292 : return FAIL;
293 : }
294 :
295 204 : dbproc->dbresults_state = _DB_RES_INIT;
296 :
297 408 : for (rpc = dbproc->rpc; rpc != NULL; rpc = rpc->next) {
298 : TDSRET erc;
299 204 : TDSPARAMINFO *pparam_info = NULL;
300 :
301 : /*
302 : * liam@inodes.org: allow stored procedures to have no paramaters
303 : */
304 204 : if (rpc->param_list != NULL) {
305 204 : pparam_info = param_info_alloc(dbproc->tds_socket, rpc);
306 204 : if (!pparam_info)
307 : return FAIL;
308 : }
309 204 : erc = tds_submit_rpc(dbproc->tds_socket, dbproc->rpc->name, pparam_info, NULL);
310 204 : tds_free_param_results(pparam_info);
311 204 : if (TDS_FAILED(erc)) {
312 0 : tdsdump_log(TDS_DBG_INFO1, "returning FAIL: tds_submit_rpc() failed\n");
313 : return FAIL;
314 : }
315 : }
316 :
317 : /* free up the memory */
318 204 : rpc_clear(dbproc->rpc);
319 204 : dbproc->rpc = NULL;
320 :
321 204 : tdsdump_log(TDS_DBG_FUNC, "dbrpcsend() returning SUCCEED\n");
322 :
323 : return SUCCEED;
324 : }
325 :
326 : /**
327 : * Tell the TDSPARAMINFO structure where the data go. This is a kind of "bind" operation.
328 : */
329 : static const unsigned char *
330 264 : param_row_alloc(TDSPARAMINFO * params, TDSCOLUMN * curcol, int param_num, void *value, int size)
331 : {
332 264 : const void *row = tds_alloc_param_data(curcol);
333 264 : tdsdump_log(TDS_DBG_INFO1, "parameter size = %d, data = %p, row_size = %d\n",
334 : size, curcol->column_data, params->row_size);
335 264 : if (!row)
336 : return NULL;
337 264 : if (size > 0 && value) {
338 216 : tdsdump_log(TDS_DBG_FUNC, "copying %d bytes of data to parameter #%d\n", size, param_num);
339 216 : if (!is_blob_col(curcol)) {
340 216 : if (is_numeric_type(curcol->column_type))
341 192 : memset(curcol->column_data, 0, sizeof(TDS_NUMERIC));
342 216 : memcpy(curcol->column_data, value, size);
343 : } else {
344 0 : TDSBLOB *blob = (TDSBLOB *) curcol->column_data;
345 0 : blob->textvalue = tds_new(TDS_CHAR, size);
346 0 : tdsdump_log(TDS_DBG_FUNC, "blob parameter supported, size %d textvalue pointer is %p\n",
347 : size, blob->textvalue);
348 0 : if (!blob->textvalue)
349 : return NULL;
350 0 : memcpy(blob->textvalue, value, size);
351 : }
352 : }
353 : else {
354 48 : tdsdump_log(TDS_DBG_FUNC, "setting parameter #%d to NULL\n", param_num);
355 48 : curcol->column_cur_size = -1;
356 : }
357 :
358 : return (const unsigned char*) row;
359 : }
360 :
361 : /**
362 : * Allocate memory and copy the rpc information into a TDSPARAMINFO structure.
363 : */
364 : static TDSPARAMINFO *
365 204 : param_info_alloc(TDSSOCKET * tds, DBREMOTE_PROC * rpc)
366 : {
367 : int i;
368 : DBREMOTE_PROC_PARAM *p;
369 : TDSCOLUMN *pcol;
370 204 : TDSPARAMINFO *params = NULL, *new_params;
371 : BYTE *temp_value;
372 : int temp_datalen;
373 : TDS_SERVER_TYPE temp_type;
374 : int param_is_null;
375 :
376 : /* sanity */
377 204 : if (rpc == NULL)
378 : return NULL;
379 :
380 : /* see v 1.10 2002/11/23 for first broken attempt */
381 :
382 468 : for (i = 0, p = rpc->param_list; p != NULL; p = p->next, i++) {
383 : const unsigned char *prow;
384 :
385 264 : if (!(new_params = tds_alloc_param_result(params))) {
386 0 : tds_free_param_results(params);
387 0 : tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
388 : return NULL;
389 : }
390 264 : params = new_params;
391 :
392 : /*
393 : * Determine whether an input parameter is NULL or not.
394 : */
395 264 : param_is_null = 0;
396 264 : temp_type = p->type;
397 264 : temp_value = p->value;
398 264 : temp_datalen = p->datalen;
399 :
400 264 : if (p->datalen == 0)
401 48 : param_is_null = 1;
402 :
403 264 : tdsdump_log(TDS_DBG_INFO1, "parm_info_alloc(): parameter null-ness = %d\n", param_is_null);
404 :
405 264 : pcol = params->columns[i];
406 :
407 264 : if (temp_value && is_numeric_type(temp_type)) {
408 192 : DBDECIMAL *dec = (DBDECIMAL*) temp_value;
409 192 : pcol->column_prec = dec->precision;
410 192 : pcol->column_scale = dec->scale;
411 192 : if (dec->precision > 0 && dec->precision <= MAXPRECISION)
412 192 : temp_datalen = tds_numeric_bytes_per_prec[dec->precision] + 2;
413 : }
414 264 : if (param_is_null || (p->status & DBRPCRETURN)) {
415 56 : if (param_is_null) {
416 : temp_datalen = 0;
417 : temp_value = NULL;
418 8 : } else if (is_fixed_type(temp_type)) {
419 8 : temp_datalen = tds_get_size_by_type(temp_type);
420 : }
421 56 : temp_type = tds_get_null_type(temp_type);
422 208 : } else if (is_fixed_type(temp_type)) {
423 0 : temp_datalen = tds_get_size_by_type(temp_type);
424 : }
425 :
426 : /* meta data */
427 264 : if (p->name)
428 252 : if (!tds_dstr_copy(&pcol->column_name, p->name)) {
429 0 : tds_free_param_results(params);
430 0 : tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
431 : return NULL;
432 : }
433 :
434 264 : tds_set_param_type(tds->conn, pcol, temp_type);
435 :
436 264 : if (p->maxlen > 0)
437 30 : pcol->column_size = p->maxlen;
438 : else {
439 234 : if (is_fixed_type(p->type)) {
440 18 : pcol->column_size = tds_get_size_by_type(p->type);
441 : } else {
442 216 : pcol->column_size = p->datalen;
443 : }
444 : }
445 264 : if (p->type == XSYBNVARCHAR)
446 26 : pcol->column_size *= 2;
447 264 : pcol->on_server.column_size = pcol->column_size;
448 :
449 264 : pcol->column_output = p->status;
450 264 : pcol->column_cur_size = temp_datalen;
451 :
452 264 : prow = param_row_alloc(params, pcol, i, temp_value, temp_datalen);
453 :
454 264 : if (!prow) {
455 0 : tds_free_param_results(params);
456 0 : tdsdump_log(TDS_DBG_ERROR, "out of memory for rpc row!");
457 : return NULL;
458 : }
459 :
460 : }
461 :
462 : return params;
463 :
464 : }
465 :
466 : /**
467 : * erase the procedure list
468 : */
469 : static void
470 204 : rpc_clear(DBREMOTE_PROC * rpc)
471 : {
472 : DBREMOTE_PROC * next;
473 :
474 612 : while (rpc) {
475 204 : next = rpc->next;
476 204 : param_clear(rpc->param_list);
477 204 : free(rpc->name);
478 204 : free(rpc);
479 204 : rpc = next;
480 : }
481 204 : }
482 :
483 : /**
484 : * erase the parameter list
485 : */
486 : static void
487 204 : param_clear(DBREMOTE_PROC_PARAM * pparam)
488 : {
489 : DBREMOTE_PROC_PARAM * next;
490 :
491 672 : while (pparam) {
492 264 : next = pparam->next;
493 264 : free(pparam->name);
494 : /* free self */
495 264 : free(pparam);
496 264 : pparam = next;
497 : }
498 204 : }
|