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