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