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