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