Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns
3 : * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Frediano Ziglio
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 : #if HAVE_STDLIB_H
27 : #include <stdlib.h>
28 : #endif /* HAVE_STDLIB_H */
29 :
30 : #if HAVE_STRING_H
31 : #include <string.h>
32 : #endif /* HAVE_STRING_H */
33 :
34 : #include <ctype.h>
35 :
36 : #include <freetds/tds.h>
37 : #include <freetds/enum_cap.h>
38 : #include <freetds/tds/iconv.h>
39 : #include <freetds/tds/convert.h>
40 : #include <freetds/utils/string.h>
41 : #include <freetds/tds/checks.h>
42 : #include <freetds/tds/stream.h>
43 : #include <freetds/bytes.h>
44 : #include <freetds/replacements.h>
45 :
46 : #include <assert.h>
47 :
48 : static TDSRET tds5_put_params(TDSSOCKET * tds, TDSPARAMINFO * info, int flags) TDS_WUR;
49 : static void tds7_put_query_params(TDSSOCKET * tds, const char *query, size_t query_len);
50 : static TDSRET tds_put_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int flags);
51 : static inline TDSRET tds_put_data(TDSSOCKET * tds, TDSCOLUMN * curcol);
52 : static TDSRET tds7_write_param_def_from_query(TDSSOCKET * tds, const char* converted_query,
53 : size_t converted_query_len, TDSPARAMINFO * params) TDS_WUR;
54 : static TDSRET tds7_write_param_def_from_params(TDSSOCKET * tds, const char* query, size_t query_len,
55 : TDSPARAMINFO * params) TDS_WUR;
56 :
57 : static TDSRET tds_put_param_as_string(TDSSOCKET * tds, TDSPARAMINFO * params, int n);
58 : static TDSRET tds_send_emulated_execute(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params);
59 : static int tds_count_placeholders_ucs2le(const char *query, const char *query_end);
60 :
61 : #define TDS_PUT_DATA_USE_NAME 1
62 : #define TDS_PUT_DATA_PREFIX_NAME 2
63 : #define TDS_PUT_DATA_LONG_STATUS 4
64 :
65 : /* All manner of client to server submittal functions */
66 :
67 : /**
68 : * \ingroup libtds
69 : * \defgroup query Query
70 : * Function to handle query.
71 : */
72 :
73 : /**
74 : * \addtogroup query
75 : * @{
76 : */
77 :
78 : /**
79 : * Accept an ASCII string, convert it to UCS2-LE
80 : * The input is NUL-terminated, but the output does not contains the NUL.
81 : * \param buffer buffer where to store output
82 : * \param buf string to write
83 : * \return bytes written
84 : */
85 : static size_t
86 1138 : tds_ascii_to_ucs2(char *buffer, const char *buf)
87 : {
88 : char *s;
89 1138 : assert(buffer && buf && *buf); /* This is an internal function. Call it correctly. */
90 :
91 11380 : for (s = buffer; *buf != '\0'; ++buf) {
92 11380 : *s++ = *buf;
93 11380 : *s++ = '\0';
94 : }
95 :
96 1138 : return s - buffer;
97 : }
98 :
99 : /**
100 : * Utility to convert a constant ascii string to ucs2 and send to server.
101 : * Used to send internal store procedure names to server.
102 : * \tds
103 : * \param s constanst string to send
104 : */
105 : #define TDS_PUT_N_AS_UCS2(tds, s) do { \
106 : char buffer[sizeof(s)*2-2]; \
107 : tds_put_smallint(tds, sizeof(buffer)/2); \
108 : tds_put_n(tds, buffer, tds_ascii_to_ucs2(buffer, s)); \
109 : } while(0)
110 :
111 : /**
112 : * Convert a string in an allocated buffer
113 : * \param tds state information for the socket and the TDS protocol
114 : * \param char_conv information about the encodings involved
115 : * \param s input string
116 : * \param len input string length (in bytes), -1 for NUL-terminated
117 : * \param out_len returned output length (in bytes)
118 : * \return string allocated (or input pointer if no conversion required) or NULL if error
119 : */
120 : const char *
121 11856 : tds_convert_string(TDSSOCKET * tds, TDSICONV * char_conv, const char *s, ptrdiff_t len, size_t *out_len)
122 : {
123 : char *buf;
124 :
125 : const char *ib;
126 : char *ob;
127 : size_t il, ol;
128 :
129 : /* char_conv is only mostly const */
130 11856 : TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress;
131 :
132 11856 : CHECK_TDS_EXTRA(tds);
133 :
134 11856 : il = len < 0 ? strlen(s) : (size_t) len;
135 11856 : if (char_conv->flags == TDS_ENCODING_MEMCPY) {
136 0 : *out_len = il;
137 0 : return s;
138 : }
139 :
140 : /* allocate needed buffer (+1 is to exclude 0 case) */
141 11856 : ol = il * char_conv->to.charset.max_bytes_per_char / char_conv->from.charset.min_bytes_per_char + 1;
142 11856 : buf = tds_new(char, ol);
143 11856 : if (!buf) {
144 0 : *out_len = 0;
145 0 : return NULL;
146 : }
147 :
148 11856 : ib = s;
149 11856 : ob = buf;
150 11856 : memset(suppress, 0, sizeof(char_conv->suppress));
151 11856 : if (tds_iconv(tds, char_conv, to_server, &ib, &il, &ob, &ol) == (size_t)-1) {
152 0 : free(buf);
153 0 : return NULL;
154 : }
155 11856 : *out_len = ob - buf;
156 11856 : return buf;
157 : }
158 :
159 : #if ENABLE_EXTRA_CHECKS
160 : void
161 6594 : tds_convert_string_free(const char *original, const char *converted)
162 : {
163 11840 : if (original != converted)
164 11840 : free((char *) converted);
165 6594 : }
166 : #endif
167 :
168 : /**
169 : * Flush query packet.
170 : * Used at the end of packet write to really send packet to server.
171 : * This also changes the state to TDS_PENDING.
172 : * \tds
173 : */
174 : static TDSRET
175 : tds_query_flush_packet(TDSSOCKET *tds)
176 : {
177 75164 : TDSRET ret = tds_flush_packet(tds);
178 : /* TODO depend on result ?? */
179 75164 : tds_set_state(tds, TDS_PENDING);
180 : return ret;
181 : }
182 :
183 : /**
184 : * Set current dynamic.
185 : * \tds
186 : * \param dyn dynamic to set
187 : */
188 : void
189 1010 : tds_set_cur_dyn(TDSSOCKET *tds, TDSDYNAMIC *dyn)
190 : {
191 2018 : if (dyn)
192 6940 : ++dyn->ref_count;
193 6940 : tds_release_cur_dyn(tds);
194 6940 : tds->cur_dyn = dyn;
195 1010 : }
196 :
197 : /**
198 : * Sends a language string to the database server for
199 : * processing. TDS 4.2 is a plain text message with a packet type of 0x01,
200 : * TDS 7.0 is a unicode string with packet type 0x01, and TDS 5.0 uses a
201 : * TDS_LANGUAGE_TOKEN to encapsulate the query and a packet type of 0x0f.
202 : * \tds
203 : * \param query language query to submit
204 : * \return TDS_FAIL or TDS_SUCCESS
205 : */
206 : TDSRET
207 36083 : tds_submit_query(TDSSOCKET * tds, const char *query)
208 : {
209 37979 : return tds_submit_query_params(tds, query, NULL, NULL);
210 : }
211 :
212 : /**
213 : * Substitute ?-style placeholders with named (\@param) ones.
214 : * Sybase does not support ?-style placeholders so convert them.
215 : * Also the function replace parameter names.
216 : * \param query query string
217 : * \param[in,out] query_len pointer to query length.
218 : * On input length of input query, on output length
219 : * of output query
220 : * \param params parameters to send to server
221 : * \returns new query or NULL on error
222 : */
223 : static char *
224 2 : tds5_fix_dot_query(const char *query, size_t *query_len, TDSPARAMINFO * params)
225 : {
226 : int i;
227 : size_t len, pos;
228 : const char *e, *s;
229 2 : size_t size = *query_len + 30;
230 : char colname[32];
231 : char *out;
232 :
233 2 : out = tds_new(char, size);
234 2 : if (!out)
235 : goto memory_error;
236 : pos = 0;
237 :
238 : s = query;
239 12 : for (i = 0;; ++i) {
240 26 : e = tds_next_placeholder(s);
241 14 : len = e ? e - s : strlen(s);
242 14 : if (pos + len + 12 >= size) {
243 0 : size = pos + len + 30;
244 0 : if (!TDS_RESIZE(out, size))
245 : goto memory_error;
246 : }
247 14 : memcpy(out + pos, s, len);
248 14 : pos += len;
249 14 : if (!e)
250 : break;
251 12 : pos += sprintf(out + pos, "@P%d", i + 1);
252 24 : if (!params || i >= params->num_cols)
253 : goto memory_error;
254 12 : sprintf(colname, "@P%d", i + 1);
255 12 : if (!tds_dstr_copy(¶ms->columns[i]->column_name, colname))
256 : goto memory_error;
257 :
258 12 : s = e + 1;
259 : }
260 2 : out[pos] = 0;
261 2 : *query_len = pos;
262 2 : return out;
263 :
264 0 : memory_error:
265 0 : free(out);
266 0 : return NULL;
267 : }
268 :
269 : /**
270 : * Write data to wire
271 : * \tds
272 : * \param curcol column where store column information
273 : * \return TDS_FAIL on error or TDS_SUCCESS
274 : */
275 : static inline TDSRET
276 : tds_put_data(TDSSOCKET * tds, TDSCOLUMN * curcol)
277 : {
278 9752 : return curcol->funcs->put_data(tds, curcol, false);
279 : }
280 :
281 : /**
282 : * Start query packet of a given type
283 : * \tds
284 : * \param packet_type packet type
285 : * \param head extra information to put in a TDS7 header
286 : */
287 : static TDSRET
288 66074 : tds_start_query_head(TDSSOCKET *tds, unsigned char packet_type, TDSHEADERS * head)
289 : {
290 66074 : tds->out_flag = packet_type;
291 66074 : if (IS_TDS72_PLUS(tds->conn)) {
292 : TDSFREEZE outer;
293 :
294 33167 : tds_freeze(tds, &outer, 4); /* total length */
295 33167 : tds_put_int(tds, 18); /* length: transaction descriptor */
296 33167 : tds_put_smallint(tds, 2); /* type: transaction descriptor */
297 33167 : tds_put_n(tds, tds->conn->tds72_transaction, 8); /* transaction */
298 33167 : tds_put_int(tds, 1); /* request count */
299 33167 : if (head && head->qn_msgtext && head->qn_options) {
300 : TDSFREEZE query;
301 :
302 4 : tds_freeze(tds, &query, 4); /* length: query notification */
303 4 : tds_put_smallint(tds, 1); /* type: query notification */
304 :
305 4 : TDS_START_LEN_USMALLINT(tds) {
306 4 : tds_put_string(tds, head->qn_msgtext, -1); /* notifyid */
307 4 : } TDS_END_LEN
308 :
309 4 : TDS_START_LEN_USMALLINT(tds) {
310 4 : tds_put_string(tds, head->qn_options, -1); /* ssbdeployment */
311 4 : } TDS_END_LEN
312 :
313 4 : if (head->qn_timeout != 0)
314 4 : tds_put_int(tds, head->qn_timeout); /* timeout */
315 :
316 4 : tds_freeze_close_len(&query, tds_freeze_written(&query));
317 : }
318 33167 : tds_freeze_close_len(&outer, tds_freeze_written(&outer));
319 : }
320 66074 : return TDS_SUCCESS;
321 : }
322 :
323 : /**
324 : * Start query packet of a given type
325 : * \tds
326 : * \param packet_type packet type
327 : */
328 : void
329 778 : tds_start_query(TDSSOCKET *tds, unsigned char packet_type)
330 : {
331 : /* no need to check return value here because tds_start_query_head() cannot
332 : fail when given a NULL head parameter */
333 10008 : tds_start_query_head(tds, packet_type, NULL);
334 778 : }
335 :
336 : /**
337 : * Sends a language string to the database server for
338 : * processing. TDS 4.2 is a plain text message with a packet type of 0x01,
339 : * TDS 7.0 is a unicode string with packet type 0x01, and TDS 5.0 uses a
340 : * TDS_LANGUAGE_TOKEN to encapsulate the query and a packet type of 0x0f.
341 : * \tds
342 : * \param query language query to submit
343 : * \param params parameters of query
344 : * \return TDS_FAIL or TDS_SUCCESS
345 : */
346 : TDSRET
347 63954 : tds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head)
348 : {
349 : size_t query_len;
350 63954 : int num_params = params ? params->num_cols : 0;
351 :
352 63954 : CHECK_TDS_EXTRA(tds);
353 63954 : if (params)
354 30 : CHECK_PARAMINFO_EXTRA(params);
355 :
356 63954 : if (!query)
357 : return TDS_FAIL;
358 :
359 63954 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
360 : return TDS_FAIL;
361 :
362 63952 : query_len = strlen(query);
363 :
364 63952 : if (IS_TDS50(tds->conn)) {
365 10020 : char *new_query = NULL;
366 : /* are there '?' style parameters ? */
367 10020 : if (tds_next_placeholder(query)) {
368 2 : if ((new_query = tds5_fix_dot_query(query, &query_len, params)) == NULL) {
369 0 : tds_set_state(tds, TDS_IDLE);
370 0 : return TDS_FAIL;
371 : }
372 : query = new_query;
373 : }
374 :
375 10020 : tds->out_flag = TDS_NORMAL;
376 10020 : tds_put_byte(tds, TDS_LANGUAGE_TOKEN);
377 10020 : TDS_START_LEN_UINT(tds) {
378 10020 : tds_put_byte(tds, params ? 1 : 0); /* 1 if there are params, 0 otherwise */
379 10020 : tds_put_string(tds, query, query_len);
380 10020 : } TDS_END_LEN
381 10020 : if (params) {
382 : /* add on parameters */
383 12 : int flags = tds_dstr_isempty(¶ms->columns[0]->column_name) ? 0 : TDS_PUT_DATA_USE_NAME;
384 6 : TDS_PROPAGATE(tds5_put_params(tds, params, flags));
385 : }
386 10020 : free(new_query);
387 53932 : } else if (!IS_TDS7_PLUS(tds->conn) || !params || !params->num_cols) {
388 53908 : if (tds_start_query_head(tds, TDS_QUERY, head) != TDS_SUCCESS)
389 : return TDS_FAIL;
390 53908 : tds_put_string(tds, query, (int)query_len);
391 : } else {
392 : TDSCOLUMN *param;
393 : int count, i;
394 : size_t converted_query_len;
395 : const char *converted_query;
396 : TDSFREEZE outer;
397 : TDSRET rc;
398 :
399 24 : converted_query =
400 24 : tds_convert_string(tds, tds->conn->char_convs[client2ucs2],
401 : query, query_len, &converted_query_len);
402 24 : if (!converted_query) {
403 0 : tds_set_state(tds, TDS_IDLE);
404 0 : return TDS_FAIL;
405 : }
406 :
407 48 : count = tds_count_placeholders_ucs2le(converted_query, converted_query + converted_query_len);
408 :
409 24 : if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
410 : tds_convert_string_free(query, converted_query);
411 : return TDS_FAIL;
412 : }
413 :
414 24 : tds_freeze(tds, &outer, 0);
415 :
416 : /* procedure name */
417 24 : if (IS_TDS71_PLUS(tds->conn)) {
418 24 : tds_put_smallint(tds, -1);
419 24 : tds_put_smallint(tds, TDS_SP_EXECUTESQL);
420 : } else {
421 0 : TDS_PUT_N_AS_UCS2(tds, "sp_executesql");
422 : }
423 24 : tds_put_smallint(tds, 0);
424 :
425 : /* string with sql statement */
426 24 : if (!count) {
427 16 : tds_put_byte(tds, 0);
428 16 : tds_put_byte(tds, 0);
429 16 : tds_put_byte(tds, SYBNTEXT); /* must be Ntype */
430 16 : TDS_PUT_INT(tds, converted_query_len);
431 16 : if (IS_TDS71_PLUS(tds->conn))
432 16 : tds_put_n(tds, tds->conn->collation, 5);
433 16 : TDS_PUT_INT(tds, converted_query_len);
434 16 : tds_put_n(tds, converted_query, converted_query_len);
435 :
436 16 : rc = tds7_write_param_def_from_params(tds, converted_query, converted_query_len, params);
437 : } else {
438 8 : tds7_put_query_params(tds, converted_query, converted_query_len);
439 :
440 8 : rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
441 : }
442 24 : tds_convert_string_free(query, converted_query);
443 24 : if (TDS_FAILED(rc)) {
444 0 : tds_freeze_abort(&outer);
445 0 : return rc;
446 : }
447 24 : tds_freeze_close(&outer);
448 :
449 168 : for (i = 0; i < num_params; i++) {
450 144 : param = params->columns[i];
451 144 : TDS_PROPAGATE(tds_put_data_info(tds, param, 0));
452 144 : TDS_PROPAGATE(tds_put_data(tds, param));
453 : }
454 24 : tds->current_op = TDS_OP_EXECUTESQL;
455 : }
456 63952 : return tds_query_flush_packet(tds);
457 : }
458 :
459 : /**
460 : * Format and submit a query
461 : * \tds
462 : * \param queryf query format. printf like expansion is performed on
463 : * this query.
464 : */
465 : TDSRET
466 1270 : tds_submit_queryf(TDSSOCKET * tds, const char *queryf, ...)
467 : {
468 : va_list ap;
469 1270 : char *query = NULL;
470 1270 : TDSRET rc = TDS_FAIL;
471 :
472 1270 : CHECK_TDS_EXTRA(tds);
473 :
474 1270 : va_start(ap, queryf);
475 1270 : if (vasprintf(&query, queryf, ap) >= 0) {
476 2540 : rc = tds_submit_query(tds, query);
477 1270 : free(query);
478 : }
479 1270 : va_end(ap);
480 1270 : return rc;
481 : }
482 :
483 : /**
484 : * Skip a comment in a query
485 : * \param s start of the string (or part of it)
486 : * \returns pointer to end of comment
487 : */
488 : const char *
489 960 : tds_skip_comment(const char *s)
490 : {
491 960 : const char *p = s;
492 :
493 960 : if (*p == '-' && p[1] == '-') {
494 218856 : for (;*++p != '\0';)
495 218836 : if (*p == '\n')
496 404 : return p + 1;
497 536 : } else if (*p == '/' && p[1] == '*') {
498 100 : ++p;
499 2066 : for(;*++p != '\0';)
500 1936 : if (*p == '*' && p[1] == '/')
501 70 : return p + 2;
502 : } else
503 436 : ++p;
504 :
505 : return p;
506 : }
507 :
508 : /**
509 : * Skip quoting string (like 'sfsf', "dflkdj" or [dfkjd])
510 : * \param s pointer to first quoting character. @verbatim Should be ', " or [. @endverbatim
511 : * \return character after quoting
512 : */
513 : const char *
514 13928 : tds_skip_quoted(const char *s)
515 : {
516 32378 : const char *p = s;
517 32378 : char quote = (*s == '[') ? ']' : *s;
518 :
519 394507 : for (; *++p;) {
520 380579 : if (*p == quote) {
521 34146 : if (*++p != quote)
522 : return p;
523 : }
524 : }
525 : return p;
526 : }
527 :
528 : /**
529 : * Get position of next placeholder
530 : * \param start pointer to part of query to search
531 : * \return next placeholder or NULL if not found
532 : */
533 : const char *
534 53315 : tds_next_placeholder(const char *start)
535 : {
536 53315 : const char *p = start;
537 :
538 53315 : if (!p)
539 : return NULL;
540 :
541 : for (;;) {
542 2046891 : switch (*p) {
543 : case '\0':
544 : return NULL;
545 18220 : case '\'':
546 : case '\"':
547 : case '[':
548 : p = tds_skip_quoted(p);
549 : break;
550 :
551 730 : case '-':
552 : case '/':
553 730 : p = tds_skip_comment(p);
554 730 : break;
555 :
556 : case '?':
557 : return p;
558 1974626 : default:
559 1974626 : ++p;
560 1974626 : break;
561 : }
562 : }
563 : }
564 :
565 : /**
566 : * Count the number of placeholders ('?') in a query
567 : * \param query query string
568 : */
569 : int
570 32191 : tds_count_placeholders(const char *query)
571 : {
572 33033 : const char *p = query - 1;
573 33033 : int count = 0;
574 :
575 7900 : for (;; ++count) {
576 47349 : if (!(p = tds_next_placeholder(p + 1)))
577 32191 : return count;
578 : }
579 : }
580 :
581 : /**
582 : * Skip a comment in a query
583 : * \param s start of the string (or part of it). Encoded in ucs2le
584 : * \param end end of string
585 : * \returns pointer to end of comment
586 : */
587 : static const char *
588 128 : tds_skip_comment_ucs2le(const char *s, const char *end)
589 : {
590 128 : const char *p = s;
591 :
592 128 : if (p+4 <= end && memcmp(p, "-\0-", 4) == 0) {
593 120100 : for (;(p+=2) < end;)
594 120080 : if (p[0] == '\n' && p[1] == 0)
595 34 : return p + 2;
596 74 : } else if (p+4 <= end && memcmp(p, "/\0*", 4) == 0) {
597 50 : p += 2;
598 50 : end -= 2;
599 160 : for(;(p+=2) < end;)
600 80 : if (memcmp(p, "*\0/", 4) == 0)
601 20 : return p + 4;
602 : return end + 2;
603 : } else
604 24 : p += 2;
605 :
606 : return p;
607 : }
608 :
609 :
610 : /**
611 : * Return pointer to end of a quoted string.
612 : * At the beginning pointer should point to delimiter.
613 : * \param s start of string to skip encoded in ucs2le
614 : * \param end pointer to end of string
615 : */
616 : static const char *
617 710 : tds_skip_quoted_ucs2le(const char *s, const char *end)
618 : {
619 710 : const char *p = s;
620 710 : char quote = (*s == '[') ? ']' : *s;
621 :
622 710 : assert(s[1] == 0 && s < end && (end - s) % 2 == 0);
623 :
624 3434 : for (; (p += 2) != end;) {
625 3434 : if (p[0] == quote && !p[1]) {
626 830 : p += 2;
627 830 : if (p == end || p[0] != quote || p[1])
628 : return p;
629 : }
630 : }
631 : return p;
632 : }
633 :
634 : /**
635 : * Found the next placeholder (? or \@param) in a string.
636 : * String must be encoded in ucs2le.
637 : * \param start start of the string (or part of it)
638 : * \param end end of string
639 : * \param named true if named parameters should be returned
640 : * \returns either start of next placeholder or end if not found
641 : */
642 : static const char *
643 28890 : tds_next_placeholder_ucs2le(const char *start, const char *end, int named)
644 : {
645 28890 : const char *p = start;
646 28890 : char prev = ' ', c;
647 :
648 28890 : assert(p && start <= end && (end - start) % 2 == 0);
649 :
650 504102 : for (; p != end;) {
651 490128 : if (p[1]) {
652 288 : prev = ' ';
653 288 : p += 2;
654 288 : continue;
655 : }
656 489840 : c = p[0];
657 489840 : switch (c) {
658 480 : case '\'':
659 : case '\"':
660 : case '[':
661 480 : p = tds_skip_quoted_ucs2le(p, end);
662 480 : break;
663 :
664 48 : case '-':
665 : case '/':
666 48 : p = tds_skip_comment_ucs2le(p, end);
667 48 : c = ' ';
668 48 : break;
669 :
670 : case '?':
671 : return p;
672 192 : case '@':
673 192 : if (named && !isalnum((unsigned char) prev))
674 : return p;
675 : default:
676 474396 : p += 2;
677 474396 : break;
678 : }
679 : prev = c;
680 : }
681 : return end;
682 : }
683 :
684 : /**
685 : * Count the number of placeholders ('?') in a query
686 : * \param query query encoded in ucs2le
687 : * \param query_end end of query
688 : * \return number of placeholders found
689 : */
690 : static int
691 : tds_count_placeholders_ucs2le(const char *query, const char *query_end)
692 : {
693 9324 : const char *p = query - 2;
694 9324 : int count = 0;
695 :
696 9928 : for (;; ++count) {
697 19252 : if ((p = tds_next_placeholder_ucs2le(p + 2, query_end, 0)) == query_end)
698 : return count;
699 : }
700 : }
701 :
702 : static const char*
703 : tds50_char_declaration_from_usertype(TDSSOCKET *tds, TDS_INT usertype, unsigned int *p_size)
704 : {
705 0 : switch (usertype) {
706 : case USER_CHAR_TYPE:
707 : return "CHAR(%u)";
708 0 : case USER_VARCHAR_TYPE:
709 : return "VARCHAR(%u)";
710 0 : case USER_SYSNAME_TYPE:
711 : return "SYSNAME";
712 0 : case USER_NCHAR_TYPE:
713 0 : *p_size /= tds->conn->ncharsize;
714 : return "NCHAR(%u)";
715 0 : case USER_NVARCHAR_TYPE:
716 0 : *p_size /= tds->conn->ncharsize;
717 : return "NVARCHAR(%u)";
718 : }
719 : return NULL;
720 : }
721 :
722 : /**
723 : * Return declaration for column (like "varchar(20)").
724 : *
725 : * This depends on:
726 : * - on_server.column_type
727 : * - varint_size (for varchar(max) distinction)
728 : * - column_size
729 : * - precision/scale (numeric)
730 : *
731 : * \tds
732 : * \param curcol column
733 : * \param out buffer to hold declaration
734 : * \return TDS_FAIL or TDS_SUCCESS
735 : */
736 : TDSRET
737 8316 : tds_get_column_declaration(TDSSOCKET * tds, TDSCOLUMN * curcol, char *out)
738 : {
739 8316 : const char *fmt = NULL;
740 : /* unsigned int is required by printf format, don't use size_t */
741 8316 : unsigned int max_len = IS_TDS7_PLUS(tds->conn) ? 8000 : 255;
742 : unsigned int size;
743 : TDS_SERVER_TYPE conversion_type;
744 :
745 8316 : CHECK_TDS_EXTRA(tds);
746 8316 : CHECK_COLUMN_EXTRA(curcol);
747 :
748 8316 : size = (unsigned int) tds_fix_column_size(tds, curcol);
749 :
750 8316 : conversion_type = tds_get_conversion_type(curcol->on_server.column_type, curcol->on_server.column_size);
751 8316 : switch (conversion_type) {
752 318 : case XSYBCHAR:
753 318 : if (IS_TDS50(tds->conn)) {
754 0 : max_len = 32767;
755 0 : fmt = tds50_char_declaration_from_usertype(tds, curcol->column_usertype, &size);
756 : if (fmt != NULL)
757 : break;
758 : }
759 : case SYBCHAR:
760 : fmt = "CHAR(%u)";
761 : break;
762 10 : case SYBVARCHAR:
763 10 : if (IS_TDS50(tds->conn)) {
764 0 : fmt = tds50_char_declaration_from_usertype(tds, curcol->column_usertype, &size);
765 : if (fmt != NULL)
766 : break;
767 : }
768 : case XSYBVARCHAR:
769 1522 : if (curcol->column_varint_size == 8)
770 : fmt = "VARCHAR(MAX)";
771 : else
772 1206 : fmt = "VARCHAR(%u)";
773 : break;
774 172 : case SYBUINT1:
775 : case SYBINT1:
776 172 : fmt = "TINYINT";
777 172 : break;
778 150 : case SYBINT2:
779 150 : fmt = "SMALLINT";
780 150 : break;
781 906 : case SYBINT4:
782 906 : fmt = "INT";
783 906 : break;
784 98 : case SYBINT8:
785 : case SYB5INT8:
786 : /* TODO even for Sybase ?? */
787 98 : fmt = "BIGINT";
788 98 : break;
789 406 : case SYBFLT8:
790 406 : fmt = "FLOAT";
791 406 : break;
792 394 : case SYBDATETIME:
793 394 : fmt = "DATETIME";
794 394 : break;
795 30 : case SYBDATE:
796 30 : fmt = "DATE";
797 30 : break;
798 30 : case SYBTIME:
799 30 : fmt = "TIME";
800 30 : break;
801 336 : case SYBBIT:
802 336 : fmt = "BIT";
803 336 : break;
804 378 : case SYBTEXT:
805 378 : fmt = "TEXT";
806 378 : break;
807 148 : case SYBLONGBINARY: /* TODO correct ?? */
808 : case SYBIMAGE:
809 148 : if (IS_TDS50(tds->conn)) {
810 10 : switch (curcol->column_usertype) {
811 0 : case USER_UNICHAR_TYPE:
812 0 : size /= 2u;
813 0 : max_len = 8192;
814 0 : fmt = "UNICHAR(%u)";
815 0 : break;
816 0 : case USER_UNIVARCHAR_TYPE:
817 0 : size /= 2u;
818 0 : max_len = 8192;
819 0 : fmt = "UNIVARCHAR(%u)";
820 0 : break;
821 0 : case USER_UNITEXT_TYPE:
822 0 : fmt = "UNITEXT";
823 0 : break;
824 : }
825 10 : if (fmt != NULL)
826 : break;
827 : }
828 : fmt = "IMAGE";
829 : break;
830 94 : case SYBMONEY4:
831 94 : fmt = "SMALLMONEY";
832 94 : break;
833 128 : case SYBMONEY:
834 128 : fmt = "MONEY";
835 128 : break;
836 94 : case SYBDATETIME4:
837 94 : fmt = "SMALLDATETIME";
838 94 : break;
839 334 : case SYBREAL:
840 334 : fmt = "REAL";
841 334 : break;
842 44 : case SYBBINARY:
843 : case XSYBBINARY:
844 44 : fmt = "BINARY(%u)";
845 44 : break;
846 352 : case SYBVARBINARY:
847 : case XSYBVARBINARY:
848 352 : if (curcol->column_varint_size == 8)
849 : fmt = "VARBINARY(MAX)";
850 : else
851 252 : fmt = "VARBINARY(%u)";
852 : break;
853 : case SYBNUMERIC:
854 : fmt = "NUMERIC(%d,%d)";
855 : goto numeric_decimal;
856 78 : case SYBDECIMAL:
857 78 : fmt = "DECIMAL(%d,%d)";
858 216 : numeric_decimal:
859 216 : sprintf(out, fmt, curcol->column_prec, curcol->column_scale);
860 216 : return TDS_SUCCESS;
861 : break;
862 62 : case SYBUNIQUE:
863 62 : if (IS_TDS7_PLUS(tds->conn))
864 62 : fmt = "UNIQUEIDENTIFIER";
865 : break;
866 236 : case SYBNTEXT:
867 236 : if (IS_TDS7_PLUS(tds->conn))
868 236 : fmt = "NTEXT";
869 : break;
870 636 : case SYBNVARCHAR:
871 : case XSYBNVARCHAR:
872 636 : if (curcol->column_varint_size == 8) {
873 : fmt = "NVARCHAR(MAX)";
874 396 : } else if (IS_TDS7_PLUS(tds->conn)) {
875 396 : fmt = "NVARCHAR(%u)";
876 396 : max_len = 4000;
877 396 : size /= 2u;
878 : }
879 : break;
880 240 : case XSYBNCHAR:
881 240 : if (IS_TDS7_PLUS(tds->conn)) {
882 240 : fmt = "NCHAR(%u)";
883 240 : max_len = 4000;
884 240 : size /= 2u;
885 : }
886 : break;
887 490 : case SYBVARIANT:
888 490 : if (IS_TDS7_PLUS(tds->conn))
889 490 : fmt = "SQL_VARIANT";
890 : break;
891 : /* TODO support scale !! */
892 42 : case SYBMSTIME:
893 42 : fmt = "TIME";
894 42 : break;
895 42 : case SYBMSDATE:
896 42 : fmt = "DATE";
897 42 : break;
898 278 : case SYBMSDATETIME2:
899 278 : fmt = "DATETIME2";
900 278 : break;
901 18 : case SYBMSDATETIMEOFFSET:
902 18 : fmt = "DATETIMEOFFSET";
903 18 : break;
904 10 : case SYB5BIGTIME:
905 10 : fmt = "BIGTIME";
906 10 : break;
907 10 : case SYB5BIGDATETIME:
908 10 : fmt = "BIGDATETIME";
909 10 : break;
910 30 : case SYBUINT2:
911 30 : fmt = "UNSIGNED SMALLINT";
912 30 : break;
913 30 : case SYBUINT4:
914 30 : fmt = "UNSIGNED INT";
915 30 : break;
916 30 : case SYBUINT8:
917 30 : fmt = "UNSIGNED BIGINT";
918 30 : break;
919 0 : case SYBXML:
920 : case SYBMSXML:
921 0 : fmt = "XML";
922 0 : break;
923 0 : case SYBUNITEXT:
924 0 : fmt = "UNITEXT";
925 0 : break;
926 : /* nullable types should not occur here... */
927 : case SYBFLTN:
928 : case SYBMONEYN:
929 : case SYBDATETIMN:
930 : case SYBBITN:
931 : case SYBINTN:
932 : case SYBUINTN:
933 : case SYBDATEN:
934 : case SYBTIMEN:
935 0 : assert(0);
936 : /* TODO... */
937 : #if ENABLE_EXTRA_CHECKS
938 : case SYBVOID:
939 : case SYBSINT1:
940 : case SYBMSUDT:
941 : case SYBMSTABLE:
942 : case SYBINTERVAL:
943 : #else
944 : default:
945 : #endif
946 0 : tdsdump_log(TDS_DBG_ERROR, "Unknown type %d\n", conversion_type);
947 : break;
948 : }
949 :
950 8100 : if (fmt) {
951 : /* fill out */
952 8100 : sprintf(out, fmt, size > 0 ? TDS_MIN(size, max_len) : 1u);
953 8100 : return TDS_SUCCESS;
954 : }
955 :
956 0 : out[0] = 0;
957 0 : return TDS_FAIL;
958 : }
959 :
960 : /**
961 : * Write string with parameters definition, useful for TDS7+.
962 : * Looks like "@P1 INT, @P2 VARCHAR(100)"
963 : * \param tds state information for the socket and the TDS protocol
964 : * \param converted_query query to send to server in ucs2le encoding
965 : * \param converted_query_len query length in bytes
966 : * \param params parameters to build declaration
967 : * \return result of write
968 : */
969 : /* TODO find a better name for this function */
970 : static TDSRET
971 4650 : tds7_write_param_def_from_query(TDSSOCKET * tds, const char* converted_query, size_t converted_query_len, TDSPARAMINFO * params)
972 : {
973 : char declaration[128], *p;
974 : int i, count;
975 : unsigned int written;
976 : TDSFREEZE outer, inner;
977 :
978 4650 : assert(IS_TDS7_PLUS(tds->conn));
979 :
980 4650 : CHECK_TDS_EXTRA(tds);
981 4650 : if (params)
982 4120 : CHECK_PARAMINFO_EXTRA(params);
983 :
984 9300 : count = tds_count_placeholders_ucs2le(converted_query, converted_query + converted_query_len);
985 :
986 : /* string with parameters types */
987 4650 : tds_put_byte(tds, 0);
988 4650 : tds_put_byte(tds, 0);
989 4650 : tds_put_byte(tds, SYBNTEXT); /* must be Ntype */
990 :
991 : /* put parameters definitions */
992 4650 : tds_freeze(tds, &outer, 4);
993 4650 : if (IS_TDS71_PLUS(tds->conn))
994 4650 : tds_put_n(tds, tds->conn->collation, 5);
995 4650 : tds_freeze(tds, &inner, 4);
996 :
997 9590 : for (i = 0; i < count; ++i) {
998 4940 : p = declaration;
999 4940 : if (i)
1000 612 : *p++ = ',';
1001 :
1002 : /* get this parameter declaration */
1003 4940 : p += sprintf(p, "@P%d ", i + 1);
1004 4940 : if (!params || i >= params->num_cols) {
1005 : /* type unknown, use a type able to hold any value */
1006 384 : strcpy(p, IS_TDS72_PLUS(tds->conn) ? "varchar(max)" : "varchar(4000)");
1007 4556 : } else if (TDS_FAILED(tds_get_column_declaration(tds, params->columns[i], p))) {
1008 0 : tds_freeze_abort(&inner);
1009 0 : tds_freeze_abort(&outer);
1010 0 : return TDS_FAIL;
1011 : }
1012 :
1013 4940 : tds_put_string(tds, declaration, -1);
1014 : }
1015 :
1016 4650 : written = tds_freeze_written(&inner) - 4;
1017 4650 : tds_freeze_close_len(&inner, written ? written : -1);
1018 4650 : tds_freeze_close_len(&outer, written);
1019 4650 : return TDS_SUCCESS;
1020 : }
1021 :
1022 : /**
1023 : * Write string with parameters definition, useful for TDS7+.
1024 : * Looks like "@P1 INT, @P2 VARCHAR(100)"
1025 : * \param tds state information for the socket and the TDS protocol
1026 : * \param query query to send to server encoded in ucs2le
1027 : * \param query_len query length in bytes
1028 : * \param params parameters to build declaration
1029 : * \return result of the operation
1030 : */
1031 : /* TODO find a better name for this function */
1032 : static TDSRET
1033 16 : tds7_write_param_def_from_params(TDSSOCKET * tds, const char* query, size_t query_len, TDSPARAMINFO * params)
1034 : {
1035 : char declaration[40];
1036 : int i;
1037 : struct tds_ids {
1038 : const char *p;
1039 : size_t len;
1040 16 : } *ids = NULL;
1041 : TDSFREEZE outer, inner;
1042 : unsigned int written;
1043 :
1044 16 : assert(IS_TDS7_PLUS(tds->conn));
1045 :
1046 16 : CHECK_TDS_EXTRA(tds);
1047 16 : if (params)
1048 16 : CHECK_PARAMINFO_EXTRA(params);
1049 :
1050 : /* string with parameters types */
1051 16 : tds_put_byte(tds, 0);
1052 16 : tds_put_byte(tds, 0);
1053 16 : tds_put_byte(tds, SYBNTEXT); /* must be Ntype */
1054 :
1055 : /* put parameters definitions */
1056 16 : tds_freeze(tds, &outer, 4);
1057 16 : if (IS_TDS71_PLUS(tds->conn))
1058 16 : tds_put_n(tds, tds->conn->collation, 5);
1059 16 : tds_freeze(tds, &inner, 4);
1060 :
1061 16 : if (!params || !params->num_cols) {
1062 0 : tds_freeze_close_len(&inner, -1);
1063 0 : tds_freeze_close_len(&outer, 0);
1064 0 : return TDS_SUCCESS;
1065 : }
1066 :
1067 : /* try to detect missing names */
1068 16 : ids = tds_new0(struct tds_ids, params->num_cols);
1069 16 : if (!ids)
1070 : goto Cleanup;
1071 32 : if (tds_dstr_isempty(¶ms->columns[0]->column_name)) {
1072 8 : const char *s = query, *e, *id_end;
1073 8 : const char *query_end = query + query_len;
1074 :
1075 56 : for (i = 0; i < params->num_cols; s = e + 2) {
1076 48 : e = tds_next_placeholder_ucs2le(s, query_end, 1);
1077 48 : if (e == query_end)
1078 : break;
1079 48 : if (e[0] != '@')
1080 0 : continue;
1081 : /* find end of param name */
1082 304 : for (id_end = e + 2; id_end != query_end; id_end += 2)
1083 304 : if (!id_end[1] && (id_end[0] != '_' && id_end[1] != '#' && !isalnum((unsigned char) id_end[0])))
1084 : break;
1085 48 : ids[i].p = e;
1086 48 : ids[i].len = id_end - e;
1087 48 : ++i;
1088 : }
1089 : }
1090 :
1091 96 : for (i = 0; i < params->num_cols; ++i) {
1092 96 : if (i)
1093 80 : tds_put_smallint(tds, ',');
1094 :
1095 : /* this part of buffer can be not-ascii compatible, use all ucs2... */
1096 96 : if (ids[i].p) {
1097 48 : tds_put_n(tds, ids[i].p, ids[i].len);
1098 : } else {
1099 48 : const DSTR *name = ¶ms->columns[i]->column_name;
1100 144 : tds_put_string(tds, tds_dstr_cstr(name), (int) tds_dstr_len(name));
1101 : }
1102 96 : tds_put_smallint(tds, ' ');
1103 :
1104 : /* get this parameter declaration */
1105 96 : tds_get_column_declaration(tds, params->columns[i], declaration);
1106 96 : if (!declaration[0])
1107 : goto Cleanup;
1108 96 : tds_put_string(tds, declaration, -1);
1109 : }
1110 16 : free(ids);
1111 :
1112 16 : written = tds_freeze_written(&inner) - 4;
1113 16 : tds_freeze_close_len(&inner, written);
1114 16 : tds_freeze_close_len(&outer, written);
1115 :
1116 16 : return TDS_SUCCESS;
1117 :
1118 0 : Cleanup:
1119 0 : free(ids);
1120 0 : tds_freeze_abort(&inner);
1121 0 : tds_freeze_abort(&outer);
1122 0 : return TDS_FAIL;
1123 : }
1124 :
1125 :
1126 : /**
1127 : * Output params types and query (required by sp_prepare/sp_executesql/sp_prepexec)
1128 : * \param tds state information for the socket and the TDS protocol
1129 : * \param query query (encoded in ucs2le)
1130 : * \param query_len query length in bytes
1131 : */
1132 : static void
1133 4650 : tds7_put_query_params(TDSSOCKET * tds, const char *query, size_t query_len)
1134 : {
1135 : size_t len;
1136 : int i, num_placeholders;
1137 : const char *s, *e;
1138 : char buf[24];
1139 4650 : const char *const query_end = query + query_len;
1140 :
1141 4650 : CHECK_TDS_EXTRA(tds);
1142 :
1143 4650 : assert(IS_TDS7_PLUS(tds->conn));
1144 :
1145 : /* we use all "@PX" for parameters */
1146 4650 : num_placeholders = tds_count_placeholders_ucs2le(query, query_end);
1147 4650 : len = num_placeholders * 2;
1148 : /* adjust for the length of X */
1149 4666 : for (i = 10; i <= num_placeholders; i *= 10) {
1150 16 : len += num_placeholders - i + 1;
1151 : }
1152 :
1153 : /* string with sql statement */
1154 : /* replace placeholders with dummy parametes */
1155 4650 : tds_put_byte(tds, 0);
1156 4650 : tds_put_byte(tds, 0);
1157 4650 : tds_put_byte(tds, SYBNTEXT); /* must be Ntype */
1158 4650 : len = 2u * len + query_len;
1159 4650 : TDS_PUT_INT(tds, len);
1160 4650 : if (IS_TDS71_PLUS(tds->conn))
1161 4650 : tds_put_n(tds, tds->conn->collation, 5);
1162 4650 : TDS_PUT_INT(tds, len);
1163 4650 : s = query;
1164 : /* TODO do a test with "...?" and "...?)" */
1165 9590 : for (i = 1;; ++i) {
1166 14530 : e = tds_next_placeholder_ucs2le(s, query_end, 0);
1167 9590 : assert(e && query <= e && e <= query_end);
1168 9590 : tds_put_n(tds, s, e - s);
1169 9590 : if (e == query_end)
1170 : break;
1171 4940 : sprintf(buf, "@P%d", i);
1172 4940 : tds_put_string(tds, buf, -1);
1173 4940 : s = e + 2;
1174 : }
1175 4650 : }
1176 :
1177 : /**
1178 : * Creates a temporary stored procedure in the server.
1179 : *
1180 : * Under TDS 4.2 dynamic statements are emulated building sql command.
1181 : * TDS 5 does not uses parameters type.
1182 : * TDS 7+ uses parameter types to prepare the query. You should
1183 : * prepare again the query if parameters changes.
1184 : * \param tds state information for the socket and the TDS protocol
1185 : * \param query language query with given placeholders (?)
1186 : * \param id string to identify the dynamic query. Pass NULL for automatic generation.
1187 : * \param dyn_out will receive allocated TDSDYNAMIC*. Any older allocated dynamic won't be freed, Can be NULL.
1188 : * \param params parameters to use. It can be NULL even if parameters are present. Used only for TDS7+
1189 : * \return TDS_FAIL or TDS_SUCCESS
1190 : */
1191 : /* TODO parse all results ?? */
1192 : TDSRET
1193 692 : tds_submit_prepare(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params)
1194 : {
1195 : int query_len;
1196 692 : TDSRET rc = TDS_FAIL;
1197 : TDSDYNAMIC *dyn;
1198 :
1199 692 : CHECK_TDS_EXTRA(tds);
1200 692 : if (params)
1201 64 : CHECK_PARAMINFO_EXTRA(params);
1202 :
1203 692 : if (!query || !dyn_out)
1204 : return TDS_FAIL;
1205 :
1206 692 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1207 : return TDS_FAIL;
1208 :
1209 : /* allocate a structure for this thing */
1210 692 : dyn = tds_alloc_dynamic(tds->conn, id);
1211 692 : if (!dyn)
1212 : return TDS_FAIL;
1213 692 : tds_release_dynamic(dyn_out);
1214 692 : *dyn_out = dyn;
1215 692 : tds_release_cur_dyn(tds);
1216 :
1217 : /* TDS5 sometimes cannot accept prepare so we need to store query */
1218 692 : if (!IS_TDS7_PLUS(tds->conn)) {
1219 364 : dyn->query = strdup(query);
1220 364 : if (!dyn->query)
1221 : goto failure;
1222 : }
1223 :
1224 692 : if (!IS_TDS50(tds->conn) && !IS_TDS7_PLUS(tds->conn)) {
1225 0 : dyn->emulated = true;
1226 0 : tds_dynamic_deallocated(tds->conn, dyn);
1227 0 : tds_set_state(tds, TDS_IDLE);
1228 0 : return TDS_SUCCESS;
1229 : }
1230 :
1231 692 : query_len = (int)strlen(query);
1232 :
1233 692 : tds_set_cur_dyn(tds, dyn);
1234 :
1235 692 : if (IS_TDS7_PLUS(tds->conn)) {
1236 : size_t converted_query_len;
1237 : const char *converted_query;
1238 : TDSFREEZE outer;
1239 : TDSRET rc;
1240 :
1241 328 : converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, query_len, &converted_query_len);
1242 328 : if (!converted_query)
1243 : goto failure;
1244 :
1245 328 : tds_freeze(tds, &outer, 0);
1246 328 : tds_start_query(tds, TDS_RPC);
1247 : /* procedure name */
1248 328 : if (IS_TDS71_PLUS(tds->conn)) {
1249 328 : tds_put_smallint(tds, -1);
1250 328 : tds_put_smallint(tds, TDS_SP_PREPARE);
1251 : } else {
1252 0 : TDS_PUT_N_AS_UCS2(tds, "sp_prepare");
1253 : }
1254 328 : tds_put_smallint(tds, 0);
1255 :
1256 : /* return param handle (int) */
1257 328 : tds_put_byte(tds, 0);
1258 328 : tds_put_byte(tds, 1); /* result */
1259 328 : tds_put_byte(tds, SYBINTN);
1260 328 : tds_put_byte(tds, 4);
1261 328 : tds_put_byte(tds, 0);
1262 :
1263 328 : rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
1264 328 : tds7_put_query_params(tds, converted_query, converted_query_len);
1265 328 : tds_convert_string_free(query, converted_query);
1266 328 : if (TDS_FAILED(rc)) {
1267 0 : tds_freeze_abort(&outer);
1268 0 : return rc;
1269 : }
1270 328 : tds_freeze_close(&outer);
1271 :
1272 : /* options, 1 == RETURN_METADATA */
1273 328 : tds_put_byte(tds, 0);
1274 328 : tds_put_byte(tds, 0);
1275 328 : tds_put_byte(tds, SYBINTN);
1276 328 : tds_put_byte(tds, 4);
1277 328 : tds_put_byte(tds, 4);
1278 328 : tds_put_int(tds, 1);
1279 :
1280 328 : tds->current_op = TDS_OP_PREPARE;
1281 : } else {
1282 364 : tds->out_flag = TDS_NORMAL;
1283 :
1284 364 : tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
1285 364 : TDS_START_LEN_USMALLINT(tds) {
1286 364 : tds_put_byte(tds, TDS_DYN_PREPARE);
1287 364 : tds_put_byte(tds, 0x00);
1288 364 : TDS_START_LEN_TINYINT(tds) {
1289 364 : tds_put_string(tds, dyn->id, -1);
1290 364 : } TDS_END_LEN
1291 :
1292 : /* TODO how to pass parameters type? like store procedures ? */
1293 364 : TDS_START_LEN_USMALLINT(tds) {
1294 728 : if (tds_capability_has_req(tds->conn, TDS_REQ_PROTO_DYNPROC)) {
1295 364 : tds_put_n(tds, "create proc ", 12);
1296 364 : tds_put_string(tds, dyn->id, -1);
1297 364 : tds_put_n(tds, " as ", 4);
1298 : }
1299 364 : tds_put_string(tds, query, query_len);
1300 364 : } TDS_END_LEN
1301 364 : } TDS_END_LEN
1302 : }
1303 :
1304 692 : rc = tds_query_flush_packet(tds);
1305 692 : if (TDS_SUCCEED(rc))
1306 : return rc;
1307 :
1308 20 : failure:
1309 : /* TODO correct if writing fail ?? */
1310 20 : tds_set_state(tds, TDS_IDLE);
1311 :
1312 20 : tds_release_dynamic(dyn_out);
1313 20 : tds_dynamic_deallocated(tds->conn, dyn);
1314 20 : return rc;
1315 : }
1316 :
1317 : /**
1318 : * Submit a prepared query with parameters
1319 : * \param tds state information for the socket and the TDS protocol
1320 : * \param query language query with given placeholders (?)
1321 : * \param params parameters to send
1322 : * \return TDS_FAIL or TDS_SUCCESS
1323 : */
1324 : TDSRET
1325 814 : tds_submit_execdirect(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head)
1326 : {
1327 : size_t query_len;
1328 : TDSCOLUMN *param;
1329 : TDSDYNAMIC *dyn;
1330 : unsigned int id_len;
1331 : TDSFREEZE outer;
1332 :
1333 814 : CHECK_TDS_EXTRA(tds);
1334 814 : CHECK_PARAMINFO_EXTRA(params);
1335 :
1336 814 : if (!query)
1337 : return TDS_FAIL;
1338 814 : query_len = strlen(query);
1339 :
1340 814 : if (IS_TDS7_PLUS(tds->conn)) {
1341 : int i;
1342 : size_t converted_query_len;
1343 : const char *converted_query;
1344 : TDSRET rc;
1345 :
1346 702 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1347 : return TDS_FAIL;
1348 :
1349 702 : converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, (int)query_len, &converted_query_len);
1350 702 : if (!converted_query) {
1351 0 : tds_set_state(tds, TDS_IDLE);
1352 0 : return TDS_FAIL;
1353 : }
1354 :
1355 702 : if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
1356 : tds_convert_string_free(query, converted_query);
1357 : return TDS_FAIL;
1358 : }
1359 702 : tds_freeze(tds, &outer, 0);
1360 : /* procedure name */
1361 702 : if (IS_TDS71_PLUS(tds->conn)) {
1362 702 : tds_put_smallint(tds, -1);
1363 702 : tds_put_smallint(tds, TDS_SP_EXECUTESQL);
1364 : } else {
1365 0 : TDS_PUT_N_AS_UCS2(tds, "sp_executesql");
1366 : }
1367 702 : tds_put_smallint(tds, 0);
1368 :
1369 702 : tds7_put_query_params(tds, converted_query, converted_query_len);
1370 702 : rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
1371 702 : tds_convert_string_free(query, converted_query);
1372 702 : if (TDS_FAILED(rc)) {
1373 0 : tds_freeze_abort(&outer);
1374 0 : return rc;
1375 : }
1376 702 : tds_freeze_close(&outer);
1377 :
1378 1424 : for (i = 0; i < params->num_cols; i++) {
1379 722 : param = params->columns[i];
1380 722 : TDS_PROPAGATE(tds_put_data_info(tds, param, 0));
1381 722 : TDS_PROPAGATE(tds_put_data(tds, param));
1382 : }
1383 :
1384 702 : tds->current_op = TDS_OP_EXECUTESQL;
1385 702 : return tds_query_flush_packet(tds);
1386 : }
1387 :
1388 : /* allocate a structure for this thing */
1389 112 : dyn = tds_alloc_dynamic(tds->conn, NULL);
1390 :
1391 112 : if (!dyn)
1392 : return TDS_FAIL;
1393 : /* check if no parameters */
1394 112 : if (params && !params->num_cols)
1395 0 : params = NULL;
1396 :
1397 : /* TDS 4.2, emulate prepared statements */
1398 : /*
1399 : * TODO Sybase seems to not support parameters in prepared execdirect
1400 : * so use language or prepare and then exec
1401 : */
1402 112 : if (!IS_TDS50(tds->conn) || params) {
1403 112 : TDSRET ret = TDS_SUCCESS;
1404 :
1405 112 : if (!params) {
1406 0 : ret = tds_submit_query(tds, query);
1407 : } else {
1408 112 : dyn->emulated = true;
1409 112 : dyn->params = params;
1410 112 : dyn->query = strdup(query);
1411 112 : if (!dyn->query)
1412 : ret = TDS_FAIL;
1413 : if (TDS_SUCCEED(ret))
1414 112 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1415 : ret = TDS_FAIL;
1416 112 : if (TDS_SUCCEED(ret)) {
1417 112 : ret = tds_send_emulated_execute(tds, dyn->query, dyn->params);
1418 112 : if (TDS_SUCCEED(ret))
1419 112 : ret = tds_query_flush_packet(tds);
1420 : }
1421 : /* do not free our parameters */
1422 112 : dyn->params = NULL;
1423 : }
1424 112 : tds_dynamic_deallocated(tds->conn, dyn);
1425 112 : tds_release_dynamic(&dyn);
1426 112 : return ret;
1427 : }
1428 :
1429 0 : tds_release_cur_dyn(tds);
1430 0 : tds->cur_dyn = dyn;
1431 :
1432 0 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1433 : return TDS_FAIL;
1434 :
1435 0 : tds->out_flag = TDS_NORMAL;
1436 :
1437 0 : id_len = (unsigned int) strlen(dyn->id);
1438 0 : tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
1439 0 : tds_freeze(tds, &outer, 2);
1440 0 : tds_put_byte(tds, TDS_DYN_EXEC_IMMED);
1441 0 : tds_put_byte(tds, params ? 0x01 : 0);
1442 0 : TDS_START_LEN_TINYINT(tds) {
1443 0 : tds_put_string(tds, dyn->id, id_len);
1444 0 : } TDS_END_LEN
1445 : /* TODO how to pass parameters type? like store procedures ? */
1446 0 : TDS_START_LEN_USMALLINT(tds) {
1447 0 : tds_put_n(tds, "create proc ", 12);
1448 0 : tds_put_string(tds, dyn->id, id_len);
1449 0 : tds_put_n(tds, " as ", 4);
1450 0 : tds_put_string(tds, query, query_len);
1451 0 : } TDS_END_LEN
1452 0 : tds_freeze_close(&outer);
1453 :
1454 : if (params)
1455 : TDS_PROPAGATE(tds5_put_params(tds, params, 0));
1456 :
1457 0 : return tds_flush_packet(tds);
1458 : }
1459 :
1460 : /**
1461 : * Creates a temporary stored procedure in the server and execute it.
1462 : * \param tds state information for the socket and the TDS protocol
1463 : * \param query language query with given placeholders ('?')
1464 : * \param id string to identify the dynamic query. Pass NULL for automatic generation.
1465 : * \param dyn_out will receive allocated TDSDYNAMIC*. Any older allocated dynamic won't be freed. Can be NULL.
1466 : * \param params parameters to use. It can be NULL even if parameters are present.
1467 : * \return TDS_FAIL or TDS_SUCCESS
1468 : */
1469 : TDSRET
1470 1846 : tds71_submit_prepexec(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params)
1471 : {
1472 : int query_len;
1473 1846 : TDSRET rc = TDS_FAIL;
1474 : TDSDYNAMIC *dyn;
1475 : size_t converted_query_len;
1476 : const char *converted_query;
1477 : TDSFREEZE outer;
1478 :
1479 1846 : CHECK_TDS_EXTRA(tds);
1480 1846 : if (params)
1481 1580 : CHECK_PARAMINFO_EXTRA(params);
1482 :
1483 1846 : if (!query || !dyn_out || !IS_TDS7_PLUS(tds->conn))
1484 : return TDS_FAIL;
1485 :
1486 1846 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1487 : return TDS_FAIL;
1488 :
1489 : /* allocate a structure for this thing */
1490 1846 : dyn = tds_alloc_dynamic(tds->conn, id);
1491 1846 : if (!dyn)
1492 : return TDS_FAIL;
1493 1846 : tds_release_dynamic(dyn_out);
1494 1846 : *dyn_out = dyn;
1495 :
1496 1846 : tds_set_cur_dyn(tds, dyn);
1497 :
1498 1846 : query_len = (int)strlen(query);
1499 :
1500 1846 : converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, query_len, &converted_query_len);
1501 1846 : if (!converted_query)
1502 : goto failure;
1503 :
1504 1846 : tds_freeze(tds, &outer, 0);
1505 1846 : tds_start_query(tds, TDS_RPC);
1506 : /* procedure name */
1507 1846 : if (IS_TDS71_PLUS(tds->conn)) {
1508 1846 : tds_put_smallint(tds, -1);
1509 1846 : tds_put_smallint(tds, TDS_SP_PREPEXEC);
1510 : } else {
1511 0 : TDS_PUT_N_AS_UCS2(tds, "sp_prepexec");
1512 : }
1513 1846 : tds_put_smallint(tds, 0);
1514 :
1515 : /* return param handle (int) */
1516 1846 : tds_put_byte(tds, 0);
1517 1846 : tds_put_byte(tds, 1); /* result */
1518 1846 : tds_put_byte(tds, SYBINTN);
1519 1846 : tds_put_byte(tds, 4);
1520 1846 : tds_put_byte(tds, 0);
1521 :
1522 1846 : rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
1523 1846 : tds7_put_query_params(tds, converted_query, converted_query_len);
1524 1846 : tds_convert_string_free(query, converted_query);
1525 1846 : if (TDS_FAILED(rc)) {
1526 0 : tds_freeze_abort(&outer);
1527 0 : return rc;
1528 : }
1529 1846 : tds_freeze_close(&outer);
1530 :
1531 1846 : if (params) {
1532 : int i;
1533 :
1534 1892 : for (i = 0; i < params->num_cols; i++) {
1535 1892 : TDSCOLUMN *param = params->columns[i];
1536 1892 : TDS_PROPAGATE(tds_put_data_info(tds, param, 0));
1537 1892 : TDS_PROPAGATE(tds_put_data(tds, param));
1538 : }
1539 : }
1540 :
1541 1846 : tds->current_op = TDS_OP_PREPEXEC;
1542 :
1543 1846 : rc = tds_query_flush_packet(tds);
1544 1846 : if (TDS_SUCCEED(rc))
1545 : return rc;
1546 :
1547 0 : failure:
1548 : /* TODO correct if writing fail ?? */
1549 0 : tds_set_state(tds, TDS_IDLE);
1550 :
1551 0 : tds_release_dynamic(dyn_out);
1552 0 : tds_dynamic_deallocated(tds->conn, dyn);
1553 0 : return rc;
1554 : }
1555 :
1556 : /**
1557 : * Get column size for wire
1558 : */
1559 : size_t
1560 132346 : tds_fix_column_size(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * curcol)
1561 : {
1562 132346 : size_t size = curcol->on_server.column_size, min;
1563 :
1564 132346 : if (!size) {
1565 1390 : size = curcol->column_size;
1566 1390 : if (is_unicode_type(curcol->on_server.column_type))
1567 776 : size *= 2u;
1568 : }
1569 :
1570 132346 : switch (curcol->column_varint_size) {
1571 12058 : case 1:
1572 12058 : size = TDS_CLAMP(size, 1, 255);
1573 : break;
1574 60500 : case 2:
1575 : /* note that varchar(max)/varbinary(max) have a varint of 8 */
1576 60500 : if (curcol->on_server.column_type == XSYBNVARCHAR || curcol->on_server.column_type == XSYBNCHAR)
1577 : min = 2;
1578 : else
1579 57524 : min = 1;
1580 60500 : size = TDS_CLAMP(size, min, 8000u);
1581 : break;
1582 508 : case 4:
1583 508 : if (curcol->on_server.column_type == SYBNTEXT)
1584 : size = 0x7ffffffeu;
1585 : else
1586 508 : size = 0x7fffffffu;
1587 : break;
1588 : default:
1589 : break;
1590 : }
1591 132346 : return size;
1592 : }
1593 :
1594 : /**
1595 : * Put data information to wire
1596 : * \param tds state information for the socket and the TDS protocol
1597 : * \param curcol column where to store information
1598 : * \param flags bit flags on how to send data (use TDS_PUT_DATA_USE_NAME for use name information)
1599 : * \return TDS_SUCCESS or TDS_FAIL
1600 : */
1601 : static TDSRET
1602 9752 : tds_put_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int flags)
1603 : {
1604 : int len;
1605 :
1606 9752 : CHECK_TDS_EXTRA(tds);
1607 9752 : CHECK_COLUMN_EXTRA(curcol);
1608 :
1609 9752 : if (flags & TDS_PUT_DATA_USE_NAME) {
1610 5028 : len = (int) tds_dstr_len(&curcol->column_name);
1611 2514 : tdsdump_log(TDS_DBG_ERROR, "tds_put_data_info putting param_name \n");
1612 :
1613 2514 : if (IS_TDS7_PLUS(tds->conn)) {
1614 2190 : TDS_START_LEN_TINYINT(tds) { /* param name len */
1615 2190 : if ((flags & TDS_PUT_DATA_PREFIX_NAME) != 0)
1616 144 : tds_put_smallint(tds, '@');
1617 4380 : tds_put_string(tds, tds_dstr_cstr(&curcol->column_name), len);
1618 2190 : } TDS_END_LEN_STRING
1619 : } else {
1620 324 : TDS_START_LEN_TINYINT(tds) { /* param name len */
1621 648 : tds_put_string(tds, tds_dstr_cstr(&curcol->column_name), len);
1622 324 : } TDS_END_LEN
1623 : }
1624 : } else {
1625 7238 : tds_put_byte(tds, 0x00); /* param name len */
1626 : }
1627 : /*
1628 : * TODO support other flags (use defaul null/no metadata)
1629 : * bit 1 (2 as flag) in TDS7+ is "default value" bit
1630 : * (what's the meaning of "default value" ?)
1631 : */
1632 :
1633 9752 : tdsdump_log(TDS_DBG_ERROR, "tds_put_data_info putting status \n");
1634 9752 : if (flags & TDS_PUT_DATA_LONG_STATUS)
1635 0 : tds_put_int(tds, curcol->column_output); /* status (input) */
1636 : else
1637 9752 : tds_put_byte(tds, curcol->column_output); /* status (input) */
1638 9752 : if (!IS_TDS7_PLUS(tds->conn))
1639 806 : tds_put_int(tds, curcol->column_usertype); /* usertype */
1640 9752 : TDS_PUT_BYTE(tds, curcol->on_server.column_type);
1641 :
1642 9752 : if (curcol->funcs->put_info(tds, curcol) != TDS_SUCCESS)
1643 : return TDS_FAIL;
1644 :
1645 : /* TODO needed in TDS4.2 ?? now is called only if TDS >= 5 */
1646 9752 : if (!IS_TDS7_PLUS(tds->conn))
1647 806 : tds_put_byte(tds, 0x00); /* locale info length */
1648 :
1649 : return TDS_SUCCESS;
1650 : }
1651 :
1652 : /**
1653 : * Send dynamic request on TDS 7+ to be executed
1654 : * \tds
1655 : * \param dyn dynamic query to execute
1656 : */
1657 : static TDSRET
1658 1138 : tds7_send_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn)
1659 : {
1660 : TDSCOLUMN *param;
1661 : TDSPARAMINFO *info;
1662 : int i;
1663 :
1664 : /* procedure name */
1665 : /* NOTE do not call this procedure using integer name (TDS_SP_EXECUTE) on mssql2k, it doesn't work! */
1666 1138 : TDS_PUT_N_AS_UCS2(tds, "sp_execute");
1667 1138 : tds_put_smallint(tds, 0); /* flags */
1668 :
1669 : /* id of prepared statement */
1670 1138 : tds_put_byte(tds, 0);
1671 1138 : tds_put_byte(tds, 0);
1672 1138 : tds_put_byte(tds, SYBINTN);
1673 1138 : tds_put_byte(tds, 4);
1674 1138 : tds_put_byte(tds, 4);
1675 1138 : tds_put_int(tds, dyn->num_id);
1676 :
1677 1138 : info = dyn->params;
1678 1138 : if (info)
1679 2232 : for (i = 0; i < info->num_cols; i++) {
1680 2232 : param = info->columns[i];
1681 2232 : TDS_PROPAGATE(tds_put_data_info(tds, param, 0));
1682 2232 : TDS_PROPAGATE(tds_put_data(tds, param));
1683 : }
1684 :
1685 1138 : tds->current_op = TDS_OP_EXECUTE;
1686 : return TDS_SUCCESS;
1687 : }
1688 :
1689 : /**
1690 : * Sends a previously prepared dynamic statement to the server.
1691 : * \param tds state information for the socket and the TDS protocol
1692 : * \param dyn dynamic proc to execute. Must build from same tds.
1693 : */
1694 : TDSRET
1695 1008 : tds_submit_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn)
1696 : {
1697 1008 : CHECK_TDS_EXTRA(tds);
1698 : /* TODO this dynamic should be in tds */
1699 1008 : CHECK_DYNAMIC_EXTRA(dyn);
1700 :
1701 1008 : tdsdump_log(TDS_DBG_FUNC, "tds_submit_execute()\n");
1702 :
1703 1008 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1704 : return TDS_FAIL;
1705 :
1706 1008 : tds_set_cur_dyn(tds, dyn);
1707 :
1708 1008 : if (IS_TDS7_PLUS(tds->conn)) {
1709 : /* check proper id */
1710 554 : if (dyn->num_id == 0) {
1711 0 : tds_set_state(tds, TDS_IDLE);
1712 0 : return TDS_FAIL;
1713 : }
1714 :
1715 : /* RPC on sp_execute */
1716 554 : tds_start_query(tds, TDS_RPC);
1717 :
1718 554 : tds7_send_execute(tds, dyn);
1719 :
1720 554 : return tds_query_flush_packet(tds);
1721 : }
1722 :
1723 454 : if (dyn->emulated) {
1724 84 : TDS_PROPAGATE(tds_send_emulated_execute(tds, dyn->query, dyn->params));
1725 84 : return tds_query_flush_packet(tds);
1726 : }
1727 :
1728 : /* query has been prepared successfully, discard original query */
1729 370 : if (dyn->query)
1730 236 : TDS_ZERO_FREE(dyn->query);
1731 :
1732 370 : tds->out_flag = TDS_NORMAL;
1733 : /* dynamic id */
1734 370 : tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
1735 370 : TDS_START_LEN_USMALLINT(tds) {
1736 370 : tds_put_byte(tds, 0x02);
1737 370 : tds_put_byte(tds, dyn->params ? 0x01 : 0);
1738 370 : TDS_START_LEN_TINYINT(tds) {
1739 370 : tds_put_string(tds, dyn->id, -1);
1740 370 : } TDS_END_LEN
1741 370 : tds_put_smallint(tds, 0);
1742 370 : } TDS_END_LEN
1743 :
1744 370 : if (dyn->params)
1745 342 : TDS_PROPAGATE(tds5_put_params(tds, dyn->params, 0));
1746 :
1747 : /* send it */
1748 370 : return tds_query_flush_packet(tds);
1749 : }
1750 :
1751 : /**
1752 : * Send parameters to server.
1753 : * \tds
1754 : * \param info parameters to send
1755 : * \param flags 0 or TDS_PUT_DATA_USE_NAME
1756 : */
1757 : static TDSRET
1758 518 : tds5_put_params(TDSSOCKET * tds, TDSPARAMINFO * info, int flags)
1759 : {
1760 : int i;
1761 518 : bool wide = false;
1762 :
1763 518 : CHECK_TDS_EXTRA(tds);
1764 518 : CHECK_PARAMINFO_EXTRA(info);
1765 :
1766 : /* column descriptions */
1767 0 : for (;;) {
1768 : TDSFREEZE outer, inner;
1769 :
1770 518 : tds_freeze(tds, &outer, 0);
1771 518 : if (wide) {
1772 0 : tds_put_byte(tds, TDS5_PARAMFMT2_TOKEN);
1773 0 : tds_freeze(tds, &inner, 4);
1774 0 : flags |= TDS_PUT_DATA_LONG_STATUS;
1775 : } else {
1776 518 : tds_put_byte(tds, TDS5_PARAMFMT_TOKEN);
1777 518 : tds_freeze(tds, &inner, 2);
1778 : }
1779 :
1780 : /* number of parameters */
1781 518 : tds_put_smallint(tds, info->num_cols);
1782 :
1783 : /* column detail for each parameter */
1784 1324 : for (i = 0; i < info->num_cols; i++)
1785 806 : TDS_PROPAGATE(tds_put_data_info(tds, info->columns[i], flags));
1786 :
1787 : /* if we fits we are fine */
1788 518 : if (wide || tds_freeze_written(&inner) - 2 < 0x10000u) {
1789 518 : tds_freeze_close(&inner);
1790 518 : tds_freeze_close(&outer);
1791 518 : break;
1792 : }
1793 :
1794 : /* try again with wide */
1795 0 : tds_freeze_abort(&inner);
1796 0 : tds_freeze_abort(&outer);
1797 0 : if (!tds_capability_has_req(tds->conn, TDS_REQ_WIDETABLE))
1798 : return TDS_FAIL;
1799 0 : wide = true;
1800 : }
1801 :
1802 : /* row data */
1803 518 : tds_put_byte(tds, TDS5_PARAMS_TOKEN);
1804 1324 : for (i = 0; i < info->num_cols; i++)
1805 1612 : TDS_PROPAGATE(tds_put_data(tds, info->columns[i]));
1806 : return TDS_SUCCESS;
1807 : }
1808 :
1809 : /**
1810 : * Check if dynamic request must be unprepared.
1811 : * Depending on status and protocol version request should be unprepared
1812 : * or not.
1813 : * \tds
1814 : * \param dyn dynamic request to check
1815 : */
1816 : int
1817 2260 : tds_needs_unprepare(TDSCONNECTION * conn, TDSDYNAMIC * dyn)
1818 : {
1819 : CHECK_CONN_EXTRA(conn);
1820 2260 : CHECK_DYNAMIC_EXTRA(dyn);
1821 :
1822 : /* check if statement is prepared */
1823 2260 : if (IS_TDS7_PLUS(conn) && !dyn->num_id)
1824 : return 0;
1825 :
1826 2228 : if (dyn->emulated || !dyn->id[0])
1827 : return 0;
1828 :
1829 2168 : return 1;
1830 : }
1831 :
1832 : /**
1833 : * Unprepare dynamic on idle.
1834 : * This let libTDS close the prepared statement when possible.
1835 : * \tds
1836 : * \param dyn dynamic request to close
1837 : */
1838 : TDSRET
1839 8 : tds_deferred_unprepare(TDSCONNECTION * conn, TDSDYNAMIC * dyn)
1840 : {
1841 : CHECK_CONN_EXTRA(conn);
1842 8 : CHECK_DYNAMIC_EXTRA(dyn);
1843 :
1844 8 : if (!tds_needs_unprepare(conn, dyn)) {
1845 0 : tds_dynamic_deallocated(conn, dyn);
1846 0 : return TDS_SUCCESS;
1847 : }
1848 :
1849 8 : dyn->defer_close = true;
1850 8 : conn->pending_close = 1;
1851 :
1852 8 : return TDS_SUCCESS;
1853 : }
1854 :
1855 : /**
1856 : * Send a unprepare request for a prepared query
1857 : * \param tds state information for the socket and the TDS protocol
1858 : * \param dyn dynamic query
1859 : * \result TDS_SUCCESS or TDS_FAIL
1860 : */
1861 : TDSRET
1862 2384 : tds_submit_unprepare(TDSSOCKET * tds, TDSDYNAMIC * dyn)
1863 : {
1864 2384 : CHECK_TDS_EXTRA(tds);
1865 : /* TODO test dyn in tds */
1866 2384 : CHECK_DYNAMIC_EXTRA(dyn);
1867 :
1868 2384 : if (!dyn)
1869 : return TDS_FAIL;
1870 :
1871 2384 : tdsdump_log(TDS_DBG_FUNC, "tds_submit_unprepare() %s\n", dyn->id);
1872 :
1873 2384 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1874 : return TDS_FAIL;
1875 :
1876 2384 : tds_set_cur_dyn(tds, dyn);
1877 :
1878 2384 : if (IS_TDS7_PLUS(tds->conn)) {
1879 : /* RPC on sp_execute */
1880 2094 : tds_start_query(tds, TDS_RPC);
1881 :
1882 : /* procedure name */
1883 2094 : if (IS_TDS71_PLUS(tds->conn)) {
1884 : /* save some byte for mssql2k */
1885 2094 : tds_put_smallint(tds, -1);
1886 2094 : tds_put_smallint(tds, TDS_SP_UNPREPARE);
1887 : } else {
1888 0 : TDS_PUT_N_AS_UCS2(tds, "sp_unprepare");
1889 : }
1890 2094 : tds_put_smallint(tds, 0); /* flags */
1891 :
1892 : /* id of prepared statement */
1893 2094 : tds_put_byte(tds, 0);
1894 2094 : tds_put_byte(tds, 0);
1895 2094 : tds_put_byte(tds, SYBINTN);
1896 2094 : tds_put_byte(tds, 4);
1897 2094 : tds_put_byte(tds, 4);
1898 2094 : tds_put_int(tds, dyn->num_id);
1899 :
1900 2094 : tds->current_op = TDS_OP_UNPREPARE;
1901 2094 : return tds_query_flush_packet(tds);
1902 : }
1903 :
1904 290 : if (dyn->emulated) {
1905 6 : tds_start_query(tds, TDS_QUERY);
1906 :
1907 : /* just a dummy select to return some data */
1908 6 : tds_put_string(tds, "select 1 where 0=1", -1);
1909 6 : return tds_query_flush_packet(tds);
1910 : }
1911 :
1912 284 : tds->out_flag = TDS_NORMAL;
1913 : /* dynamic id */
1914 284 : tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
1915 284 : TDS_START_LEN_USMALLINT(tds) {
1916 284 : tds_put_byte(tds, TDS_DYN_DEALLOC);
1917 284 : tds_put_byte(tds, 0x00);
1918 284 : TDS_START_LEN_TINYINT(tds) {
1919 284 : tds_put_string(tds, dyn->id, -1);
1920 284 : } TDS_END_LEN
1921 284 : tds_put_smallint(tds, 0);
1922 284 : } TDS_END_LEN
1923 :
1924 : /* send it */
1925 284 : tds->current_op = TDS_OP_DYN_DEALLOC;
1926 284 : return tds_query_flush_packet(tds);
1927 : }
1928 :
1929 : /**
1930 : * Send RPC as string query.
1931 : * This function is used on old protocol which does not support RPC queries.
1932 : * \tds
1933 : * \param rpc_name name of RPC to invoke
1934 : * \param params parameters to send to server
1935 : * \returns TDS_FAIL or TDS_SUCCESS
1936 : */
1937 : static TDSRET
1938 0 : tds4_send_emulated_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params)
1939 : {
1940 : TDSCOLUMN *param;
1941 : int i, n;
1942 0 : int num_params = params ? params->num_cols : 0;
1943 0 : const char *sep = " ";
1944 : char buf[80];
1945 :
1946 : /* create params and set */
1947 0 : for (i = 0, n = 0; i < num_params; ++i) {
1948 :
1949 0 : param = params->columns[i];
1950 :
1951 : /* declare and set output parameters */
1952 0 : if (!param->column_output)
1953 0 : continue;
1954 0 : ++n;
1955 0 : sprintf(buf, " DECLARE @P%d ", n);
1956 0 : tds_get_column_declaration(tds, param, buf + strlen(buf));
1957 0 : sprintf(buf + strlen(buf), " SET @P%d=", n);
1958 0 : tds_put_string(tds, buf, -1);
1959 0 : tds_put_param_as_string(tds, params, i);
1960 : }
1961 :
1962 : /* put exec statement */
1963 0 : tds_put_string(tds, " EXEC ", 6);
1964 0 : tds_put_string(tds, rpc_name, -1);
1965 :
1966 : /* put arguments */
1967 0 : for (i = 0, n = 0; i < num_params; ++i) {
1968 0 : param = params->columns[i];
1969 0 : tds_put_string(tds, sep, -1);
1970 0 : if (!tds_dstr_isempty(¶m->column_name)) {
1971 0 : tds_put_string(tds, tds_dstr_cstr(¶m->column_name), (int)tds_dstr_len(¶m->column_name));
1972 0 : tds_put_string(tds, "=", 1);
1973 : }
1974 0 : if (param->column_output) {
1975 0 : ++n;
1976 0 : sprintf(buf, "@P%d OUTPUT", n);
1977 0 : tds_put_string(tds, buf, -1);
1978 : } else {
1979 0 : tds_put_param_as_string(tds, params, i);
1980 : }
1981 0 : sep = ",";
1982 : }
1983 :
1984 0 : return tds_query_flush_packet(tds);
1985 : }
1986 :
1987 : /**
1988 : * Calls a RPC from server. Output parameters will be stored in tds->param_info.
1989 : * \param tds state information for the socket and the TDS protocol
1990 : * \param rpc_name name of RPC
1991 : * \param params parameters information. NULL for no parameters
1992 : */
1993 : TDSRET
1994 1458 : tds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params, TDSHEADERS * head)
1995 : {
1996 : TDSCOLUMN *param;
1997 : int i;
1998 1458 : int num_params = params ? params->num_cols : 0;
1999 :
2000 1458 : CHECK_TDS_EXTRA(tds);
2001 1458 : if (params)
2002 1400 : CHECK_PARAMINFO_EXTRA(params);
2003 :
2004 1458 : assert(tds);
2005 1458 : assert(rpc_name);
2006 :
2007 1458 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2008 : return TDS_FAIL;
2009 :
2010 : /* distinguish from dynamic query */
2011 1458 : tds_release_cur_dyn(tds);
2012 :
2013 1458 : if (IS_TDS7_PLUS(tds->conn)) {
2014 1278 : if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS)
2015 : return TDS_FAIL;
2016 :
2017 : /* procedure name */
2018 1278 : TDS_START_LEN_USMALLINT(tds) {
2019 1278 : tds_put_string(tds, rpc_name, -1);
2020 1278 : } TDS_END_LEN_STRING
2021 :
2022 : /*
2023 : * TODO support flags
2024 : * bit 0 (1 as flag) in TDS7/TDS5 is "recompile"
2025 : * bit 1 (2 as flag) in TDS7+ is "no metadata" bit
2026 : * (I don't know meaning of "no metadata")
2027 : */
2028 1278 : tds_put_smallint(tds, 0);
2029 :
2030 3324 : for (i = 0; i < num_params; i++) {
2031 2046 : param = params->columns[i];
2032 2046 : TDS_PROPAGATE(tds_put_data_info(tds, param, TDS_PUT_DATA_USE_NAME));
2033 2046 : TDS_PROPAGATE(tds_put_data(tds, param));
2034 : }
2035 :
2036 1278 : return tds_query_flush_packet(tds);
2037 : }
2038 :
2039 180 : if (IS_TDS50(tds->conn)) {
2040 180 : tds->out_flag = TDS_NORMAL;
2041 :
2042 : /* DBRPC */
2043 180 : tds_put_byte(tds, TDS_DBRPC_TOKEN);
2044 180 : TDS_START_LEN_USMALLINT(tds) {
2045 180 : TDS_START_LEN_TINYINT(tds) {
2046 180 : tds_put_string(tds, rpc_name, -1);
2047 180 : } TDS_END_LEN
2048 : /* TODO flags */
2049 180 : tds_put_smallint(tds, num_params ? 2 : 0);
2050 180 : } TDS_END_LEN
2051 :
2052 180 : if (num_params)
2053 170 : TDS_PROPAGATE(tds5_put_params(tds, params, TDS_PUT_DATA_USE_NAME));
2054 :
2055 : /* send it */
2056 180 : return tds_query_flush_packet(tds);
2057 : }
2058 :
2059 : /* emulate it for TDS4.x, send RPC for mssql */
2060 0 : if (tds->conn->tds_version < 0x500)
2061 0 : return tds4_send_emulated_rpc(tds, rpc_name, params);
2062 :
2063 : /* TODO continue, support for TDS4?? */
2064 0 : tds_set_state(tds, TDS_IDLE);
2065 0 : return TDS_FAIL;
2066 : }
2067 :
2068 : /**
2069 : * tds_send_cancel() sends an empty packet (8 byte header only)
2070 : * tds_process_cancel should be called directly after this.
2071 : * \param tds state information for the socket and the TDS protocol
2072 : * \remarks
2073 : * tcp will either deliver the packet or time out.
2074 : * (TIME_WAIT determines how long it waits between retries.)
2075 : *
2076 : * On sending the cancel, we may get EAGAIN. We then select(2) until we know
2077 : * either 1) it succeeded or 2) it didn't. On failure, close the socket,
2078 : * tell the app, and fail the function.
2079 : *
2080 : * On success, we read(2) and wait for a reply with select(2). If we get
2081 : * one, great. If the client's timeout expires, we tell him, but all we can
2082 : * do is wait some more or give up and close the connection. If he tells us
2083 : * to cancel again, we wait some more.
2084 : */
2085 : TDSRET
2086 11450 : tds_send_cancel(TDSSOCKET * tds)
2087 : {
2088 : #if ENABLE_ODBC_MARS
2089 5732 : CHECK_TDS_EXTRA(tds);
2090 :
2091 5732 : tdsdump_log(TDS_DBG_FUNC, "tds_send_cancel: %sin_cancel and %sidle\n",
2092 : (tds->in_cancel? "":"not "), (tds->state == TDS_IDLE? "":"not "));
2093 :
2094 : /* one cancel is sufficient */
2095 5732 : if (tds->in_cancel || tds->state == TDS_IDLE) {
2096 : return TDS_SUCCESS;
2097 : }
2098 :
2099 616 : tds->in_cancel = 1;
2100 :
2101 616 : if (tds_mutex_trylock(&tds->conn->list_mtx)) {
2102 : /* TODO check */
2103 : /* signal other socket */
2104 0 : tds_wakeup_send(&tds->conn->wakeup, 1);
2105 0 : return TDS_SUCCESS;
2106 : }
2107 616 : if (tds->conn->in_net_tds) {
2108 121 : tds_mutex_unlock(&tds->conn->list_mtx);
2109 : /* TODO check */
2110 : /* signal other socket */
2111 121 : tds_wakeup_send(&tds->conn->wakeup, 1);
2112 121 : return TDS_SUCCESS;
2113 : }
2114 495 : tds_mutex_unlock(&tds->conn->list_mtx);
2115 :
2116 : /*
2117 : problem: if we are in in_net and we got a signal ??
2118 : on timeout we and a cancel, directly in in_net
2119 : if we hold the lock and we get a signal lock create a death lock
2120 :
2121 : if we use a recursive mutex and we can get the lock there are 2 cases
2122 : - same thread, we could add a packet and signal, no try ok
2123 : - first thread locking, we could add a packet but are we sure it get processed ??, no try ok
2124 : if recursive mutex and we can't get another thread, wait
2125 :
2126 : if mutex is not recursive and we get the lock (try)
2127 : - nobody locked, if in_net it could be same or another
2128 : if mutex is not recursive and we can't get the lock
2129 : - another thread is locking, sending signal require not exiting and global list (not protected by list_mtx)
2130 : - same thread have lock, we can't wait nothing without deathlock, setting a flag in tds and signaling could help
2131 :
2132 : if a tds is waiting for data or is waiting for a condition or for a signal in poll
2133 : pass cancel request on socket ??
2134 : */
2135 :
2136 495 : tds->out_flag = TDS_CANCEL;
2137 495 : tds->out_pos = 8;
2138 495 : tdsdump_log(TDS_DBG_FUNC, "tds_send_cancel: sending cancel packet\n");
2139 495 : return tds_flush_packet(tds);
2140 : #else
2141 : TDSRET rc;
2142 :
2143 : /*
2144 : * if we are not able to get the lock signal other thread
2145 : * this means that either:
2146 : * - another thread is processing data
2147 : * - we got called from a signal inside processing thread
2148 : * - we got called from message handler
2149 : */
2150 5718 : if (tds_mutex_trylock(&tds->wire_mtx)) {
2151 : /* TODO check */
2152 201 : if (!tds->in_cancel)
2153 133 : tds->in_cancel = 1;
2154 : /* signal other socket */
2155 201 : tds_wakeup_send(&tds->conn->wakeup, 1);
2156 201 : return TDS_SUCCESS;
2157 : }
2158 :
2159 5517 : CHECK_TDS_EXTRA(tds);
2160 :
2161 5517 : tdsdump_log(TDS_DBG_FUNC, "tds_send_cancel: %sin_cancel and %sidle\n",
2162 : (tds->in_cancel? "":"not "), (tds->state == TDS_IDLE? "":"not "));
2163 :
2164 : /* one cancel is sufficient */
2165 5517 : if (tds->in_cancel || tds->state == TDS_IDLE) {
2166 5040 : tds_mutex_unlock(&tds->wire_mtx);
2167 5040 : return TDS_SUCCESS;
2168 : }
2169 :
2170 477 : rc = tds_put_cancel(tds);
2171 477 : tds_mutex_unlock(&tds->wire_mtx);
2172 :
2173 477 : return rc;
2174 : #endif
2175 : }
2176 :
2177 : /**
2178 : * Quote a string properly. Output string is always NUL-terminated
2179 : * \param buffer output buffer. If NULL function will just return
2180 : * required bytes
2181 : * \param quoting quote character (should be one of '\'', '"', ']')
2182 : * \param id string to quote
2183 : * \param len length of string to quote
2184 : * \returns size of output string
2185 : */
2186 : static size_t
2187 11651 : tds_quote(char *buffer, char quoting, const char *id, size_t len)
2188 : {
2189 : size_t size;
2190 : const char *src, *pend;
2191 : char *dst;
2192 :
2193 11651 : pend = id + len;
2194 :
2195 : /* quote */
2196 11651 : src = id;
2197 11651 : if (!buffer) {
2198 7743 : size = 2u + len;
2199 43184 : for (; src != pend; ++src)
2200 35441 : if (*src == quoting)
2201 0 : ++size;
2202 : return size;
2203 : }
2204 :
2205 3908 : dst = buffer;
2206 3908 : *dst++ = (quoting == ']') ? '[' : quoting;
2207 31423 : for (; src != pend; ++src) {
2208 27515 : if (*src == quoting)
2209 0 : *dst++ = quoting;
2210 27515 : *dst++ = *src;
2211 : }
2212 3908 : *dst++ = quoting;
2213 3908 : *dst = 0;
2214 3908 : return dst - buffer;
2215 : }
2216 :
2217 : /**
2218 : * Quote an id
2219 : * \param tds state information for the socket and the TDS protocol
2220 : * \param buffer buffer to store quoted id. If NULL do not write anything
2221 : * (useful to compute quote length)
2222 : * \param id id to quote
2223 : * \param idlen id length (< 0 for NUL terminated)
2224 : * \result written chars (not including needed terminator)
2225 : * \see tds_quote_id_rpc
2226 : */
2227 : size_t
2228 11611 : tds_quote_id(TDSSOCKET * tds, char *buffer, const char *id, ptrdiff_t idlen)
2229 : {
2230 : size_t i, len;
2231 :
2232 11611 : CHECK_TDS_EXTRA(tds);
2233 :
2234 11611 : len = idlen < 0 ? strlen(id) : (size_t) idlen;
2235 :
2236 : /* quote always for mssql */
2237 11611 : if (TDS_IS_MSSQL(tds) || tds->conn->product_version >= TDS_SYB_VER(12, 5, 1))
2238 11611 : return tds_quote(buffer, ']', id, len);
2239 :
2240 : /* need quote ?? */
2241 0 : for (i = 0; i < len; ++i) {
2242 0 : char c = id[i];
2243 :
2244 0 : if (c >= 'a' && c <= 'z')
2245 0 : continue;
2246 0 : if (c >= 'A' && c <= 'Z')
2247 0 : continue;
2248 0 : if (i > 0 && c >= '0' && c <= '9')
2249 0 : continue;
2250 0 : if (c == '_')
2251 0 : continue;
2252 0 : return tds_quote(buffer, '\"', id, len);
2253 : }
2254 :
2255 0 : if (buffer) {
2256 0 : memcpy(buffer, id, len);
2257 0 : buffer[len] = '\0';
2258 : }
2259 : return len;
2260 : }
2261 :
2262 : /**
2263 : * Quote an id for a RPC call
2264 : * \param tds state information for the socket and the TDS protocol
2265 : * \param buffer buffer to store quoted id. If NULL do not write anything
2266 : * (useful to compute quote length)
2267 : * \param id id to quote
2268 : * \param idlen id length (< 0 for NUL terminated)
2269 : * \result written chars (not including needed terminator)
2270 : * \see tds_quote_id
2271 : */
2272 : size_t
2273 40 : tds_quote_id_rpc(TDSSOCKET * tds, char *buffer, const char *id, ptrdiff_t idlen)
2274 : {
2275 : size_t len;
2276 : /* We are quoting for RPC calls, not base language queries. For RPC calls Sybase
2277 : * servers don't accept '[]' style quoting so don't use them but use normal
2278 : * identifier quoting ('""') */
2279 40 : char quote_id_char = TDS_IS_MSSQL(tds) ? ']' : '\"';
2280 :
2281 40 : CHECK_TDS_EXTRA(tds);
2282 :
2283 40 : len = idlen < 0 ? strlen(id) : (size_t) idlen;
2284 :
2285 40 : return tds_quote(buffer, quote_id_char, id, len);
2286 : }
2287 :
2288 : /**
2289 : * Quote a string
2290 : * \param tds state information for the socket and the TDS protocol
2291 : * \param buffer buffer to store quoted id. If NULL do not write anything
2292 : * (useful to compute quote length)
2293 : * \param str string to quote (not necessary NUL-terminated)
2294 : * \param len length of string (-1 for NUL-terminated)
2295 : * \result written chars (not including needed terminator)
2296 : */
2297 : size_t
2298 0 : tds_quote_string(TDSSOCKET * tds TDS_UNUSED, char *buffer, const char *str, ptrdiff_t len)
2299 : {
2300 0 : return tds_quote(buffer, '\'', str, len < 0 ? strlen(str) : (size_t) len);
2301 : }
2302 :
2303 : /**
2304 : * Set current cursor.
2305 : * Current cursor is the one will receive output from server.
2306 : * \tds
2307 : * \param cursor cursor to set as current
2308 : */
2309 : static inline void
2310 : tds_set_cur_cursor(TDSSOCKET *tds, TDSCURSOR *cursor)
2311 : {
2312 4094 : ++cursor->ref_count;
2313 4094 : if (tds->cur_cursor)
2314 6 : tds_release_cursor(&tds->cur_cursor);
2315 4094 : tds->cur_cursor = cursor;
2316 : }
2317 :
2318 : TDSRET
2319 2354 : tds_cursor_declare(TDSSOCKET * tds, TDSCURSOR * cursor, bool *something_to_send)
2320 : {
2321 2354 : CHECK_TDS_EXTRA(tds);
2322 :
2323 2354 : if (!cursor)
2324 : return TDS_FAIL;
2325 :
2326 2354 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_declare() cursor id = %d\n", cursor->cursor_id);
2327 :
2328 2354 : if (IS_TDS7_PLUS(tds->conn)) {
2329 2346 : cursor->srv_status |= TDS_CUR_ISTAT_DECLARED;
2330 2346 : cursor->srv_status |= TDS_CUR_ISTAT_CLOSED;
2331 2346 : cursor->srv_status |= TDS_CUR_ISTAT_RDONLY;
2332 : }
2333 :
2334 2354 : if (IS_TDS50(tds->conn)) {
2335 8 : if (!*something_to_send) {
2336 8 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2337 : return TDS_FAIL;
2338 :
2339 8 : tds->out_flag = TDS_NORMAL;
2340 : }
2341 8 : if (tds->state != TDS_WRITING || tds->out_flag != TDS_NORMAL)
2342 : return TDS_FAIL;
2343 :
2344 8 : tds_put_byte(tds, TDS_CURDECLARE_TOKEN);
2345 :
2346 : /* length of the data stream that follows */
2347 8 : TDS_START_LEN_USMALLINT(tds) {
2348 8 : TDS_START_LEN_TINYINT(tds) {
2349 8 : tds_put_string(tds, cursor->cursor_name, -1);
2350 8 : } TDS_END_LEN
2351 8 : tds_put_byte(tds, 1); /* cursor option is read only=1, unused=0 */
2352 8 : tds_put_byte(tds, 0); /* status unused=0 */
2353 8 : TDS_START_LEN_USMALLINT(tds) {
2354 8 : tds_put_string(tds, cursor->query, -1);
2355 8 : } TDS_END_LEN
2356 8 : tds_put_tinyint(tds, 0); /* number of columns = 0 , valid value applicable only for updatable cursor */
2357 8 : } TDS_END_LEN
2358 8 : *something_to_send = true;
2359 : }
2360 :
2361 : return TDS_SUCCESS;
2362 : }
2363 :
2364 : TDSRET
2365 2354 : tds_cursor_open(TDSSOCKET * tds, TDSCURSOR * cursor, TDSPARAMINFO *params, bool *something_to_send)
2366 : {
2367 2354 : CHECK_TDS_EXTRA(tds);
2368 :
2369 2354 : if (!cursor)
2370 : return TDS_FAIL;
2371 :
2372 2354 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_open() cursor id = %d\n", cursor->cursor_id);
2373 :
2374 2354 : if (!*something_to_send) {
2375 2348 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2376 : return TDS_FAIL;
2377 : }
2378 2354 : if (tds->state != TDS_WRITING)
2379 : return TDS_FAIL;
2380 :
2381 2354 : tds_set_cur_cursor(tds, cursor);
2382 :
2383 2354 : if (IS_TDS50(tds->conn)) {
2384 8 : tds->out_flag = TDS_NORMAL;
2385 8 : tds_put_byte(tds, TDS_CUROPEN_TOKEN);
2386 8 : TDS_START_LEN_USMALLINT(tds) {
2387 :
2388 : /*tds_put_int(tds, cursor->cursor_id); *//* Only if cursor id is passed as zero, the cursor name need to be sent */
2389 :
2390 8 : tds_put_int(tds, 0);
2391 8 : TDS_START_LEN_TINYINT(tds) {
2392 8 : tds_put_string(tds, cursor->cursor_name, -1);
2393 8 : } TDS_END_LEN
2394 8 : tds_put_byte(tds, 0); /* Cursor status : 0 for no arguments */
2395 8 : } TDS_END_LEN
2396 8 : *something_to_send = true;
2397 : }
2398 2354 : if (IS_TDS7_PLUS(tds->conn)) {
2399 : const char *converted_query;
2400 : size_t converted_query_len;
2401 2346 : int num_params = params ? params->num_cols : 0;
2402 : TDSFREEZE outer;
2403 2346 : TDSRET rc = TDS_SUCCESS;
2404 :
2405 : /* cursor statement */
2406 2346 : converted_query =
2407 2346 : tds_convert_string(tds, tds->conn->char_convs[client2ucs2], cursor->query,
2408 2346 : strlen(cursor->query), &converted_query_len);
2409 2346 : if (!converted_query) {
2410 0 : if (!*something_to_send)
2411 0 : tds_set_state(tds, TDS_IDLE);
2412 0 : return TDS_FAIL;
2413 : }
2414 :
2415 2346 : tds_freeze(tds, &outer, 0);
2416 :
2417 : /* RPC call to sp_cursoropen */
2418 2346 : tds_start_query(tds, TDS_RPC);
2419 :
2420 : /* procedure identifier by number */
2421 :
2422 2346 : if (IS_TDS71_PLUS(tds->conn)) {
2423 2346 : tds_put_smallint(tds, -1);
2424 2346 : tds_put_smallint(tds, TDS_SP_CURSOROPEN);
2425 : } else {
2426 0 : TDS_PUT_N_AS_UCS2(tds, "sp_cursoropen");
2427 : }
2428 :
2429 2346 : tds_put_smallint(tds, 0); /* flags */
2430 :
2431 : /* return cursor handle (int) */
2432 :
2433 2346 : tds_put_byte(tds, 0); /* no parameter name */
2434 2346 : tds_put_byte(tds, 1); /* output parameter */
2435 2346 : tds_put_byte(tds, SYBINTN);
2436 2346 : tds_put_byte(tds, 4);
2437 2346 : tds_put_byte(tds, 0);
2438 :
2439 2346 : if (num_params) {
2440 1766 : tds7_put_query_params(tds, converted_query, converted_query_len);
2441 : } else {
2442 580 : tds_put_byte(tds, 0);
2443 580 : tds_put_byte(tds, 0);
2444 580 : tds_put_byte(tds, SYBNTEXT); /* must be Ntype */
2445 580 : TDS_PUT_INT(tds, converted_query_len);
2446 580 : if (IS_TDS71_PLUS(tds->conn))
2447 580 : tds_put_n(tds, tds->conn->collation, 5);
2448 580 : TDS_PUT_INT(tds, converted_query_len);
2449 580 : tds_put_n(tds, converted_query, (int)converted_query_len);
2450 : }
2451 :
2452 : /* type */
2453 2346 : tds_put_byte(tds, 0); /* no parameter name */
2454 2346 : tds_put_byte(tds, 1); /* output parameter */
2455 2346 : tds_put_byte(tds, SYBINTN);
2456 2346 : tds_put_byte(tds, 4);
2457 2346 : tds_put_byte(tds, 4);
2458 2346 : tds_put_int(tds, num_params ? cursor->type | 0x1000 : cursor->type);
2459 :
2460 : /* concurrency */
2461 2346 : tds_put_byte(tds, 0); /* no parameter name */
2462 2346 : tds_put_byte(tds, 1); /* output parameter */
2463 2346 : tds_put_byte(tds, SYBINTN);
2464 2346 : tds_put_byte(tds, 4);
2465 2346 : tds_put_byte(tds, 4);
2466 2346 : tds_put_int(tds, cursor->concurrency);
2467 :
2468 : /* row count */
2469 2346 : tds_put_byte(tds, 0);
2470 2346 : tds_put_byte(tds, 1); /* output parameter */
2471 2346 : tds_put_byte(tds, SYBINTN);
2472 2346 : tds_put_byte(tds, 4);
2473 2346 : tds_put_byte(tds, 4);
2474 2346 : tds_put_int(tds, 0);
2475 :
2476 2346 : if (num_params) {
2477 : int i;
2478 :
2479 1766 : rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
2480 :
2481 3532 : for (i = 0; i < num_params; i++) {
2482 1766 : TDSCOLUMN *param = params->columns[i];
2483 : /* TODO check error */
2484 1766 : tds_put_data_info(tds, param, 0);
2485 : /* FIXME handle error */
2486 1766 : tds_put_data(tds, param);
2487 : }
2488 : }
2489 4692 : tds_convert_string_free(cursor->query, converted_query);
2490 2346 : if (TDS_FAILED(rc)) {
2491 0 : tds_freeze_abort(&outer);
2492 0 : if (!*something_to_send)
2493 0 : tds_set_state(tds, TDS_IDLE);
2494 : return rc;
2495 : }
2496 2346 : tds_freeze_close(&outer);
2497 :
2498 2346 : *something_to_send = true;
2499 2346 : tds->current_op = TDS_OP_CURSOROPEN;
2500 2346 : tdsdump_log(TDS_DBG_ERROR, "tds_cursor_open (): RPC call set up \n");
2501 : }
2502 :
2503 :
2504 2354 : tdsdump_log(TDS_DBG_ERROR, "tds_cursor_open (): cursor open completed\n");
2505 : return TDS_SUCCESS;
2506 : }
2507 :
2508 : TDSRET
2509 288 : tds_cursor_setrows(TDSSOCKET * tds, TDSCURSOR * cursor, bool *something_to_send)
2510 : {
2511 288 : CHECK_TDS_EXTRA(tds);
2512 :
2513 288 : if (!cursor)
2514 : return TDS_FAIL;
2515 :
2516 288 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_setrows() cursor id = %d\n", cursor->cursor_id);
2517 :
2518 288 : if (IS_TDS7_PLUS(tds->conn)) {
2519 280 : cursor->srv_status &= ~TDS_CUR_ISTAT_DECLARED;
2520 280 : cursor->srv_status |= TDS_CUR_ISTAT_CLOSED;
2521 280 : cursor->srv_status |= TDS_CUR_ISTAT_ROWCNT;
2522 : }
2523 :
2524 288 : if (IS_TDS50(tds->conn)) {
2525 8 : if (!*something_to_send) {
2526 2 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2527 : return TDS_FAIL;
2528 :
2529 2 : tds->out_flag = TDS_NORMAL;
2530 : }
2531 8 : if (tds->state != TDS_WRITING || tds->out_flag != TDS_NORMAL)
2532 : return TDS_FAIL;
2533 :
2534 8 : tds_set_cur_cursor(tds, cursor);
2535 8 : tds_put_byte(tds, TDS_CURINFO_TOKEN);
2536 :
2537 8 : TDS_START_LEN_USMALLINT(tds) {
2538 : /* length of data stream that follows */
2539 :
2540 : /* tds_put_int(tds, tds->cursor->cursor_id); */ /* Cursor id */
2541 :
2542 8 : tds_put_int(tds, 0);
2543 8 : TDS_START_LEN_TINYINT(tds) {
2544 8 : tds_put_string(tds, cursor->cursor_name, -1);
2545 8 : } TDS_END_LEN
2546 8 : tds_put_byte(tds, 1); /* Command TDS_CUR_CMD_SETCURROWS */
2547 8 : tds_put_byte(tds, 0x00); /* Status - TDS_CUR_ISTAT_ROWCNT 0x0020 */
2548 8 : tds_put_byte(tds, 0x20); /* Status - TDS_CUR_ISTAT_ROWCNT 0x0020 */
2549 8 : tds_put_int(tds, cursor->cursor_rows); /* row count to set */
2550 8 : } TDS_END_LEN
2551 8 : *something_to_send = true;
2552 :
2553 : }
2554 : return TDS_SUCCESS;
2555 : }
2556 :
2557 : static void
2558 824 : tds7_put_cursor_fetch(TDSSOCKET * tds, TDS_INT cursor_id, TDS_TINYINT fetch_type, TDS_INT i_row, TDS_INT num_rows)
2559 : {
2560 824 : if (IS_TDS71_PLUS(tds->conn)) {
2561 824 : tds_put_smallint(tds, -1);
2562 824 : tds_put_smallint(tds, TDS_SP_CURSORFETCH);
2563 : } else {
2564 0 : TDS_PUT_N_AS_UCS2(tds, "sp_cursorfetch");
2565 : }
2566 :
2567 : /* This flag tells the SP only to */
2568 : /* output a dummy metadata token */
2569 :
2570 824 : tds_put_smallint(tds, 2);
2571 :
2572 : /* input cursor handle (int) */
2573 :
2574 824 : tds_put_byte(tds, 0); /* no parameter name */
2575 824 : tds_put_byte(tds, 0); /* input parameter */
2576 824 : tds_put_byte(tds, SYBINTN);
2577 824 : tds_put_byte(tds, 4);
2578 824 : tds_put_byte(tds, 4);
2579 824 : tds_put_int(tds, cursor_id);
2580 :
2581 : /* fetch type - 2 = NEXT */
2582 :
2583 824 : tds_put_byte(tds, 0); /* no parameter name */
2584 824 : tds_put_byte(tds, 0); /* input parameter */
2585 824 : tds_put_byte(tds, SYBINTN);
2586 824 : tds_put_byte(tds, 4);
2587 824 : tds_put_byte(tds, 4);
2588 824 : tds_put_int(tds, fetch_type);
2589 :
2590 : /* row number */
2591 824 : tds_put_byte(tds, 0); /* no parameter name */
2592 824 : tds_put_byte(tds, 0); /* input parameter */
2593 824 : tds_put_byte(tds, SYBINTN);
2594 824 : tds_put_byte(tds, 4);
2595 824 : if ((fetch_type & 0x30) != 0) {
2596 72 : tds_put_byte(tds, 4);
2597 72 : tds_put_int(tds, i_row);
2598 : } else {
2599 752 : tds_put_byte(tds, 0);
2600 : }
2601 :
2602 : /* number of rows to fetch */
2603 824 : tds_put_byte(tds, 0); /* no parameter name */
2604 824 : tds_put_byte(tds, 0); /* input parameter */
2605 824 : tds_put_byte(tds, SYBINTN);
2606 824 : tds_put_byte(tds, 4);
2607 824 : tds_put_byte(tds, 4);
2608 824 : tds_put_int(tds, num_rows);
2609 824 : }
2610 :
2611 : TDSRET
2612 846 : tds_cursor_fetch(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_CURSOR_FETCH fetch_type, TDS_INT i_row)
2613 : {
2614 846 : CHECK_TDS_EXTRA(tds);
2615 :
2616 846 : if (!cursor)
2617 : return TDS_FAIL;
2618 :
2619 846 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_fetch() cursor id = %d\n", cursor->cursor_id);
2620 :
2621 846 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2622 : return TDS_FAIL;
2623 :
2624 846 : tds_set_cur_cursor(tds, cursor);
2625 :
2626 846 : if (IS_TDS50(tds->conn)) {
2627 30 : size_t len = strlen(cursor->cursor_name);
2628 30 : size_t row_len = 0;
2629 :
2630 30 : tds->out_flag = TDS_NORMAL;
2631 30 : tds_put_byte(tds, TDS_CURFETCH_TOKEN);
2632 :
2633 30 : if (len > (255-10))
2634 0 : len = (255-10);
2635 30 : if (fetch_type == TDS_CURSOR_FETCH_ABSOLUTE || fetch_type == TDS_CURSOR_FETCH_RELATIVE)
2636 0 : row_len = 4;
2637 :
2638 : /*tds_put_smallint(tds, 8); */
2639 :
2640 30 : TDS_PUT_SMALLINT(tds, 6 + len + row_len); /* length of the data stream that follows */
2641 :
2642 : /*tds_put_int(tds, cursor->cursor_id); *//* cursor id returned by the server */
2643 :
2644 30 : tds_put_int(tds, 0);
2645 30 : TDS_PUT_BYTE(tds, len);
2646 30 : tds_put_n(tds, cursor->cursor_name, len);
2647 30 : tds_put_tinyint(tds, fetch_type);
2648 :
2649 : /* optional argument to fetch row at absolute/relative position */
2650 30 : if (row_len)
2651 0 : tds_put_int(tds, i_row);
2652 30 : return tds_query_flush_packet(tds);
2653 : }
2654 :
2655 816 : if (IS_TDS7_PLUS(tds->conn)) {
2656 :
2657 : /* RPC call to sp_cursorfetch */
2658 : static const unsigned char mssql_fetch[7] = {
2659 : 0,
2660 : 2, /* TDS_CURSOR_FETCH_NEXT */
2661 : 4, /* TDS_CURSOR_FETCH_PREV */
2662 : 1, /* TDS_CURSOR_FETCH_FIRST */
2663 : 8, /* TDS_CURSOR_FETCH_LAST */
2664 : 0x10, /* TDS_CURSOR_FETCH_ABSOLUTE */
2665 : 0x20 /* TDS_CURSOR_FETCH_RELATIVE */
2666 : };
2667 :
2668 816 : tds_start_query(tds, TDS_RPC);
2669 :
2670 : /* TODO enum for 2 ... */
2671 816 : if (cursor->type == 2 && fetch_type == TDS_CURSOR_FETCH_ABSOLUTE) {
2672 : /* strangely dynamic cursor do not support absolute so emulate it with first + relative */
2673 8 : tds7_put_cursor_fetch(tds, cursor->cursor_id, 1, 0, 0);
2674 : /* TODO define constant */
2675 8 : tds_put_byte(tds, IS_TDS72_PLUS(tds->conn) ? 0xff : 0x80);
2676 8 : tds7_put_cursor_fetch(tds, cursor->cursor_id, 0x20, i_row, cursor->cursor_rows);
2677 : } else {
2678 : /* TODO check fetch_type ?? */
2679 808 : tds7_put_cursor_fetch(tds, cursor->cursor_id, mssql_fetch[fetch_type], i_row, cursor->cursor_rows);
2680 : }
2681 :
2682 816 : tds->current_op = TDS_OP_CURSORFETCH;
2683 816 : return tds_query_flush_packet(tds);
2684 : }
2685 :
2686 0 : tds_set_state(tds, TDS_IDLE);
2687 0 : return TDS_SUCCESS;
2688 : }
2689 :
2690 : TDSRET
2691 24 : tds_cursor_get_cursor_info(TDSSOCKET *tds, TDSCURSOR *cursor, TDS_UINT *prow_number, TDS_UINT *prow_count)
2692 : {
2693 : int done_flags;
2694 : TDSRET retcode;
2695 : TDS_INT result_type;
2696 :
2697 24 : CHECK_TDS_EXTRA(tds);
2698 :
2699 24 : if (!cursor)
2700 : return TDS_FAIL;
2701 :
2702 24 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_get_cursor_info() cursor id = %d\n", cursor->cursor_id);
2703 :
2704 : /* Assume not known */
2705 24 : assert(prow_number && prow_count);
2706 24 : *prow_number = 0;
2707 24 : *prow_count = 0;
2708 :
2709 24 : if (IS_TDS7_PLUS(tds->conn)) {
2710 : /* Change state to querying */
2711 24 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2712 : return TDS_FAIL;
2713 :
2714 : /* Remember the server has been sent a command for this cursor */
2715 24 : tds_set_cur_cursor(tds, cursor);
2716 :
2717 : /* General initialization of server command */
2718 24 : tds_start_query(tds, TDS_RPC);
2719 :
2720 : /* Create and send query to server */
2721 24 : if (IS_TDS71_PLUS(tds->conn)) {
2722 24 : tds_put_smallint(tds, -1);
2723 24 : tds_put_smallint(tds, TDS_SP_CURSORFETCH);
2724 : } else {
2725 0 : TDS_PUT_N_AS_UCS2(tds, "sp_cursorfetch");
2726 : }
2727 :
2728 : /* This flag tells the SP only to */
2729 : /* output a dummy metadata token */
2730 :
2731 24 : tds_put_smallint(tds, 2);
2732 :
2733 : /* input cursor handle (int) */
2734 :
2735 24 : tds_put_byte(tds, 0); /* no parameter name */
2736 24 : tds_put_byte(tds, 0); /* input parameter */
2737 24 : tds_put_byte(tds, SYBINTN);
2738 24 : tds_put_byte(tds, 4);
2739 24 : tds_put_byte(tds, 4);
2740 24 : tds_put_int(tds, cursor->cursor_id);
2741 :
2742 24 : tds_put_byte(tds, 0); /* no parameter name */
2743 24 : tds_put_byte(tds, 0); /* input parameter */
2744 24 : tds_put_byte(tds, SYBINTN);
2745 24 : tds_put_byte(tds, 4);
2746 24 : tds_put_byte(tds, 4);
2747 24 : tds_put_int(tds, 0x100); /* FETCH_INFO */
2748 :
2749 : /* row number */
2750 24 : tds_put_byte(tds, 0); /* no parameter name */
2751 24 : tds_put_byte(tds, 1); /* output parameter */
2752 24 : tds_put_byte(tds, SYBINTN);
2753 24 : tds_put_byte(tds, 4);
2754 24 : tds_put_byte(tds, 0);
2755 :
2756 : /* number of rows fetched */
2757 24 : tds_put_byte(tds, 0); /* no parameter name */
2758 24 : tds_put_byte(tds, 1); /* output parameter */
2759 24 : tds_put_byte(tds, SYBINTN);
2760 24 : tds_put_byte(tds, 4);
2761 24 : tds_put_byte(tds, 0);
2762 :
2763 : /* Adjust current state */
2764 24 : tds->current_op = TDS_OP_NONE;
2765 24 : TDS_PROPAGATE(tds_query_flush_packet(tds));
2766 :
2767 : /* Process answer from server */
2768 : for (;;) {
2769 72 : retcode = tds_process_tokens(tds, &result_type, &done_flags, TDS_RETURN_PROC);
2770 72 : tdsdump_log(TDS_DBG_FUNC, "tds_cursor_get_cursor_info: tds_process_tokens returned %d\n", retcode);
2771 72 : tdsdump_log(TDS_DBG_FUNC, " result_type=%d, TDS_DONE_COUNT=%x, TDS_DONE_ERROR=%x\n",
2772 : result_type, (done_flags & TDS_DONE_COUNT), (done_flags & TDS_DONE_ERROR));
2773 72 : switch (retcode) {
2774 : case TDS_NO_MORE_RESULTS:
2775 : return TDS_SUCCESS;
2776 48 : case TDS_SUCCESS:
2777 48 : if (result_type==TDS_PARAM_RESULT) {
2778 : /* Status is updated when TDS_STATUS_RESULT token arrives, before the params are processed */
2779 24 : if (tds->has_status && tds->ret_status==0) {
2780 24 : TDSPARAMINFO *pinfo = tds->current_results;
2781 :
2782 : /* Make sure the params retuned have the correct type and size */
2783 24 : if (pinfo && pinfo->num_cols==2
2784 24 : && pinfo->columns[0]->on_server.column_type==SYBINTN
2785 24 : && pinfo->columns[1]->on_server.column_type==SYBINTN
2786 24 : && pinfo->columns[0]->column_size==4
2787 24 : && pinfo->columns[1]->column_size==4) {
2788 : /* Take the values */
2789 24 : *prow_number = (TDS_UINT)(*(TDS_INT *) pinfo->columns[0]->column_data);
2790 24 : *prow_count = (TDS_UINT)(*(TDS_INT *) pinfo->columns[1]->column_data);
2791 24 : tdsdump_log(TDS_DBG_FUNC, "----------------> prow_number=%u, prow_count=%u\n",
2792 : *prow_count, *prow_number);
2793 : }
2794 : }
2795 : }
2796 : break;
2797 : default:
2798 : return retcode;
2799 : }
2800 : }
2801 : }
2802 :
2803 : return TDS_SUCCESS;
2804 : }
2805 :
2806 : TDSRET
2807 288 : tds_cursor_close(TDSSOCKET * tds, TDSCURSOR * cursor)
2808 : {
2809 288 : CHECK_TDS_EXTRA(tds);
2810 :
2811 288 : if (!cursor)
2812 : return TDS_FAIL;
2813 :
2814 288 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_close() cursor id = %d\n", cursor->cursor_id);
2815 :
2816 288 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2817 : return TDS_FAIL;
2818 :
2819 288 : tds_set_cur_cursor(tds, cursor);
2820 :
2821 288 : if (IS_TDS50(tds->conn)) {
2822 8 : tds->out_flag = TDS_NORMAL;
2823 8 : tds_put_byte(tds, TDS_CURCLOSE_TOKEN);
2824 8 : tds_put_smallint(tds, 5); /* length of the data stream that follows */
2825 8 : tds_put_int(tds, cursor->cursor_id); /* cursor id returned by the server is available now */
2826 :
2827 8 : if (cursor->status.dealloc == TDS_CURSOR_STATE_REQUESTED) {
2828 2 : tds_put_byte(tds, 0x01); /* Close option: TDS_CUR_COPT_DEALLOC */
2829 2 : cursor->status.dealloc = TDS_CURSOR_STATE_SENT;
2830 : }
2831 : else
2832 6 : tds_put_byte(tds, 0x00); /* Close option: TDS_CUR_COPT_UNUSED */
2833 :
2834 : }
2835 288 : if (IS_TDS7_PLUS(tds->conn)) {
2836 :
2837 : /* RPC call to sp_cursorclose */
2838 280 : tds_start_query(tds, TDS_RPC);
2839 :
2840 280 : if (IS_TDS71_PLUS(tds->conn)) {
2841 280 : tds_put_smallint(tds, -1);
2842 280 : tds_put_smallint(tds, TDS_SP_CURSORCLOSE);
2843 : } else {
2844 0 : TDS_PUT_N_AS_UCS2(tds, "sp_cursorclose");
2845 : }
2846 :
2847 : /* This flag tells the SP to output only a dummy metadata token */
2848 :
2849 280 : tds_put_smallint(tds, 2);
2850 :
2851 : /* input cursor handle (int) */
2852 :
2853 280 : tds_put_byte(tds, 0); /* no parameter name */
2854 280 : tds_put_byte(tds, 0); /* input parameter */
2855 280 : tds_put_byte(tds, SYBINTN);
2856 280 : tds_put_byte(tds, 4);
2857 280 : tds_put_byte(tds, 4);
2858 280 : tds_put_int(tds, cursor->cursor_id);
2859 280 : tds->current_op = TDS_OP_CURSORCLOSE;
2860 : }
2861 288 : return tds_query_flush_packet(tds);
2862 :
2863 : }
2864 :
2865 : TDSRET
2866 248 : tds_cursor_setname(TDSSOCKET * tds, TDSCURSOR * cursor)
2867 : {
2868 : TDSFREEZE outer;
2869 : unsigned int written;
2870 :
2871 248 : CHECK_TDS_EXTRA(tds);
2872 :
2873 248 : if (!cursor)
2874 : return TDS_FAIL;
2875 :
2876 248 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_setname() cursor id = %d\n", cursor->cursor_id);
2877 :
2878 248 : if (!IS_TDS7_PLUS(tds->conn))
2879 : return TDS_SUCCESS;
2880 :
2881 248 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2882 : return TDS_FAIL;
2883 :
2884 248 : tds_set_cur_cursor(tds, cursor);
2885 :
2886 : /* RPC call to sp_cursoroption */
2887 248 : tds_start_query(tds, TDS_RPC);
2888 :
2889 248 : if (IS_TDS71_PLUS(tds->conn)) {
2890 248 : tds_put_smallint(tds, -1);
2891 248 : tds_put_smallint(tds, TDS_SP_CURSOROPTION);
2892 : } else {
2893 0 : TDS_PUT_N_AS_UCS2(tds, "sp_cursoroption");
2894 : }
2895 :
2896 248 : tds_put_smallint(tds, 0);
2897 :
2898 : /* input cursor handle (int) */
2899 248 : tds_put_byte(tds, 0); /* no parameter name */
2900 248 : tds_put_byte(tds, 0); /* input parameter */
2901 248 : tds_put_byte(tds, SYBINTN);
2902 248 : tds_put_byte(tds, 4);
2903 248 : tds_put_byte(tds, 4);
2904 248 : tds_put_int(tds, cursor->cursor_id);
2905 :
2906 : /* code, 2 == set cursor name */
2907 248 : tds_put_byte(tds, 0); /* no parameter name */
2908 248 : tds_put_byte(tds, 0); /* input parameter */
2909 248 : tds_put_byte(tds, SYBINTN);
2910 248 : tds_put_byte(tds, 4);
2911 248 : tds_put_byte(tds, 4);
2912 248 : tds_put_int(tds, 2);
2913 :
2914 : /* cursor name */
2915 248 : tds_put_byte(tds, 0);
2916 248 : tds_put_byte(tds, 0);
2917 248 : tds_put_byte(tds, XSYBNVARCHAR);
2918 248 : tds_freeze(tds, &outer, 2);
2919 248 : if (IS_TDS71_PLUS(tds->conn))
2920 248 : tds_put_n(tds, tds->conn->collation, 5);
2921 248 : TDS_START_LEN_USMALLINT(tds) {
2922 248 : tds_put_string(tds, cursor->cursor_name, -1);
2923 248 : written = tds_freeze_written(current_freeze) - 2;
2924 248 : } TDS_END_LEN
2925 248 : tds_freeze_close_len(&outer, written);
2926 :
2927 248 : tds->current_op = TDS_OP_CURSOROPTION;
2928 :
2929 248 : return tds_query_flush_packet(tds);
2930 : }
2931 :
2932 : TDSRET
2933 320 : tds_cursor_update(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_CURSOR_OPERATION op, TDS_INT i_row, TDSPARAMINFO *params)
2934 : {
2935 320 : CHECK_TDS_EXTRA(tds);
2936 :
2937 320 : if (!cursor)
2938 : return TDS_FAIL;
2939 :
2940 320 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_update() cursor id = %d\n", cursor->cursor_id);
2941 :
2942 : /* client must provide parameters for update */
2943 320 : if (op == TDS_CURSOR_UPDATE && (!params || params->num_cols <= 0))
2944 : return TDS_FAIL;
2945 :
2946 320 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
2947 : return TDS_FAIL;
2948 :
2949 320 : tds_set_cur_cursor(tds, cursor);
2950 :
2951 320 : if (IS_TDS50(tds->conn)) {
2952 0 : tds->out_flag = TDS_NORMAL;
2953 :
2954 : /* FIXME finish*/
2955 0 : tds_set_state(tds, TDS_IDLE);
2956 0 : return TDS_FAIL;
2957 : }
2958 320 : if (IS_TDS7_PLUS(tds->conn)) {
2959 :
2960 : /* RPC call to sp_cursorclose */
2961 320 : tds_start_query(tds, TDS_RPC);
2962 :
2963 320 : if (IS_TDS71_PLUS(tds->conn)) {
2964 320 : tds_put_smallint(tds, -1);
2965 320 : tds_put_smallint(tds, TDS_SP_CURSOR);
2966 : } else {
2967 0 : TDS_PUT_N_AS_UCS2(tds, "sp_cursor");
2968 : }
2969 :
2970 320 : tds_put_smallint(tds, 0);
2971 :
2972 : /* input cursor handle (int) */
2973 320 : tds_put_byte(tds, 0); /* no parameter name */
2974 320 : tds_put_byte(tds, 0); /* input parameter */
2975 320 : tds_put_byte(tds, SYBINTN);
2976 320 : tds_put_byte(tds, 4);
2977 320 : tds_put_byte(tds, 4);
2978 320 : tds_put_int(tds, cursor->cursor_id);
2979 :
2980 : /* cursor operation */
2981 320 : tds_put_byte(tds, 0); /* no parameter name */
2982 320 : tds_put_byte(tds, 0); /* input parameter */
2983 320 : tds_put_byte(tds, SYBINTN);
2984 320 : tds_put_byte(tds, 4);
2985 320 : tds_put_byte(tds, 4);
2986 320 : tds_put_int(tds, 32 | op);
2987 :
2988 : /* row number */
2989 320 : tds_put_byte(tds, 0); /* no parameter name */
2990 320 : tds_put_byte(tds, 0); /* input parameter */
2991 320 : tds_put_byte(tds, SYBINTN);
2992 320 : tds_put_byte(tds, 4);
2993 320 : tds_put_byte(tds, 4);
2994 320 : tds_put_int(tds, i_row);
2995 :
2996 : /* update require table name */
2997 320 : if (op == TDS_CURSOR_UPDATE) {
2998 : TDSCOLUMN *param;
2999 : unsigned int n, num_params;
3000 80 : const char *table_name = NULL;
3001 : TDSFREEZE outer;
3002 : unsigned int written;
3003 :
3004 : /* empty table name */
3005 80 : tds_put_byte(tds, 0);
3006 80 : tds_put_byte(tds, 0);
3007 80 : tds_put_byte(tds, XSYBNVARCHAR);
3008 80 : num_params = params->num_cols;
3009 80 : for (n = 0; n < num_params; ++n) {
3010 80 : param = params->columns[n];
3011 160 : if (!tds_dstr_isempty(¶m->table_name)) {
3012 160 : table_name = tds_dstr_cstr(¶m->table_name);
3013 80 : break;
3014 : }
3015 : }
3016 :
3017 80 : tds_freeze(tds, &outer, 2);
3018 80 : if (IS_TDS71_PLUS(tds->conn))
3019 80 : tds_put_n(tds, tds->conn->collation, 5);
3020 80 : TDS_START_LEN_USMALLINT(tds) {
3021 80 : if (table_name)
3022 80 : tds_put_string(tds, table_name, -1);
3023 80 : written = tds_freeze_written(current_freeze) - 2;
3024 80 : } TDS_END_LEN
3025 80 : tds_freeze_close_len(&outer, written);
3026 :
3027 : /* output columns to update */
3028 224 : for (n = 0; n < num_params; ++n) {
3029 144 : param = params->columns[n];
3030 : /* TODO check error */
3031 144 : tds_put_data_info(tds, param, TDS_PUT_DATA_USE_NAME|TDS_PUT_DATA_PREFIX_NAME);
3032 : /* FIXME handle error */
3033 144 : tds_put_data(tds, param);
3034 : }
3035 : }
3036 :
3037 320 : tds->current_op = TDS_OP_CURSOR;
3038 : }
3039 320 : return tds_query_flush_packet(tds);
3040 : }
3041 :
3042 : /**
3043 : * Check if a cursor is allocated into the server.
3044 : * If is not allocated it assures is removed from the connection list
3045 : * \tds
3046 : * \return true if allocated false otherwise
3047 : */
3048 : static bool
3049 2336 : tds_cursor_check_allocated(TDSCONNECTION * conn, TDSCURSOR * cursor)
3050 : {
3051 2336 : if (cursor->srv_status == TDS_CUR_ISTAT_UNUSED || (cursor->srv_status & TDS_CUR_ISTAT_DEALLOC) != 0
3052 286 : || (IS_TDS7_PLUS(conn) && (cursor->srv_status & TDS_CUR_ISTAT_CLOSED) != 0)) {
3053 2330 : tds_cursor_deallocated(conn, cursor);
3054 2330 : return false;
3055 : }
3056 :
3057 : return true;
3058 : }
3059 :
3060 : /**
3061 : * Send a deallocation request to server
3062 : */
3063 : TDSRET
3064 2336 : tds_cursor_dealloc(TDSSOCKET * tds, TDSCURSOR * cursor)
3065 : {
3066 2336 : TDSRET res = TDS_SUCCESS;
3067 :
3068 2336 : CHECK_TDS_EXTRA(tds);
3069 :
3070 2336 : if (!cursor)
3071 : return TDS_FAIL;
3072 :
3073 2336 : if (!tds_cursor_check_allocated(tds->conn, cursor))
3074 : return TDS_SUCCESS;
3075 :
3076 6 : tdsdump_log(TDS_DBG_INFO1, "tds_cursor_dealloc() cursor id = %d\n", cursor->cursor_id);
3077 :
3078 6 : if (IS_TDS50(tds->conn)) {
3079 6 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
3080 : return TDS_FAIL;
3081 6 : tds_set_cur_cursor(tds, cursor);
3082 :
3083 6 : tds->out_flag = TDS_NORMAL;
3084 6 : tds_put_byte(tds, TDS_CURCLOSE_TOKEN);
3085 6 : tds_put_smallint(tds, 5); /* length of the data stream that follows */
3086 6 : tds_put_int(tds, cursor->cursor_id); /* cursor id returned by the server is available now */
3087 6 : tds_put_byte(tds, 0x01); /* Close option: TDS_CUR_COPT_DEALLOC */
3088 6 : res = tds_query_flush_packet(tds);
3089 : }
3090 :
3091 : /*
3092 : * in TDS 5 the cursor deallocate function involves
3093 : * a server interaction. The cursor will be freed
3094 : * when we receive acknowledgement of the cursor
3095 : * deallocate from the server. for TDS 7 we do it
3096 : * here...
3097 : */
3098 6 : if (IS_TDS7_PLUS(tds->conn)) {
3099 0 : if (cursor->status.dealloc == TDS_CURSOR_STATE_SENT ||
3100 : cursor->status.dealloc == TDS_CURSOR_STATE_REQUESTED) {
3101 0 : tdsdump_log(TDS_DBG_ERROR, "tds_cursor_dealloc(): freeing cursor \n");
3102 : }
3103 : }
3104 :
3105 : return res;
3106 : }
3107 :
3108 : /**
3109 : * Deallocate cursor on idle.
3110 : * This let libTDS close the cursor when possible.
3111 : * \tds
3112 : * \param cursor cursor to close
3113 : */
3114 : TDSRET
3115 0 : tds_deferred_cursor_dealloc(TDSCONNECTION *conn, TDSCURSOR * cursor)
3116 : {
3117 : CHECK_CONN_EXTRA(conn);
3118 0 : CHECK_CURSOR_EXTRA(cursor);
3119 :
3120 : /* do not mark if already deallocated */
3121 0 : if (!tds_cursor_check_allocated(conn, cursor))
3122 : return TDS_SUCCESS;
3123 :
3124 0 : cursor->defer_close = true;
3125 0 : conn->pending_close = 1;
3126 :
3127 0 : return TDS_SUCCESS;
3128 : }
3129 :
3130 : /**
3131 : * Send a string to server while quoting it.
3132 : * \tds
3133 : * \param s string start
3134 : * \param end string end
3135 : */
3136 : static void
3137 732 : tds_quote_and_put(TDSSOCKET * tds, const char *s, const char *end)
3138 : {
3139 : char buf[256];
3140 : int i;
3141 :
3142 732 : CHECK_TDS_EXTRA(tds);
3143 :
3144 2906 : for (i = 0; s != end; ++s) {
3145 2174 : buf[i++] = *s;
3146 2174 : if (*s == '\'')
3147 0 : buf[i++] = '\'';
3148 2174 : if (i >= 254) {
3149 0 : tds_put_string(tds, buf, i);
3150 0 : i = 0;
3151 : }
3152 : }
3153 732 : tds_put_string(tds, buf, i);
3154 732 : }
3155 :
3156 : typedef struct tds_quoteout_stream {
3157 : TDSOUTSTREAM stream;
3158 : TDSSOCKET *tds;
3159 : char buffer[2048];
3160 : } TDSQUOTEOUTSTREAM;
3161 :
3162 : static int
3163 802 : tds_quoteout_stream_write(TDSOUTSTREAM *stream, size_t len)
3164 : {
3165 802 : TDSQUOTEOUTSTREAM *s = (TDSQUOTEOUTSTREAM *) stream;
3166 802 : TDSSOCKET *tds = s->tds;
3167 : uint16_t buf[sizeof(s->buffer)];
3168 :
3169 802 : assert(len <= stream->buf_len);
3170 :
3171 : #define QUOTE(type, ch) do { \
3172 : type *src, *dst = (type *) buf, *end = (type *) (s->buffer + len); \
3173 : \
3174 : for (src = (type *) s->buffer; src < end; ++src) { \
3175 : if (*src == (ch)) \
3176 : *dst++ = *src; \
3177 : *dst++ = *src; \
3178 : } \
3179 : tds_put_n(tds, buf, (char *) dst - (char *) buf); \
3180 : } while(0)
3181 :
3182 802 : if (IS_TDS7_PLUS(tds->conn))
3183 560 : QUOTE(uint16_t, TDS_HOST2LE('\''));
3184 : else
3185 242 : QUOTE(char, '\'');
3186 :
3187 : #undef QUOTE
3188 :
3189 802 : return len;
3190 : }
3191 :
3192 : static void
3193 : tds_quoteout_stream_init(TDSQUOTEOUTSTREAM * stream, TDSSOCKET * tds)
3194 : {
3195 678 : stream->stream.write = tds_quoteout_stream_write;
3196 678 : stream->stream.buffer = stream->buffer;
3197 678 : stream->stream.buf_len = sizeof(stream->buffer);
3198 678 : stream->tds = tds;
3199 : }
3200 :
3201 : static TDSRET
3202 678 : tds_put_char_param_as_string(TDSSOCKET * tds, const TDSCOLUMN *curcol)
3203 : {
3204 : TDS_CHAR *src;
3205 678 : TDSICONV *char_conv = curcol->char_conv;
3206 : int from, to;
3207 : TDSSTATICINSTREAM r;
3208 : TDSQUOTEOUTSTREAM w;
3209 :
3210 678 : src = (TDS_CHAR *) curcol->column_data;
3211 678 : if (is_blob_col(curcol))
3212 70 : src = ((TDSBLOB *)src)->textvalue;
3213 :
3214 678 : if (is_unicode_type(curcol->on_server.column_type))
3215 0 : tds_put_string(tds, "N", 1);
3216 678 : tds_put_string(tds, "\'", 1);
3217 :
3218 : /* Compute proper characted conversion.
3219 : * The conversion should be to UTF16/UCS2 for MS SQL.
3220 : * Avoid double conversion, convert directly from client to server.
3221 : */
3222 678 : from = char_conv ? char_conv->from.charset.canonic : tds->conn->char_convs[client2ucs2]->from.charset.canonic;
3223 678 : to = tds->conn->char_convs[IS_TDS7_PLUS(tds->conn) ? client2ucs2 : client2server_chardata]->to.charset.canonic;
3224 678 : if (!char_conv || char_conv->to.charset.canonic != to)
3225 568 : char_conv = tds_iconv_get_info(tds->conn, from, to);
3226 678 : if (!char_conv)
3227 : return TDS_FAIL;
3228 :
3229 678 : tds_staticin_stream_init(&r, src, curcol->column_cur_size);
3230 678 : tds_quoteout_stream_init(&w, tds);
3231 :
3232 678 : tds_convert_stream(tds, char_conv, to_server, &r.stream, &w.stream);
3233 :
3234 678 : tds_put_string(tds, "\'", 1);
3235 678 : return TDS_SUCCESS;
3236 : }
3237 :
3238 : /**
3239 : * Send a parameter to server.
3240 : * Parameters are converted to string and sent to server.
3241 : * \tds
3242 : * \param params parameters structure
3243 : * \param n number of parameter to send
3244 : * \returns TDS_FAIL or TDS_SUCCESS
3245 : */
3246 : static TDSRET
3247 1484 : tds_put_param_as_string(TDSSOCKET * tds, TDSPARAMINFO * params, int n)
3248 : {
3249 1484 : TDSCOLUMN *curcol = params->columns[n];
3250 : CONV_RESULT cr;
3251 : TDS_INT res;
3252 : TDS_CHAR *src;
3253 1484 : int src_len = curcol->column_cur_size;
3254 :
3255 : int i;
3256 : char buf[256];
3257 1484 : bool quote = false;
3258 :
3259 1484 : CHECK_TDS_EXTRA(tds);
3260 1484 : CHECK_PARAMINFO_EXTRA(params);
3261 :
3262 1484 : if (src_len < 0) {
3263 : /* on TDS 4 TEXT/IMAGE cannot be NULL, use empty */
3264 42 : if (!IS_TDS50_PLUS(tds->conn) && type_has_textptr(curcol->on_server.column_type))
3265 0 : tds_put_string(tds, "''", 2);
3266 : else
3267 42 : tds_put_string(tds, "NULL", 4);
3268 : return TDS_SUCCESS;
3269 : }
3270 :
3271 1442 : if (is_char_type(curcol->on_server.column_type))
3272 678 : return tds_put_char_param_as_string(tds, curcol);
3273 :
3274 764 : src = (TDS_CHAR *) curcol->column_data;
3275 764 : if (is_blob_col(curcol))
3276 28 : src = ((TDSBLOB *)src)->textvalue;
3277 :
3278 : /* we could try to use only tds_convert but is not good in all cases */
3279 764 : switch (curcol->on_server.column_type) {
3280 : /* binary/char, do conversion in line */
3281 32 : case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY:
3282 32 : tds_put_string(tds, "0x", 2);
3283 423242 : for (i=0; src_len; ++src, --src_len) {
3284 423210 : buf[i++] = tds_hex_digits[*src >> 4 & 0xF];
3285 423210 : buf[i++] = tds_hex_digits[*src & 0xF];
3286 423210 : if (i == 256) {
3287 3300 : tds_put_string(tds, buf, i);
3288 3300 : i = 0;
3289 : }
3290 : }
3291 32 : tds_put_string(tds, buf, i);
3292 32 : break;
3293 : /* TODO date, use iso format */
3294 18 : case SYBDATETIME:
3295 : case SYBDATETIME4:
3296 : case SYBDATETIMN:
3297 : case SYBMSTIME:
3298 : case SYBMSDATE:
3299 : case SYBMSDATETIME2:
3300 : case SYBMSDATETIMEOFFSET:
3301 : case SYBTIME:
3302 : case SYBDATE:
3303 : case SYB5BIGTIME:
3304 : case SYB5BIGDATETIME:
3305 : /* TODO use an ISO context */
3306 : case SYBUNIQUE:
3307 18 : quote = true;
3308 732 : default:
3309 732 : res = tds_convert(tds_get_ctx(tds), tds_get_conversion_type(curcol->on_server.column_type, curcol->column_size), src, src_len, SYBCHAR, &cr);
3310 732 : if (res < 0)
3311 : return TDS_FAIL;
3312 :
3313 732 : if (quote)
3314 18 : tds_put_string(tds, "\'", 1);
3315 732 : tds_quote_and_put(tds, cr.c, cr.c + res);
3316 732 : if (quote)
3317 18 : tds_put_string(tds, "\'", 1);
3318 732 : free(cr.c);
3319 : }
3320 : return TDS_SUCCESS;
3321 : }
3322 :
3323 : /**
3324 : * Emulate prepared execute traslating to a normal language
3325 : */
3326 : static TDSRET
3327 842 : tds_send_emulated_execute(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params)
3328 : {
3329 : int num_placeholders, i;
3330 : const char *s, *e;
3331 :
3332 842 : CHECK_TDS_EXTRA(tds);
3333 :
3334 842 : assert(query);
3335 :
3336 842 : num_placeholders = tds_count_placeholders(query);
3337 1684 : if (num_placeholders && num_placeholders > params->num_cols)
3338 : return TDS_FAIL;
3339 :
3340 : /*
3341 : * NOTE: even for TDS5 we use this packet so to avoid computing
3342 : * entire sql command
3343 : */
3344 842 : tds->out_flag = TDS_QUERY;
3345 842 : if (!num_placeholders) {
3346 0 : tds_put_string(tds, query, -1);
3347 0 : return TDS_SUCCESS;
3348 : }
3349 :
3350 : s = query;
3351 1484 : for (i = 0;; ++i) {
3352 3810 : e = tds_next_placeholder(s);
3353 2326 : tds_put_string(tds, s, (int)(e ? e - s : -1));
3354 2326 : if (!e)
3355 : break;
3356 : /* now translate parameter in string */
3357 1484 : tds_put_param_as_string(tds, params, i);
3358 :
3359 1484 : s = e + 1;
3360 : }
3361 :
3362 : return TDS_SUCCESS;
3363 : }
3364 :
3365 : enum { MUL_STARTED = 1 };
3366 :
3367 : TDSRET
3368 154 : tds_multiple_init(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDS_MULTIPLE_TYPE type, TDSHEADERS * head)
3369 : {
3370 : unsigned char packet_type;
3371 154 : multiple->type = type;
3372 154 : multiple->flags = 0;
3373 :
3374 154 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
3375 : return TDS_FAIL;
3376 :
3377 154 : packet_type = TDS_QUERY;
3378 154 : switch (type) {
3379 : case TDS_MULTIPLE_QUERY:
3380 : break;
3381 66 : case TDS_MULTIPLE_EXECUTE:
3382 : case TDS_MULTIPLE_RPC:
3383 66 : if (IS_TDS7_PLUS(tds->conn))
3384 64 : packet_type = TDS_RPC;
3385 : break;
3386 : }
3387 154 : if (tds_start_query_head(tds, packet_type, head) != TDS_SUCCESS)
3388 : return TDS_FAIL;
3389 :
3390 154 : return TDS_SUCCESS;
3391 : }
3392 :
3393 : TDSRET
3394 154 : tds_multiple_done(TDSSOCKET *tds, TDSMULTIPLE *multiple)
3395 : {
3396 154 : assert(tds && multiple);
3397 :
3398 154 : return tds_query_flush_packet(tds);
3399 : }
3400 :
3401 : TDSRET
3402 640 : tds_multiple_query(TDSSOCKET *tds, TDSMULTIPLE *multiple, const char *query, TDSPARAMINFO * params)
3403 : {
3404 640 : assert(multiple->type == TDS_MULTIPLE_QUERY);
3405 :
3406 640 : if (multiple->flags & MUL_STARTED)
3407 552 : tds_put_string(tds, " ", 1);
3408 640 : multiple->flags |= MUL_STARTED;
3409 :
3410 640 : return tds_send_emulated_execute(tds, query, params);
3411 : }
3412 :
3413 : TDSRET
3414 590 : tds_multiple_execute(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDSDYNAMIC * dyn)
3415 : {
3416 590 : assert(multiple->type == TDS_MULTIPLE_EXECUTE);
3417 :
3418 590 : if (IS_TDS7_PLUS(tds->conn)) {
3419 584 : if (multiple->flags & MUL_STARTED) {
3420 : /* TODO define constant */
3421 520 : tds_put_byte(tds, IS_TDS72_PLUS(tds->conn) ? 0xff : 0x80);
3422 : }
3423 584 : multiple->flags |= MUL_STARTED;
3424 :
3425 584 : tds7_send_execute(tds, dyn);
3426 :
3427 584 : return TDS_SUCCESS;
3428 : }
3429 :
3430 6 : if (multiple->flags & MUL_STARTED)
3431 4 : tds_put_string(tds, " ", 1);
3432 6 : multiple->flags |= MUL_STARTED;
3433 :
3434 6 : return tds_send_emulated_execute(tds, dyn->query, dyn->params);
3435 : }
3436 :
3437 : /**
3438 : * Send option commands to server.
3439 : * Option commands are used to change server options.
3440 : * \tds
3441 : * \param command command type.
3442 : * \param option option to set/get.
3443 : * \param param parameter value
3444 : * \param param_size length of parameter value in bytes
3445 : */
3446 : TDSRET
3447 80 : tds_submit_optioncmd(TDSSOCKET * tds, TDS_OPTION_CMD command, TDS_OPTION option, TDS_OPTION_ARG *param, TDS_INT param_size)
3448 : {
3449 : char cmd[128];
3450 :
3451 80 : CHECK_TDS_EXTRA(tds);
3452 :
3453 80 : tdsdump_log(TDS_DBG_FUNC, "tds_submit_optioncmd() \n");
3454 :
3455 80 : if (IS_TDS50(tds->conn)) {
3456 16 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
3457 : return TDS_FAIL;
3458 :
3459 16 : tds->out_flag = TDS_NORMAL;
3460 16 : tds_put_byte(tds, TDS_OPTIONCMD_TOKEN);
3461 :
3462 16 : tds_put_smallint(tds, 3 + param_size);
3463 16 : tds_put_byte(tds, command);
3464 16 : tds_put_byte(tds, option);
3465 16 : tds_put_byte(tds, param_size);
3466 16 : if (param_size)
3467 8 : tds_put_n(tds, param, param_size);
3468 :
3469 16 : tds_query_flush_packet(tds);
3470 :
3471 16 : TDS_PROPAGATE(tds_process_simple_query(tds));
3472 16 : return TDS_SUCCESS;
3473 : }
3474 :
3475 64 : if (!IS_TDS7_PLUS(tds->conn))
3476 : return TDS_SUCCESS;
3477 :
3478 64 : cmd[0] = 0;
3479 64 : if (command == TDS_OPT_SET) {
3480 : char datefmt[4];
3481 :
3482 32 : switch (option) {
3483 8 : case TDS_OPT_ANSINULL :
3484 8 : sprintf(cmd, "SET ANSI_NULLS %s", param->ti ? "ON" : "OFF");
3485 8 : break;
3486 0 : case TDS_OPT_ARITHABORTON :
3487 0 : strcpy(cmd, "SET ARITHABORT ON");
3488 0 : break;
3489 0 : case TDS_OPT_ARITHABORTOFF :
3490 0 : strcpy(cmd, "SET ARITHABORT OFF");
3491 0 : break;
3492 0 : case TDS_OPT_ARITHIGNOREON :
3493 0 : strcpy(cmd, "SET ARITHIGNORE ON");
3494 0 : break;
3495 0 : case TDS_OPT_ARITHIGNOREOFF :
3496 0 : strcpy(cmd, "SET ARITHIGNORE OFF");
3497 0 : break;
3498 8 : case TDS_OPT_CHAINXACTS :
3499 8 : sprintf(cmd, "SET IMPLICIT_TRANSACTIONS %s", param->ti ? "ON" : "OFF");
3500 8 : break;
3501 0 : case TDS_OPT_CURCLOSEONXACT :
3502 0 : sprintf(cmd, "SET CURSOR_CLOSE_ON_COMMIT %s", param->ti ? "ON" : "OFF");
3503 0 : break;
3504 0 : case TDS_OPT_NOCOUNT :
3505 0 : sprintf(cmd, "SET NOCOUNT %s", param->ti ? "ON" : "OFF");
3506 0 : break;
3507 0 : case TDS_OPT_QUOTED_IDENT :
3508 0 : sprintf(cmd, "SET QUOTED_IDENTIFIER %s", param->ti ? "ON" : "OFF");
3509 0 : break;
3510 0 : case TDS_OPT_TRUNCABORT :
3511 0 : sprintf(cmd, "SET ANSI_WARNINGS %s", param->ti ? "OFF" : "ON");
3512 0 : break;
3513 8 : case TDS_OPT_DATEFIRST :
3514 8 : sprintf(cmd, "SET DATEFIRST %d", param->ti);
3515 8 : break;
3516 8 : case TDS_OPT_DATEFORMAT :
3517 8 : switch (param->ti) {
3518 0 : case TDS_OPT_FMTDMY: strcpy(datefmt,"dmy"); break;
3519 0 : case TDS_OPT_FMTDYM: strcpy(datefmt,"dym"); break;
3520 0 : case TDS_OPT_FMTMDY: strcpy(datefmt,"mdy"); break;
3521 8 : case TDS_OPT_FMTMYD: strcpy(datefmt,"myd"); break;
3522 0 : case TDS_OPT_FMTYDM: strcpy(datefmt,"ydm"); break;
3523 0 : case TDS_OPT_FMTYMD: strcpy(datefmt,"ymd"); break;
3524 : }
3525 8 : sprintf(cmd, "SET DATEFORMAT %s", datefmt);
3526 8 : break;
3527 0 : case TDS_OPT_TEXTSIZE:
3528 0 : sprintf(cmd, "SET TEXTSIZE %d", (int) param->i);
3529 0 : break;
3530 : /* TODO */
3531 : case TDS_OPT_STAT_TIME:
3532 : case TDS_OPT_STAT_IO:
3533 : case TDS_OPT_ROWCOUNT:
3534 : case TDS_OPT_NATLANG:
3535 : case TDS_OPT_ISOLATION:
3536 : case TDS_OPT_AUTHON:
3537 : case TDS_OPT_CHARSET:
3538 : case TDS_OPT_SHOWPLAN:
3539 : case TDS_OPT_NOEXEC:
3540 : case TDS_OPT_PARSEONLY:
3541 : case TDS_OPT_GETDATA:
3542 : case TDS_OPT_FORCEPLAN:
3543 : case TDS_OPT_FORMATONLY:
3544 : case TDS_OPT_FIPSFLAG:
3545 : case TDS_OPT_RESTREES:
3546 : case TDS_OPT_IDENTITYON:
3547 : case TDS_OPT_CURREAD:
3548 : case TDS_OPT_CURWRITE:
3549 : case TDS_OPT_IDENTITYOFF:
3550 : case TDS_OPT_AUTHOFF:
3551 : break;
3552 : }
3553 32 : tds_submit_query(tds, cmd);
3554 32 : TDS_PROPAGATE(tds_process_simple_query(tds));
3555 : }
3556 64 : if (command == TDS_OPT_LIST) {
3557 32 : int optionval = 0;
3558 : TDS_INT resulttype;
3559 :
3560 32 : switch (option) {
3561 16 : case TDS_OPT_ANSINULL :
3562 : case TDS_OPT_ARITHABORTON :
3563 : case TDS_OPT_ARITHABORTOFF :
3564 : case TDS_OPT_ARITHIGNOREON :
3565 : case TDS_OPT_ARITHIGNOREOFF :
3566 : case TDS_OPT_CHAINXACTS :
3567 : case TDS_OPT_CURCLOSEONXACT :
3568 : case TDS_OPT_NOCOUNT :
3569 : case TDS_OPT_QUOTED_IDENT :
3570 : case TDS_OPT_TRUNCABORT :
3571 16 : tdsdump_log(TDS_DBG_FUNC, "SELECT @@options\n");
3572 16 : strcpy(cmd, "SELECT @@options");
3573 16 : break;
3574 8 : case TDS_OPT_DATEFIRST :
3575 8 : strcpy(cmd, "SELECT @@datefirst");
3576 8 : break;
3577 8 : case TDS_OPT_DATEFORMAT :
3578 8 : strcpy(cmd, "SELECT DATEPART(dy,'01/02/03')");
3579 8 : break;
3580 0 : case TDS_OPT_TEXTSIZE:
3581 0 : strcpy(cmd, "SELECT @@textsize");
3582 0 : break;
3583 : /* TODO */
3584 0 : case TDS_OPT_STAT_TIME:
3585 : case TDS_OPT_STAT_IO:
3586 : case TDS_OPT_ROWCOUNT:
3587 : case TDS_OPT_NATLANG:
3588 : case TDS_OPT_ISOLATION:
3589 : case TDS_OPT_AUTHON:
3590 : case TDS_OPT_CHARSET:
3591 : case TDS_OPT_SHOWPLAN:
3592 : case TDS_OPT_NOEXEC:
3593 : case TDS_OPT_PARSEONLY:
3594 : case TDS_OPT_GETDATA:
3595 : case TDS_OPT_FORCEPLAN:
3596 : case TDS_OPT_FORMATONLY:
3597 : case TDS_OPT_FIPSFLAG:
3598 : case TDS_OPT_RESTREES:
3599 : case TDS_OPT_IDENTITYON:
3600 : case TDS_OPT_CURREAD:
3601 : case TDS_OPT_CURWRITE:
3602 : case TDS_OPT_IDENTITYOFF:
3603 : case TDS_OPT_AUTHOFF:
3604 : default:
3605 0 : tdsdump_log(TDS_DBG_FUNC, "what!\n");
3606 : }
3607 : tds_submit_query(tds, cmd);
3608 96 : while (tds_process_tokens(tds, &resulttype, NULL, TDS_TOKEN_RESULTS) == TDS_SUCCESS) {
3609 64 : switch (resulttype) {
3610 : case TDS_ROWFMT_RESULT:
3611 : break;
3612 : case TDS_ROW_RESULT:
3613 64 : while (tds_process_tokens(tds, &resulttype, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW) == TDS_SUCCESS) {
3614 64 : if (resulttype != TDS_ROW_RESULT)
3615 : break;
3616 :
3617 32 : if (!tds->current_results || tds->current_results->num_cols < 1)
3618 0 : continue;
3619 :
3620 : /* keep previous value if NULL or not convertible */
3621 32 : optionval = tds_get_int_col(tds, tds->current_results->columns[0], optionval);
3622 : }
3623 : break;
3624 : default:
3625 : break;
3626 : }
3627 : }
3628 32 : tdsdump_log(TDS_DBG_FUNC, "optionval = %d\n", optionval);
3629 32 : switch (option) {
3630 8 : case TDS_OPT_CHAINXACTS :
3631 8 : tds->option_value = (optionval & 0x02) > 0;
3632 8 : break;
3633 0 : case TDS_OPT_CURCLOSEONXACT :
3634 0 : tds->option_value = (optionval & 0x04) > 0;
3635 0 : break;
3636 0 : case TDS_OPT_TRUNCABORT :
3637 0 : tds->option_value = (optionval & 0x08) > 0;
3638 0 : break;
3639 8 : case TDS_OPT_ANSINULL :
3640 8 : tds->option_value = (optionval & 0x20) > 0;
3641 8 : break;
3642 0 : case TDS_OPT_ARITHABORTON :
3643 0 : tds->option_value = (optionval & 0x40) > 0;
3644 0 : break;
3645 0 : case TDS_OPT_ARITHABORTOFF :
3646 0 : tds->option_value = (optionval & 0x40) > 0;
3647 0 : break;
3648 0 : case TDS_OPT_ARITHIGNOREON :
3649 0 : tds->option_value = (optionval & 0x80) > 0;
3650 0 : break;
3651 0 : case TDS_OPT_ARITHIGNOREOFF :
3652 0 : tds->option_value = (optionval & 0x80) > 0;
3653 0 : break;
3654 0 : case TDS_OPT_QUOTED_IDENT :
3655 0 : tds->option_value = (optionval & 0x0100) > 0;
3656 0 : break;
3657 0 : case TDS_OPT_NOCOUNT :
3658 0 : tds->option_value = (optionval & 0x0200) > 0;
3659 0 : break;
3660 8 : case TDS_OPT_TEXTSIZE:
3661 : case TDS_OPT_DATEFIRST :
3662 8 : tds->option_value = optionval;
3663 8 : break;
3664 8 : case TDS_OPT_DATEFORMAT :
3665 8 : switch (optionval) {
3666 0 : case 61: tds->option_value = TDS_OPT_FMTYDM; break;
3667 0 : case 34: tds->option_value = TDS_OPT_FMTYMD; break;
3668 0 : case 32: tds->option_value = TDS_OPT_FMTDMY; break;
3669 0 : case 60: tds->option_value = TDS_OPT_FMTYDM; break;
3670 0 : case 2: tds->option_value = TDS_OPT_FMTMDY; break;
3671 8 : case 3: tds->option_value = TDS_OPT_FMTMYD; break;
3672 : }
3673 : break;
3674 : /* TODO */
3675 : case TDS_OPT_STAT_TIME:
3676 : case TDS_OPT_STAT_IO:
3677 : case TDS_OPT_ROWCOUNT:
3678 : case TDS_OPT_NATLANG:
3679 : case TDS_OPT_ISOLATION:
3680 : case TDS_OPT_AUTHON:
3681 : case TDS_OPT_CHARSET:
3682 : case TDS_OPT_SHOWPLAN:
3683 : case TDS_OPT_NOEXEC:
3684 : case TDS_OPT_PARSEONLY:
3685 : case TDS_OPT_GETDATA:
3686 : case TDS_OPT_FORCEPLAN:
3687 : case TDS_OPT_FORMATONLY:
3688 : case TDS_OPT_FIPSFLAG:
3689 : case TDS_OPT_RESTREES:
3690 : case TDS_OPT_IDENTITYON:
3691 : case TDS_OPT_CURREAD:
3692 : case TDS_OPT_CURWRITE:
3693 : case TDS_OPT_IDENTITYOFF:
3694 : case TDS_OPT_AUTHOFF:
3695 : break;
3696 : }
3697 32 : tdsdump_log(TDS_DBG_FUNC, "tds_submit_optioncmd: returned option_value = %d\n", tds->option_value);
3698 : }
3699 : return TDS_SUCCESS;
3700 : }
3701 :
3702 :
3703 : /**
3704 : * Send a rollback request.
3705 : * TDS 7.2+ need this in order to handle transactions correctly if MARS is used.
3706 : * \tds
3707 : * \sa tds_submit_commit, tds_submit_rollback
3708 : */
3709 : TDSRET
3710 132 : tds_submit_begin_tran(TDSSOCKET *tds)
3711 : {
3712 132 : CHECK_TDS_EXTRA(tds);
3713 :
3714 132 : if (!IS_TDS72_PLUS(tds->conn))
3715 78 : return tds_submit_query(tds, "BEGIN TRANSACTION");
3716 :
3717 54 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
3718 : return TDS_FAIL;
3719 :
3720 54 : tds_start_query(tds, TDS7_TRANS);
3721 :
3722 : /* begin transaction */
3723 54 : tds_put_smallint(tds, 5);
3724 54 : tds_put_byte(tds, 0); /* new transaction level TODO */
3725 54 : tds_put_byte(tds, 0); /* new transaction name */
3726 :
3727 54 : return tds_query_flush_packet(tds);
3728 : }
3729 :
3730 : /**
3731 : * Send a rollback request.
3732 : * TDS 7.2+ need this in order to handle transactions correctly if MARS is used.
3733 : * \tds
3734 : * \param cont true to start a new transaction
3735 : * \sa tds_submit_begin_tran, tds_submit_commit
3736 : */
3737 : TDSRET
3738 516 : tds_submit_rollback(TDSSOCKET *tds, bool cont)
3739 : {
3740 516 : CHECK_TDS_EXTRA(tds);
3741 :
3742 516 : if (!IS_TDS72_PLUS(tds->conn))
3743 640 : return tds_submit_query(tds, cont ? "IF @@TRANCOUNT > 0 ROLLBACK BEGIN TRANSACTION" : "IF @@TRANCOUNT > 0 ROLLBACK");
3744 :
3745 196 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
3746 : return TDS_FAIL;
3747 :
3748 196 : tds_start_query(tds, TDS7_TRANS);
3749 196 : tds_put_smallint(tds, 8); /* rollback */
3750 196 : tds_put_byte(tds, 0); /* name */
3751 196 : if (cont) {
3752 172 : tds_put_byte(tds, 1);
3753 172 : tds_put_byte(tds, 0); /* new transaction level TODO */
3754 172 : tds_put_byte(tds, 0); /* new transaction name */
3755 : } else {
3756 24 : tds_put_byte(tds, 0); /* do not continue */
3757 : }
3758 196 : return tds_query_flush_packet(tds);
3759 : }
3760 :
3761 : /**
3762 : * Send a commit request.
3763 : * TDS 7.2+ need this in order to handle transactions correctly if MARS is used.
3764 : * \tds
3765 : * \param cont true to start a new transaction
3766 : * \sa tds_submit_rollback, tds_submit_begin_tran
3767 : */
3768 : TDSRET
3769 282 : tds_submit_commit(TDSSOCKET *tds, bool cont)
3770 : {
3771 282 : CHECK_TDS_EXTRA(tds);
3772 :
3773 282 : if (!IS_TDS72_PLUS(tds->conn))
3774 328 : return tds_submit_query(tds, cont ? "IF @@TRANCOUNT > 0 COMMIT BEGIN TRANSACTION" : "IF @@TRANCOUNT > 0 COMMIT");
3775 :
3776 118 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
3777 : return TDS_FAIL;
3778 :
3779 118 : tds_start_query(tds, TDS7_TRANS);
3780 118 : tds_put_smallint(tds, 7); /* commit */
3781 118 : tds_put_byte(tds, 0); /* name */
3782 118 : if (cont) {
3783 118 : tds_put_byte(tds, 1);
3784 118 : tds_put_byte(tds, 0); /* new transaction level TODO */
3785 118 : tds_put_byte(tds, 0); /* new transaction name */
3786 : } else {
3787 0 : tds_put_byte(tds, 0); /* do not continue */
3788 : }
3789 118 : return tds_query_flush_packet(tds);
3790 : }
3791 :
3792 : static const TDSCONTEXT empty_ctx = {0};
3793 :
3794 : TDSRET
3795 3905 : tds_disconnect(TDSSOCKET * tds)
3796 : {
3797 : TDS_INT old_timeout;
3798 : const TDSCONTEXT *old_ctx;
3799 :
3800 3905 : CHECK_TDS_EXTRA(tds);
3801 :
3802 3905 : tdsdump_log(TDS_DBG_FUNC, "tds_disconnect()\n");
3803 :
3804 3905 : if (!IS_TDS50(tds->conn))
3805 : return TDS_SUCCESS;
3806 :
3807 : /* Socket should not be busy, otherwise this can create an infinite
3808 : * loop. */
3809 766 : if (tds_mutex_trylock(&tds->wire_mtx))
3810 : return TDS_FAIL;
3811 756 : tds_mutex_unlock(&tds->wire_mtx);
3812 :
3813 756 : old_timeout = tds->query_timeout;
3814 756 : old_ctx = tds_get_ctx(tds);
3815 :
3816 : /* avoid to stall forever */
3817 756 : tds->query_timeout = 5;
3818 :
3819 : /* do not report errors to upper libraries */
3820 756 : tds_set_ctx(tds, &empty_ctx);
3821 :
3822 756 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING) {
3823 16 : tds->query_timeout = old_timeout;
3824 16 : tds_set_ctx(tds, old_ctx);
3825 16 : return TDS_FAIL;
3826 : }
3827 :
3828 740 : tds->out_flag = TDS_NORMAL;
3829 740 : tds_put_byte(tds, TDS_LOGOUT_TOKEN);
3830 740 : tds_put_byte(tds, 0);
3831 :
3832 740 : tds_query_flush_packet(tds);
3833 :
3834 740 : return tds_process_simple_query(tds);
3835 : }
3836 :
3837 : /*
3838 : * TODO add function to return type suitable for param
3839 : * ie:
3840 : * sybvarchar -> sybvarchar / xsybvarchar
3841 : * sybint4 -> sybintn
3842 : */
3843 :
3844 : /** @} */
|