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 258 : dbrpcinit(DBPROCESS * dbproc, const char rpcname[], DBSMALLINT options)
73 : {
74 : DBREMOTE_PROC **rpc;
75 :
76 258 : tdsdump_log(TDS_DBG_FUNC, "dbrpcinit(%p, %s, %d)\n", dbproc, rpcname, options);
77 258 : CHECK_CONN(FAIL);
78 258 : 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 258 : 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 258 : options &= ~DBRPCRECOMPILE; /* turn that one off, now that we've extracted it */
94 :
95 : /* all other options except DBRPCRECOMPILE are invalid */
96 258 : DBPERROR_RETURN3(options, SYBEIPV, (int) options, "options", "dbrpcinit");
97 :
98 : /* find a free node */
99 258 : 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 258 : if ((*rpc = tds_new0(DBREMOTE_PROC, 1)) == NULL) {
111 0 : dbperror(dbproc, SYBEMEM, errno);
112 0 : return FAIL;
113 : }
114 :
115 258 : 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 258 : (*rpc)->options = options & DBRPCRECOMPILE;
124 258 : (*rpc)->param_list = NULL;
125 :
126 : /* completed */
127 258 : 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 340 : dbrpcparam(DBPROCESS * dbproc, const char paramname[], BYTE status, int db_type, DBINT maxlen, DBINT datalen, BYTE * value)
156 : {
157 340 : char *name = NULL;
158 : DBREMOTE_PROC *rpc;
159 : DBREMOTE_PROC_PARAM **pparam;
160 : DBREMOTE_PROC_PARAM *param;
161 : TDS_SERVER_TYPE type;
162 :
163 340 : 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 340 : CHECK_CONN(FAIL);
166 340 : CHECK_PARAMETER(dbproc->rpc, SYBERPCS, FAIL);
167 :
168 340 : DBPERROR_RETURN(!is_tds_type_valid(db_type), SYBEUDTY);
169 340 : type = (TDS_SERVER_TYPE) db_type;
170 :
171 : /* validate datalen parameter */
172 :
173 340 : if (is_fixed_type(type)) {
174 24 : if (datalen != 0)
175 10 : 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 316 : DBPERROR_RETURN(datalen < 0, SYBERPIL);
180 : }
181 :
182 : /* "value parameter for dbprcparam() can be NULL, only if the datalen parameter is 0." */
183 340 : DBPERROR_RETURN(value == NULL && datalen != 0, SYBERPNULL);
184 :
185 : /* nullable types must provide a data length */
186 340 : DBPERROR_RETURN(is_nullable_type(type) && datalen < 0, SYBERPUL);
187 :
188 : /* validate maxlen parameter */
189 :
190 340 : if (status & DBRPCRETURN) {
191 64 : if (is_fixed_type(type)) {
192 : maxlen = -1;
193 : } else {
194 40 : if (maxlen == -1)
195 10 : 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 276 : 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 340 : if (type == SYBVARCHAR && IS_TDS7_PLUS(dbproc->tds_socket->conn)
214 40 : && maxlen <= 4000 && datalen <= 4000)
215 32 : type = XSYBNVARCHAR;
216 :
217 : /* allocate */
218 340 : param = tds_new(DBREMOTE_PROC_PARAM, 1);
219 340 : if (param == NULL) {
220 0 : dbperror(dbproc, SYBEMEM, 0);
221 0 : return FAIL;
222 : }
223 :
224 340 : if (paramname) {
225 316 : name = strdup(paramname);
226 316 : if (name == NULL) {
227 0 : free(param);
228 0 : dbperror(dbproc, SYBEMEM, 0);
229 0 : return FAIL;
230 : }
231 : }
232 :
233 : /* initialize */
234 340 : param->next = NULL; /* NULL signifies end of linked list */
235 340 : param->name = name;
236 340 : param->status = status;
237 340 : param->type = type;
238 340 : param->maxlen = maxlen;
239 340 : 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 340 : if (datalen == 0)
246 64 : param->value = NULL;
247 : else
248 276 : 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 340 : for (rpc = dbproc->rpc; rpc->next != NULL; rpc = rpc->next) /* find "current" procedure */
258 0 : continue;
259 618 : for (pparam = &rpc->param_list; *pparam != NULL; pparam = &(*pparam)->next)
260 278 : continue;
261 :
262 : /* pparam now contains the address of the end of the rpc's parameter list */
263 :
264 340 : *pparam = param; /* add to the end of the list */
265 :
266 340 : 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 258 : dbrpcsend(DBPROCESS * dbproc)
282 : {
283 : DBREMOTE_PROC *rpc;
284 :
285 258 : tdsdump_log(TDS_DBG_FUNC, "dbrpcsend(%p)\n", dbproc);
286 258 : CHECK_CONN(FAIL);
287 258 : CHECK_PARAMETER(dbproc->rpc, SYBERPCS, FAIL); /* dbrpcinit should allocate pointer */
288 :
289 : /* sanity */
290 258 : 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 258 : dbproc->dbresults_state = _DB_RES_INIT;
296 :
297 516 : for (rpc = dbproc->rpc; rpc != NULL; rpc = rpc->next) {
298 : TDSRET erc;
299 258 : TDSPARAMINFO *pparam_info = NULL;
300 :
301 : /*
302 : * liam@inodes.org: allow stored procedures to have no paramaters
303 : */
304 258 : if (rpc->param_list != NULL) {
305 258 : pparam_info = param_info_alloc(dbproc->tds_socket, rpc);
306 258 : if (!pparam_info)
307 : return FAIL;
308 : }
309 258 : erc = tds_submit_rpc(dbproc->tds_socket, dbproc->rpc->name, pparam_info, NULL);
310 258 : tds_free_param_results(pparam_info);
311 258 : 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 258 : rpc_clear(dbproc->rpc);
319 258 : dbproc->rpc = NULL;
320 :
321 258 : 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 340 : param_row_alloc(TDSPARAMINFO * params, TDSCOLUMN * curcol, int param_num, void *value, int size)
331 : {
332 340 : const void *row = tds_alloc_param_data(curcol);
333 340 : tdsdump_log(TDS_DBG_INFO1, "parameter size = %d, data = %p, row_size = %d\n",
334 : size, curcol->column_data, params->row_size);
335 340 : if (!row)
336 : return NULL;
337 340 : if (size > 0 && value) {
338 276 : tdsdump_log(TDS_DBG_FUNC, "copying %d bytes of data to parameter #%d\n", size, param_num);
339 276 : if (!is_blob_col(curcol)) {
340 276 : if (is_numeric_type(curcol->column_type))
341 240 : memset(curcol->column_data, 0, sizeof(TDS_NUMERIC));
342 276 : 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 : } else {
353 64 : tdsdump_log(TDS_DBG_FUNC, "setting parameter #%d to NULL\n", param_num);
354 64 : curcol->column_cur_size = -1;
355 : }
356 :
357 : return (const unsigned char*) row;
358 : }
359 :
360 : /**
361 : * Allocate memory and copy the rpc information into a TDSPARAMINFO structure.
362 : */
363 : static TDSPARAMINFO *
364 258 : param_info_alloc(TDSSOCKET * tds, DBREMOTE_PROC * rpc)
365 : {
366 : int i;
367 : DBREMOTE_PROC_PARAM *p;
368 : TDSCOLUMN *pcol;
369 258 : TDSPARAMINFO *params = NULL, *new_params;
370 : BYTE *temp_value;
371 : int temp_datalen;
372 : TDS_SERVER_TYPE temp_type;
373 : int param_is_null;
374 :
375 : /* sanity */
376 258 : if (rpc == NULL)
377 : return NULL;
378 :
379 : /* see v 1.10 2002/11/23 for first broken attempt */
380 :
381 598 : for (i = 0, p = rpc->param_list; p != NULL; p = p->next, i++) {
382 : const unsigned char *prow;
383 :
384 340 : if (!(new_params = tds_alloc_param_result(params))) {
385 0 : tds_free_param_results(params);
386 0 : tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
387 : return NULL;
388 : }
389 340 : params = new_params;
390 :
391 : /*
392 : * Determine whether an input parameter is NULL or not.
393 : */
394 340 : param_is_null = 0;
395 340 : temp_type = p->type;
396 340 : temp_value = p->value;
397 340 : temp_datalen = p->datalen;
398 :
399 340 : if (p->datalen == 0)
400 64 : param_is_null = 1;
401 :
402 340 : tdsdump_log(TDS_DBG_INFO1, "parm_info_alloc(): parameter null-ness = %d\n", param_is_null);
403 :
404 340 : pcol = params->columns[i];
405 :
406 340 : if (temp_value && is_numeric_type(temp_type)) {
407 240 : DBDECIMAL *dec = (DBDECIMAL*) temp_value;
408 240 : pcol->column_prec = dec->precision;
409 240 : pcol->column_scale = dec->scale;
410 240 : if (dec->precision > 0 && dec->precision <= MAXPRECISION)
411 240 : temp_datalen = tds_numeric_bytes_per_prec[dec->precision] + 2;
412 : }
413 340 : if (param_is_null || (p->status & DBRPCRETURN)) {
414 74 : if (param_is_null) {
415 : temp_datalen = 0;
416 : temp_value = NULL;
417 10 : } else if (is_fixed_type(temp_type)) {
418 10 : temp_datalen = tds_get_size_by_type(temp_type);
419 : }
420 74 : temp_type = tds_get_null_type(temp_type);
421 266 : } else if (is_fixed_type(temp_type)) {
422 0 : temp_datalen = tds_get_size_by_type(temp_type);
423 : }
424 :
425 : /* meta data */
426 340 : if (p->name)
427 316 : if (!tds_dstr_copy(&pcol->column_name, p->name)) {
428 0 : tds_free_param_results(params);
429 0 : tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
430 : return NULL;
431 : }
432 :
433 340 : tds_set_param_type(tds->conn, pcol, temp_type);
434 :
435 340 : if (p->maxlen > 0)
436 40 : pcol->column_size = p->maxlen;
437 : else {
438 300 : if (is_fixed_type(p->type)) {
439 24 : pcol->column_size = tds_get_size_by_type(p->type);
440 : } else {
441 276 : pcol->column_size = p->datalen;
442 : }
443 : }
444 340 : if (p->type == XSYBNVARCHAR)
445 40 : pcol->column_size *= 2;
446 340 : pcol->on_server.column_size = pcol->column_size;
447 :
448 340 : pcol->column_output = p->status;
449 340 : pcol->column_cur_size = temp_datalen;
450 :
451 340 : prow = param_row_alloc(params, pcol, i, temp_value, temp_datalen);
452 :
453 340 : if (!prow) {
454 0 : tds_free_param_results(params);
455 0 : tdsdump_log(TDS_DBG_ERROR, "out of memory for rpc row!");
456 : return NULL;
457 : }
458 :
459 : }
460 :
461 : return params;
462 :
463 : }
464 :
465 : /**
466 : * erase the procedure list
467 : */
468 : static void
469 258 : rpc_clear(DBREMOTE_PROC * rpc)
470 : {
471 : DBREMOTE_PROC * next;
472 :
473 774 : while (rpc) {
474 258 : next = rpc->next;
475 258 : param_clear(rpc->param_list);
476 258 : free(rpc->name);
477 258 : free(rpc);
478 258 : rpc = next;
479 : }
480 258 : }
481 :
482 : /**
483 : * erase the parameter list
484 : */
485 : static void
486 258 : param_clear(DBREMOTE_PROC_PARAM * pparam)
487 : {
488 : DBREMOTE_PROC_PARAM * next;
489 :
490 856 : while (pparam) {
491 340 : next = pparam->next;
492 340 : free(pparam->name);
493 : /* free self */
494 340 : free(pparam);
495 340 : pparam = next;
496 : }
497 258 : }
|