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