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