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