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) 2005-2015 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 /**
22 * \file
23 * \brief Contains all routines to get replies from server
24 */
25 #include <config.h>
26
27 #if HAVE_STRING_H
28 #include <string.h>
29 #endif /* HAVE_STRING_H */
30
31 #if HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif /* HAVE_STDLIB_H */
34
35 #include <assert.h>
36
37 #if HAVE_MALLOC_H
38 #include <malloc.h>
39 #endif /* HAVE_MALLOC_H */
40
41 #include <freetds/tds.h>
42 #include <freetds/string.h>
43 #include <freetds/convert.h>
44 #include <freetds/iconv.h>
45 #include "tds_checks.h"
46 #include "replacements.h"
47
48 /** \cond HIDDEN_SYMBOLS */
49 #define USE_ICONV (tds->conn->use_iconv)
50 /** \endcond */
51
52 static TDSRET tds_process_msg(TDSSOCKET * tds, int marker);
53 static TDSRET tds_process_compute_result(TDSSOCKET * tds);
54 static TDSRET tds_process_compute_names(TDSSOCKET * tds);
55 static TDSRET tds7_process_compute_result(TDSSOCKET * tds);
56 static TDSRET tds_process_result(TDSSOCKET * tds);
57 static TDSRET tds_process_col_name(TDSSOCKET * tds);
58 static TDSRET tds_process_col_fmt(TDSSOCKET * tds);
59 static TDSRET tds_process_tabname(TDSSOCKET *tds);
60 static TDSRET tds_process_colinfo(TDSSOCKET * tds, char **names, int num_names);
61 static TDSRET tds_process_compute(TDSSOCKET * tds);
62 static TDSRET tds_process_cursor_tokens(TDSSOCKET * tds);
63 static TDSRET tds_process_row(TDSSOCKET * tds);
64 static TDSRET tds_process_nbcrow(TDSSOCKET * tds);
65 static TDSRET tds_process_param_result(TDSSOCKET * tds, TDSPARAMINFO ** info);
66 static TDSRET tds7_process_result(TDSSOCKET * tds);
67 static TDSDYNAMIC *tds_process_dynamic(TDSSOCKET * tds);
68 static TDSRET tds_process_auth(TDSSOCKET * tds);
69 static TDSRET tds_process_env_chg(TDSSOCKET * tds);
70 static TDSRET tds_process_param_result_tokens(TDSSOCKET * tds);
71 static TDSRET tds_process_params_result_token(TDSSOCKET * tds);
72 static TDSRET tds_process_dyn_result(TDSSOCKET * tds);
73 static TDSRET tds5_process_result(TDSSOCKET * tds);
74 static TDSRET tds5_process_dyn_result2(TDSSOCKET * tds);
75 static TDSRET tds_process_default_tokens(TDSSOCKET * tds, int marker);
76 static TDSRET tds5_process_optioncmd(TDSSOCKET * tds);
77 static TDSRET tds_process_end(TDSSOCKET * tds, int marker, /*@out@*/ int *flags_parm);
78
79 static TDSRET tds_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int is_param);
80 static /*@observer@*/ const char *tds_token_name(unsigned char marker);
81 static void adjust_character_column_size(TDSSOCKET * tds, TDSCOLUMN * curcol);
82 int determine_adjusted_size(const TDSICONV * char_conv, int size);
83 static /*@observer@*/ const char *tds_pr_op(int op);
84 static int tds_alloc_get_string(TDSSOCKET * tds, /*@special@*/ char **string, size_t len) /*allocates *string*/;
85
86 /**
87 * \ingroup libtds
88 * \defgroup token Results processing
89 * Handle tokens in packets. Many PDU (packets data unit) contain tokens.
90 * (like result description, rows, data, errors and many other).
91 */
92
93
94 /**
95 * \addtogroup token
96 * @{
97 */
98
99 /**
100 * tds_process_default_tokens() is a catch all function that is called to
101 * process tokens not known to other tds_process_* routines
102 * @tds
103 * @param marker Token type
104 */
105 static TDSRET
tds_process_default_tokens(TDSSOCKET * tds,int marker)106 tds_process_default_tokens(TDSSOCKET * tds, int marker)
107 {
108 int tok_size;
109 int done_flags;
110 TDS_INT ret_status;
111 TDS_CAPABILITY_TYPE *cap;
112
113 CHECK_TDS_EXTRA(tds);
114
115 tdsdump_log(TDS_DBG_FUNC, "tds_process_default_tokens() marker is %x(%s)\n", marker, tds_token_name(marker));
116
117 if (IS_TDSDEAD(tds)) {
118 tdsdump_log(TDS_DBG_FUNC, "leaving tds_process_default_tokens() connection dead\n");
119 tds_close_socket(tds);
120 return TDS_FAIL;
121 }
122
123 switch (marker) {
124 case TDS_AUTH_TOKEN:
125 return tds_process_auth(tds);
126 break;
127 case TDS_ENVCHANGE_TOKEN:
128 return tds_process_env_chg(tds);
129 break;
130 case TDS_DONE_TOKEN:
131 case TDS_DONEPROC_TOKEN:
132 case TDS_DONEINPROC_TOKEN:
133 return tds_process_end(tds, marker, &done_flags);
134 break;
135 case TDS_PROCID_TOKEN:
136 tds_get_n(tds, NULL, 8);
137 break;
138 case TDS_RETURNSTATUS_TOKEN:
139 ret_status = tds_get_int(tds);
140 marker = tds_peek(tds);
141 if (marker != TDS_PARAM_TOKEN && marker != TDS_DONEPROC_TOKEN
142 && marker != TDS_DONE_TOKEN && marker != TDS5_PARAMFMT2_TOKEN)
143 break;
144 tds->has_status = 1;
145 tds->ret_status = ret_status;
146 tdsdump_log(TDS_DBG_FUNC, "tds_process_default_tokens: return status is %d\n", tds->ret_status);
147 break;
148 case TDS_ERROR_TOKEN:
149 case TDS_INFO_TOKEN:
150 case TDS_EED_TOKEN:
151 return tds_process_msg(tds, marker);
152 break;
153 case TDS_CAPABILITY_TOKEN:
154 tok_size = tds_get_usmallint(tds);
155 cap = tds->conn->capabilities.types;
156 memset(cap, 0, 2*sizeof(*cap));
157 cap[0].type = 1;
158 cap[0].len = sizeof(cap[0].values);
159 cap[1].type = 2;
160 cap[1].len = sizeof(cap[1].values);
161 while (tok_size > 1) {
162 unsigned char type, size, *p;
163
164 type = tds_get_byte(tds);
165 size = tds_get_byte(tds);
166 tok_size -= 2 + size;
167 if (type != 1 && type != 2) {
168 tds_get_n(tds, NULL, size);
169 continue;
170 }
171 if (size > sizeof(cap->values)) {
172 tds_get_n(tds, NULL, size - sizeof(cap->values));
173 size = sizeof(cap->values);
174 }
175 p = (unsigned char *) &cap[type];
176 if (tds_get_n(tds, p-size, size) == NULL)
177 return TDS_FAIL;
178 /*
179 * Sybase 11.0 servers return the wrong length in the capability packet,
180 * causing us to read past the done packet.
181 */
182 if (tds->conn->product_version < TDS_SYB_VER(12, 0, 0) && type == 2)
183 break;
184 }
185 break;
186 /* PARAM_TOKEN can be returned inserting text in db, to return new timestamp */
187 case TDS_PARAM_TOKEN:
188 tds_unget_byte(tds);
189 return tds_process_param_result_tokens(tds);
190 break;
191 case TDS7_RESULT_TOKEN:
192 return tds7_process_result(tds);
193 break;
194 case TDS_OPTIONCMD_TOKEN:
195 return tds5_process_optioncmd(tds);
196 break;
197 case TDS_RESULT_TOKEN:
198 return tds_process_result(tds);
199 break;
200 case TDS_ROWFMT2_TOKEN:
201 return tds5_process_result(tds);
202 break;
203 case TDS_COLNAME_TOKEN:
204 return tds_process_col_name(tds);
205 break;
206 case TDS_COLFMT_TOKEN:
207 return tds_process_col_fmt(tds);
208 break;
209 case TDS_ROW_TOKEN:
210 return tds_process_row(tds);
211 break;
212 case TDS5_PARAMFMT_TOKEN:
213 /* store discarded parameters in param_info, not in old dynamic */
214 tds_release_cur_dyn(tds);
215 return tds_process_dyn_result(tds);
216 break;
217 case TDS5_PARAMFMT2_TOKEN:
218 tds_release_cur_dyn(tds);
219 return tds5_process_dyn_result2(tds);
220 break;
221 case TDS5_PARAMS_TOKEN:
222 /* save params */
223 return tds_process_params_result_token(tds);
224 break;
225 case TDS_CURINFO_TOKEN:
226 return tds_process_cursor_tokens(tds);
227 break;
228 case TDS5_DYNAMIC_TOKEN:
229 case TDS_LOGINACK_TOKEN:
230 case TDS_ORDERBY_TOKEN:
231 case TDS_CONTROL_TOKEN:
232 tdsdump_log(TDS_DBG_WARN, "Eating %s token\n", tds_token_name(marker));
233 tds_get_n(tds, NULL, tds_get_usmallint(tds));
234 break;
235 case TDS_TABNAME_TOKEN: /* used for FOR BROWSE query */
236 return tds_process_tabname(tds);
237 break;
238 case TDS_COLINFO_TOKEN:
239 return tds_process_colinfo(tds, NULL, 0);
240 break;
241 case TDS_ORDERBY2_TOKEN:
242 tdsdump_log(TDS_DBG_WARN, "Eating %s token\n", tds_token_name(marker));
243 tds_get_n(tds, NULL, tds_get_uint(tds));
244 break;
245 case TDS_NBC_ROW_TOKEN:
246 return tds_process_nbcrow(tds);
247 break;
248 default:
249 tds_close_socket(tds);
250 tdserror(tds_get_ctx(tds), tds, TDSEBTOK, 0);
251 tdsdump_log(TDS_DBG_ERROR, "Unknown marker: %d(%x)!!\n", marker, (unsigned char) marker);
252 return TDS_FAIL;
253 }
254 return TDS_SUCCESS;
255 }
256
257 /**
258 * Retrieve and set @@spid
259 * \tds
260 */
261 static TDSRET
tds_set_spid(TDSSOCKET * tds)262 tds_set_spid(TDSSOCKET * tds)
263 {
264 TDS_INT result_type;
265 TDS_INT done_flags;
266 TDSRET rc;
267 TDSCOLUMN *curcol;
268
269 CHECK_TDS_EXTRA(tds);
270
271 if (TDS_FAILED(rc=tds_submit_query(tds, "select @@spid")))
272 return rc;
273
274 while ((rc = tds_process_tokens(tds, &result_type, &done_flags, TDS_RETURN_ROWFMT|TDS_RETURN_ROW|TDS_RETURN_DONE)) == TDS_SUCCESS) {
275
276 switch (result_type) {
277
278 case TDS_ROWFMT_RESULT:
279 if (tds->res_info->num_cols != 1)
280 return TDS_FAIL;
281 break;
282
283 case TDS_ROW_RESULT:
284 curcol = tds->res_info->columns[0];
285 if (curcol->column_type == SYBINT2 || (curcol->column_type == SYBINTN && curcol->column_size == 2)) {
286 tds->spid = *((TDS_USMALLINT *) curcol->column_data);
287 } else if (curcol->column_type == SYBINT4 || (curcol->column_type == SYBINTN && curcol->column_size == 4)) {
288 tds->spid = *((TDS_UINT *) curcol->column_data);
289 } else
290 return TDS_FAIL;
291 break;
292
293 case TDS_DONE_RESULT:
294 if ((done_flags & TDS_DONE_ERROR) != 0)
295 return TDS_FAIL;
296 break;
297
298 default:
299 break;
300 }
301 }
302 if (rc == TDS_NO_MORE_RESULTS)
303 rc = TDS_SUCCESS;
304
305 return rc;
306 }
307
308 /**
309 * tds_process_login_tokens() is called after sending the login packet
310 * to the server. It returns the success or failure of the login
311 * dependent on the protocol version. 4.2 sends an ACK token only when
312 * successful, TDS 5.0 sends it always with a success byte within
313 * @tds
314 */
315 TDSRET
tds_process_login_tokens(TDSSOCKET * tds)316 tds_process_login_tokens(TDSSOCKET * tds)
317 {
318 TDSRET succeed = TDS_FAIL;
319 int marker;
320 unsigned int len;
321 int memrc = 0;
322 unsigned char ack;
323 TDS_UINT product_version;
324
325 CHECK_TDS_EXTRA(tds);
326
327 tdsdump_log(TDS_DBG_FUNC, "tds_process_login_tokens()\n");
328 /* get_incoming(tds->s); */
329 do {
330 struct { unsigned char major, minor, tiny[2];
331 unsigned int reported;
332 const char *name;
333 } ver;
334
335 marker = tds_get_byte(tds);
336 tdsdump_log(TDS_DBG_FUNC, "looking for login token, got %x(%s)\n", marker, tds_token_name(marker));
337
338 switch (marker) {
339 case TDS_LOGINACK_TOKEN:
340 /* TODO function */
341 tds->conn->tds71rev1 = 0;
342 len = tds_get_usmallint(tds);
343 if (len < 10)
344 return TDS_FAIL;
345 ack = tds_get_byte(tds);
346
347 ver.major = tds_get_byte(tds);
348 ver.minor = tds_get_byte(tds);
349 ver.tiny[0] = tds_get_byte(tds);
350 ver.tiny[1] = tds_get_byte(tds);
351 ver.reported = (ver.major << 24) | (ver.minor << 16) | (ver.tiny[0] << 8) | ver.tiny[1];
352
353 if (ver.reported == 0x07010000)
354 tds->conn->tds71rev1 = 1;
355
356 /* Log reported server product name, cf. MS-TDS LOGINACK documentation. */
357 switch(ver.reported) {
358 case 0x07000000:
359 ver.name = "7.0"; break;
360 case 0x07010000:
361 ver.name = "2000"; break;
362 case 0x71000001:
363 ver.name = "2000 SP1"; break;
364 case 0x72090002:
365 ver.name = "2005"; break;
366 case 0x730A0003:
367 ver.name = "2008 (no NBCROW or fSparseColumnSet)"; break;
368 case 0x730B0003:
369 ver.name = "2008"; break;
370 default:
371 ver.name = "unknown"; break;
372 }
373
374 tdsdump_log(TDS_DBG_FUNC, "server reports TDS version %x.%x.%x.%x\n",
375 ver.major, ver.minor, ver.tiny[0], ver.tiny[1]);
376 tdsdump_log(TDS_DBG_FUNC, "Product name for 0x%x is %s\n", ver.reported, ver.name);
377
378 /* Get server product name. */
379 /* Ignore product name length; some servers seem to set it incorrectly. */
380 tds_get_byte(tds);
381 product_version = 0;
382 /* Compute product name length from packet length. */
383 len -= 10;
384 free(tds->conn->product_name);
385 if (ver.major >= 7u) {
386 product_version = 0x80000000u;
387 memrc += tds_alloc_get_string(tds, &tds->conn->product_name, len / 2);
388 } else if (ver.major >= 5) {
389 memrc += tds_alloc_get_string(tds, &tds->conn->product_name, len);
390 } else {
391 memrc += tds_alloc_get_string(tds, &tds->conn->product_name, len);
392 if (tds->conn->product_name != NULL && strstr(tds->conn->product_name, "Microsoft") != NULL)
393 product_version = 0x80000000u;
394 }
395
396 product_version |= ((TDS_UINT) tds_get_byte(tds)) << 24;
397 product_version |= ((TDS_UINT) tds_get_byte(tds)) << 16;
398 product_version |= ((TDS_UINT) tds_get_byte(tds)) << 8;
399 product_version |= tds_get_byte(tds);
400
401 /*
402 * MSSQL 6.5 and 7.0 seem to return strange values for this
403 * using TDS 4.2, something like 5F 06 32 FF for 6.50
404 */
405 if (ver.major == 4 && ver.minor == 2 && (product_version & 0xff0000ffu) == 0x5f0000ffu)
406 product_version = ((product_version & 0xffff00u) | 0x800000u) << 8;
407 tds->conn->product_version = product_version;
408 tdsdump_log(TDS_DBG_FUNC, "Product version %lX\n", (unsigned long) product_version);
409
410 /*
411 * TDS 5.0 reports 5 on success 6 on failure
412 * TDS 4.2 reports 1 on success and is not
413 * present on failure
414 */
415 if (ack == 5 || ack == 1)
416 succeed = TDS_SUCCESS;
417 /* authentication is now useless */
418 if (tds->conn->authentication) {
419 tds->conn->authentication->free(tds->conn, tds->conn->authentication);
420 tds->conn->authentication = NULL;
421 }
422 break;
423 default:
424 if (TDS_FAILED(tds_process_default_tokens(tds, marker)))
425 return TDS_FAIL;
426 break;
427 }
428 } while (marker != TDS_DONE_TOKEN);
429 /* TODO why ?? */
430 tds->spid = (int)tds->rows_affected;
431 if (tds->spid == 0) {
432 if (TDS_FAILED(tds_set_spid(tds))) {
433 tdsdump_log(TDS_DBG_ERROR, "tds_set_spid() failed\n");
434 succeed = TDS_FAIL;
435 }
436 }
437 if (memrc != 0)
438 succeed = TDS_FAIL;
439
440 tdsdump_log(TDS_DBG_FUNC, "tds_process_login_tokens() returning %s\n",
441 (succeed == TDS_SUCCESS)? "TDS_SUCCESS" : "TDS_FAIL");
442
443 return succeed;
444 }
445
446 /**
447 * Process authentication token.
448 * This token is only TDS 7.0+.
449 * \tds
450 */
451 static TDSRET
tds_process_auth(TDSSOCKET * tds)452 tds_process_auth(TDSSOCKET * tds)
453 {
454 unsigned int pdu_size;
455
456 CHECK_TDS_EXTRA(tds);
457
458 #if ENABLE_EXTRA_CHECKS
459 if (!IS_TDS7_PLUS(tds->conn))
460 tdsdump_log(TDS_DBG_ERROR, "Called auth on TDS version < 7\n");
461 #endif
462
463 pdu_size = tds_get_usmallint(tds);
464 tdsdump_log(TDS_DBG_INFO1, "TDS_AUTH_TOKEN PDU size %u\n", pdu_size);
465
466 if (!tds->conn->authentication)
467 return TDS_FAIL;
468
469 return tds->conn->authentication->handle_next(tds, tds->conn->authentication, pdu_size);
470 }
471
472 /**
473 * process all streams.
474 * tds_process_tokens() is called after submitting a query with
475 * tds_submit_query() and is responsible for calling the routines to
476 * populate tds->res_info if appropriate (some query have no result sets)
477 * @tds
478 * @param result_type A pointer to an integer variable which
479 * tds_process_tokens sets to indicate the current type of result.
480 * @par
481 * <b>Values that indicate command status</b>
482 * <table>
483 * <tr><td>TDS_DONE_RESULT</td><td>The results of a command have been completely processed.
484 * This command returned no rows.</td></tr>
485 * <tr><td>TDS_DONEPROC_RESULT</td><td>The results of a command have been completely processed.
486 * This command returned rows.</td></tr>
487 * <tr><td>TDS_DONEINPROC_RESULT</td><td>The results of a command have been completely processed.
488 * This command returned rows.</td></tr>
489 * </table>
490 * <b>Values that indicate results information is available</b>
491 * <table><tr>
492 * <td>TDS_ROWFMT_RESULT</td><td>Regular Data format information</td>
493 * <td>tds->res_info now contains the result details ; tds->current_results now points to that data</td>
494 * </tr><tr>
495 * <td>TDS_COMPUTEFMT_ RESULT</td><td>Compute data format information</td>
496 * <td>tds->comp_info now contains the result data; tds->current_results now points to that data</td>
497 * </tr><tr>
498 * <td>TDS_DESCRIBE_RESULT</td><td></td>
499 * <td></td>
500 * </tr></table>
501 * <b>Values that indicate data is available</b>
502 * <table><tr>
503 * <td><b>Value</b></td><td><b>Meaning</b></td><td><b>Information returned</b></td>
504 * </tr><tr>
505 * <td>TDS_ROW_RESULT</td><td>Regular row results</td>
506 * <td>1 or more rows of regular data can now be retrieved</td>
507 * </tr><tr>
508 * <td>TDS_COMPUTE_RESULT</td><td>Compute row results</td>
509 * <td>A single row of compute data can now be retrieved</td>
510 * </tr><tr>
511 * <td>TDS_PARAM_RESULT</td><td>Return parameter results</td>
512 * <td>param_info or cur_dyn->params contain returned parameters</td>
513 * </tr><tr>
514 * <td>TDS_STATUS_RESULT</td><td>Stored procedure status results</td>
515 * <td>tds->ret_status contain the returned code</td>
516 * </tr></table>
517 * @param done_flags Flags contained in the TDS_DONE*_TOKEN readed
518 * @param flag Flags to select token type to stop/return
519 * @todo Complete TDS_DESCRIBE_RESULT description
520 * @retval TDS_SUCCESS if a result set is available for processing.
521 * @retval TDS_FAIL on error.
522 * @retval TDS_NO_MORE_RESULTS if all results have been completely processed.
523 * @retval anything returned by one of the many functions it calls. :-(
524 */
525 TDSRET
tds_process_tokens(TDSSOCKET * tds,TDS_INT * result_type,int * done_flags,unsigned flag)526 tds_process_tokens(TDSSOCKET *tds, TDS_INT *result_type, int *done_flags, unsigned flag)
527 {
528 int marker;
529 TDSPARAMINFO *pinfo = NULL;
530 TDSCOLUMN *curcol;
531 TDSRET rc;
532 TDS_INT8 saved_rows_affected = tds->rows_affected;
533 TDS_INT ret_status;
534 int cancel_seen = 0;
535 unsigned return_flag = 0;
536
537 /** \cond HIDDEN_SYMBOLS */
538 #define SET_RETURN(ret, f) \
539 *result_type = ret; \
540 return_flag = TDS_RETURN_##f | TDS_STOPAT_##f; \
541 if (flag & TDS_STOPAT_##f) {\
542 tds_unget_byte(tds); \
543 tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens::SET_RETURN stopping on current token\n"); \
544 break; \
545 }
546 /** \endcond */
547
548 CHECK_TDS_EXTRA(tds);
549
550 tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens(%p, %p, %p, 0x%x)\n", tds, result_type, done_flags, flag);
551
552 if (tds->state == TDS_IDLE || tds->state == TDS_SENDING) {
553 tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens() state is COMPLETED\n");
554 *result_type = TDS_DONE_RESULT;
555 return TDS_NO_MORE_RESULTS;
556 }
557
558 if (tds_set_state(tds, TDS_READING) != TDS_READING)
559 return TDS_FAIL;
560
561 rc = TDS_SUCCESS;
562 for (;;) {
563
564 marker = tds_get_byte(tds);
565 tdsdump_log(TDS_DBG_INFO1, "processing result tokens. marker is %x(%s)\n", marker, tds_token_name(marker));
566
567 switch (marker) {
568 case TDS7_RESULT_TOKEN:
569
570 /*
571 * If we're processing the results of a cursor fetch
572 * from sql server we don't want to pass back the
573 * TDS_ROWFMT_RESULT to the calling API
574 */
575
576 if (tds->current_op == TDS_OP_CURSORFETCH) {
577 rc = tds7_process_result(tds);
578 if (TDS_FAILED(rc))
579 break;
580 marker = tds_get_byte(tds);
581 if (marker != TDS_TABNAME_TOKEN)
582 tds_unget_byte(tds);
583 else
584 rc = tds_process_tabname(tds);
585 } else {
586 SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
587
588 rc = tds7_process_result(tds);
589 if (TDS_FAILED(rc))
590 break;
591 /* handle browse information (if presents) */
592 marker = tds_get_byte(tds);
593 if (marker != TDS_TABNAME_TOKEN) {
594 tds_unget_byte(tds);
595 rc = TDS_SUCCESS;
596 break;
597 }
598 rc = tds_process_tabname(tds);
599 }
600 break;
601 case TDS_RESULT_TOKEN:
602 SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
603 rc = tds_process_result(tds);
604 break;
605 case TDS_ROWFMT2_TOKEN:
606 SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
607 rc = tds5_process_result(tds);
608 break;
609 case TDS_COLNAME_TOKEN:
610 rc = tds_process_col_name(tds);
611 break;
612 case TDS_COLFMT_TOKEN:
613 SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
614 rc = tds_process_col_fmt(tds);
615 /* handle browse information (if present) */
616 marker = tds_get_byte(tds);
617 if (marker != TDS_TABNAME_TOKEN) {
618 tds_unget_byte(tds);
619 break;
620 }
621 rc = tds_process_tabname(tds);
622 break;
623 case TDS_PARAM_TOKEN:
624 tds_unget_byte(tds);
625 if (tds->current_op) {
626 tdsdump_log(TDS_DBG_FUNC, "processing parameters for op %d\n", tds->current_op);
627 while ((marker = tds_get_byte(tds)) == TDS_PARAM_TOKEN) {
628 tdsdump_log(TDS_DBG_INFO1, "calling tds_process_param_result\n");
629 tds_process_param_result(tds, &pinfo);
630 }
631 tds_unget_byte(tds);
632 tdsdump_log(TDS_DBG_FUNC, "%d hidden return parameters\n", pinfo ? pinfo->num_cols : -1);
633 if (pinfo && pinfo->num_cols > 0) {
634 curcol = pinfo->columns[0];
635 if (tds->current_op == TDS_OP_CURSOROPEN && tds->cur_cursor) {
636 TDSCURSOR *cursor = tds->cur_cursor;
637
638 cursor->cursor_id = *(TDS_INT *) curcol->column_data;
639 tdsdump_log(TDS_DBG_FUNC, "stored internal cursor id %d\n", cursor->cursor_id);
640 cursor->srv_status &= ~(TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_OPEN|TDS_CUR_ISTAT_DEALLOC);
641 cursor->srv_status |= cursor->cursor_id ? TDS_CUR_ISTAT_OPEN : TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_DEALLOC;
642 }
643 if ((tds->current_op == TDS_OP_PREPARE || tds->current_op == TDS_OP_PREPEXEC)
644 && tds->cur_dyn && tds->cur_dyn->num_id == 0 && curcol->column_cur_size > 0) {
645 tds->cur_dyn->num_id = *(TDS_INT *) curcol->column_data;
646 }
647 if (tds->current_op == TDS_OP_UNPREPARE)
648 tds_dynamic_deallocated(tds->conn, tds->cur_dyn);
649 }
650 tds_free_param_results(pinfo);
651 } else {
652 SET_RETURN(TDS_PARAM_RESULT, PROC);
653 rc = tds_process_param_result_tokens(tds);
654 }
655 break;
656 case TDS_COMPUTE_NAMES_TOKEN:
657 rc = tds_process_compute_names(tds);
658 break;
659 case TDS_COMPUTE_RESULT_TOKEN:
660 SET_RETURN(TDS_COMPUTEFMT_RESULT, COMPUTEFMT);
661 rc = tds_process_compute_result(tds);
662 break;
663 case TDS7_COMPUTE_RESULT_TOKEN:
664 SET_RETURN(TDS_COMPUTEFMT_RESULT, COMPUTEFMT);
665 rc = tds7_process_compute_result(tds);
666 break;
667 case TDS_ROW_TOKEN:
668 case TDS_NBC_ROW_TOKEN:
669 /* overstepped the mark... */
670 if (tds->cur_cursor) {
671 tds_set_current_results(tds, tds->cur_cursor->res_info);
672 tdsdump_log(TDS_DBG_INFO1, "tds_process_tokens(). set current_results to cursor->res_info\n");
673 } else {
674 /* assure that we point to row, not to compute */
675 if (tds->res_info)
676 tds_set_current_results(tds, tds->res_info);
677 }
678 /* I don't know when this it's false but it happened, also server can send garbage... */
679 if (tds->current_results)
680 tds->current_results->rows_exist = 1;
681 SET_RETURN(TDS_ROW_RESULT, ROW);
682
683 switch (marker) {
684 case TDS_ROW_TOKEN:
685 rc = tds_process_row(tds);
686 break;
687 case TDS_NBC_ROW_TOKEN:
688 rc = tds_process_nbcrow(tds);
689 break;
690 }
691 break;
692 case TDS_CMP_ROW_TOKEN:
693 /* I don't know when this it's false but it happened, also server can send garbage... */
694 if (tds->res_info)
695 tds->res_info->rows_exist = 1;
696 SET_RETURN(TDS_COMPUTE_RESULT, COMPUTE);
697 rc = tds_process_compute(tds);
698 break;
699 case TDS_RETURNSTATUS_TOKEN:
700 ret_status = tds_get_int(tds);
701 marker = tds_peek(tds);
702 if (marker != TDS_PARAM_TOKEN && marker != TDS_DONEPROC_TOKEN && marker != TDS_DONE_TOKEN && marker != TDS5_PARAMFMT_TOKEN && marker != TDS5_PARAMFMT2_TOKEN)
703 break;
704 if (tds->current_op) {
705 /* TODO perhaps we should use ret_status ?? */
706 } else {
707 /* TODO optimize */
708 flag &= ~TDS_STOPAT_PROC;
709 SET_RETURN(TDS_STATUS_RESULT, PROC);
710 tds->has_status = 1;
711 tds->ret_status = ret_status;
712 tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens: return status is %d\n", tds->ret_status);
713 rc = TDS_SUCCESS;
714 }
715 break;
716 case TDS5_DYNAMIC_TOKEN:
717 /* process acknowledge dynamic */
718 tds_set_cur_dyn(tds, tds_process_dynamic(tds));
719 /* special case, prepared statement cannot be prepared */
720 if (!tds->cur_dyn || tds->cur_dyn->emulated)
721 break;
722 marker = tds_get_byte(tds);
723 if (marker != TDS_EED_TOKEN) {
724 tds_unget_byte(tds);
725 break;
726 }
727 tds_process_msg(tds, marker);
728 if (!tds->cur_dyn || !tds->cur_dyn->emulated)
729 break;
730 marker = tds_get_byte(tds);
731 if (marker != TDS_DONE_TOKEN) {
732 tds_unget_byte(tds);
733 break;
734 }
735 rc = tds_process_end(tds, marker, done_flags);
736 if (done_flags)
737 *done_flags &= ~TDS_DONE_ERROR;
738 /* FIXME warning to macro expansion */
739 SET_RETURN(TDS_DONE_RESULT, DONE);
740 break;
741 case TDS5_PARAMFMT_TOKEN:
742 SET_RETURN(TDS_DESCRIBE_RESULT, PARAMFMT);
743 rc = tds_process_dyn_result(tds);
744 break;
745 case TDS5_PARAMFMT2_TOKEN:
746 SET_RETURN(TDS_DESCRIBE_RESULT, PARAMFMT);
747 rc = tds5_process_dyn_result2(tds);
748 break;
749 case TDS5_PARAMS_TOKEN:
750 SET_RETURN(TDS_PARAM_RESULT, PROC);
751 rc = tds_process_params_result_token(tds);
752 break;
753 case TDS_CURINFO_TOKEN:
754 rc = tds_process_cursor_tokens(tds);
755 break;
756 case TDS_DONE_TOKEN:
757 SET_RETURN(TDS_DONE_RESULT, DONE);
758 rc = tds_process_end(tds, marker, done_flags);
759 switch (tds->current_op) {
760 case TDS_OP_DYN_DEALLOC:
761 if (done_flags && (*done_flags & TDS_DONE_ERROR) == 0)
762 tds_dynamic_deallocated(tds->conn, tds->cur_dyn);
763 break;
764 default:
765 break;
766 }
767 break;
768 case TDS_DONEPROC_TOKEN:
769 SET_RETURN(TDS_DONEPROC_RESULT, DONE);
770 rc = tds_process_end(tds, marker, done_flags);
771 tds->rows_affected = saved_rows_affected;
772 switch (tds->current_op) {
773 default:
774 break;
775 case TDS_OP_CURSOROPEN:
776 *result_type = TDS_DONE_RESULT;
777 break;
778 case TDS_OP_CURSORCLOSE:
779 tdsdump_log(TDS_DBG_FUNC, "TDS_OP_CURSORCLOSE\n");
780 if (tds->cur_cursor) {
781
782 TDSCURSOR *cursor = tds->cur_cursor;
783
784 cursor->srv_status &= ~TDS_CUR_ISTAT_OPEN;
785 cursor->srv_status |= TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_DECLARED;
786 if (cursor->status.dealloc == TDS_CURSOR_STATE_SENT) {
787 tds_cursor_deallocated(tds->conn, cursor);
788 }
789 }
790 *result_type = TDS_NO_MORE_RESULTS;
791 rc = TDS_NO_MORE_RESULTS;
792 break;
793 case TDS_OP_UNPREPARE:
794 if (done_flags && (*done_flags & TDS_DONE_ERROR) == 0)
795 tds_dynamic_deallocated(tds->conn, tds->cur_dyn);
796 *result_type = TDS_NO_MORE_RESULTS;
797 rc = TDS_NO_MORE_RESULTS;
798 break;
799 case TDS_OP_CURSOR:
800 case TDS_OP_CURSORPREPARE:
801 case TDS_OP_CURSOREXECUTE:
802 case TDS_OP_CURSORPREPEXEC:
803 case TDS_OP_CURSORUNPREPARE:
804 case TDS_OP_CURSORFETCH:
805 case TDS_OP_CURSOROPTION:
806 case TDS_OP_PREPEXECRPC:
807 *result_type = TDS_NO_MORE_RESULTS;
808 rc = TDS_NO_MORE_RESULTS;
809 break;
810 }
811 break;
812 case TDS_DONEINPROC_TOKEN:
813 switch(tds->current_op) {
814 case TDS_OP_CURSOROPEN:
815 case TDS_OP_CURSORFETCH:
816 case TDS_OP_PREPARE:
817 case TDS_OP_CURSORCLOSE:
818 rc = tds_process_end(tds, marker, done_flags);
819 if (tds->rows_affected != TDS_NO_COUNT) {
820 saved_rows_affected = tds->rows_affected;
821 }
822 break;
823 default:
824 SET_RETURN(TDS_DONEINPROC_RESULT, DONE);
825 rc = tds_process_end(tds, marker, done_flags);
826 break;
827 }
828 break;
829 case TDS_ERROR_TOKEN:
830 case TDS_INFO_TOKEN:
831 case TDS_EED_TOKEN:
832 SET_RETURN(TDS_MSG_RESULT, MSG);
833 rc = tds_process_default_tokens(tds, marker);
834 break;
835 case TDS_ENVCHANGE_TOKEN:
836 SET_RETURN(TDS_MSG_RESULT, ENV);
837 rc = tds_process_default_tokens(tds, marker);
838 break;
839 default:
840 SET_RETURN(TDS_OTHERS_RESULT, OTHERS);
841 rc = tds_process_default_tokens(tds, marker);
842 break;
843 }
844
845 if (TDS_FAILED(rc)) {
846 tds_set_state(tds, TDS_PENDING);
847 return rc;
848 }
849
850 cancel_seen |= tds->in_cancel;
851 if (cancel_seen) {
852 /* during cancel handle all tokens */
853 flag = TDS_HANDLE_ALL;
854 }
855
856 if ((return_flag & flag) != 0) {
857 tds_set_state(tds, TDS_PENDING);
858 return rc;
859 }
860
861 if (tds->state == TDS_IDLE || tds->state == TDS_SENDING)
862 return cancel_seen ? TDS_CANCELLED : TDS_NO_MORE_RESULTS;
863
864 if (tds->state == TDS_DEAD) {
865 /* TODO free all results ?? */
866 return TDS_FAIL;
867 }
868 }
869 }
870
871 /**
872 * Process results for simple query as "SET TEXTSIZE" or "USE dbname"
873 * If the statement returns results, beware they are discarded.
874 *
875 * This function was written to avoid direct calls to tds_process_default_tokens
876 * (which caused problems such as ignoring query errors).
877 * Results are read until idle state or severe failure (do not stop for
878 * statement failure).
879 * @return see tds_process_tokens for results (TDS_NO_MORE_RESULTS is never returned)
880 */
881 TDSRET
tds_process_simple_query(TDSSOCKET * tds)882 tds_process_simple_query(TDSSOCKET * tds)
883 {
884 TDS_INT res_type;
885 TDS_INT done_flags;
886 TDSRET rc;
887 TDSRET ret = TDS_SUCCESS;
888
889 CHECK_TDS_EXTRA(tds);
890
891 while ((rc = tds_process_tokens(tds, &res_type, &done_flags, TDS_RETURN_DONE)) == TDS_SUCCESS) {
892 switch (res_type) {
893
894 case TDS_DONE_RESULT:
895 case TDS_DONEPROC_RESULT:
896 case TDS_DONEINPROC_RESULT:
897 if ((done_flags & TDS_DONE_ERROR) != 0)
898 ret = TDS_FAIL;
899 break;
900
901 default:
902 break;
903 }
904 }
905 if (TDS_FAILED(rc))
906 ret = rc;
907
908 return ret;
909 }
910
911 /**
912 * Holds list of names
913 */
914 struct namelist
915 {
916 /** string name */
917 char *name;
918 /** next element in the list */
919 struct namelist *next;
920 };
921
922 /**
923 * Frees list of names
924 * \param head list head to free
925 */
926 static void
tds_free_namelist(struct namelist * head)927 tds_free_namelist(struct namelist *head)
928 {
929 struct namelist *cur = head, *prev;
930
931 while (cur != NULL) {
932 prev = cur;
933 cur = cur->next;
934 free(prev->name);
935 free(prev);
936 }
937 }
938
939 /**
940 * Reads list of names (usually table names)
941 * \tds
942 * \param remainder bytes left to read
943 * \param p_head list head to return
944 * \param large true if name length from network are 2 byte (usually 1)
945 */
946 static int
tds_read_namelist(TDSSOCKET * tds,int remainder,struct namelist ** p_head,int large)947 tds_read_namelist(TDSSOCKET * tds, int remainder, struct namelist **p_head, int large)
948 {
949 struct namelist *head = NULL, *cur = NULL, *prev;
950 int num_names = 0;
951
952 /*
953 * this is a little messy...TDS 5.0 gives the number of columns
954 * upfront, while in TDS 4.2, you're expected to figure it out
955 * by the size of the message. So, I use a link list to get the
956 * colum names and then allocate the result structure, copy
957 * and delete the linked list
958 */
959 while (remainder > 0) {
960 TDS_USMALLINT namelen;
961
962 prev = cur;
963 if (!(cur = (struct namelist *) malloc(sizeof(struct namelist)))) {
964 tds_free_namelist(head);
965 return -1;
966 }
967
968 cur->next = NULL;
969 if (prev)
970 prev->next = cur;
971 else
972 head = cur;
973
974 if (large) {
975 namelen = tds_get_usmallint(tds);
976 remainder -= 2;
977 } else {
978 namelen = tds_get_byte(tds);
979 --remainder;
980 }
981
982 if (tds_alloc_get_string(tds, &cur->name, namelen) < 0) {
983 tds_free_namelist(head);
984 return -1;
985 }
986
987 remainder -= namelen;
988 if (IS_TDS7_PLUS(tds->conn))
989 remainder -= namelen;
990 num_names++;
991 }
992
993 *p_head = head;
994 return num_names;
995 }
996
997 /**
998 * tds_process_col_name() is one half of the result set under TDS 4.2
999 * it contains all the column names, a TDS_COLFMT_TOKEN should
1000 * immediately follow this token with the datatype/size information
1001 * This is a 4.2 only function
1002 * \tds
1003 */
1004 static TDSRET
tds_process_col_name(TDSSOCKET * tds)1005 tds_process_col_name(TDSSOCKET * tds)
1006 {
1007 int hdrsize;
1008 int col, num_names = 0;
1009 struct namelist *head = NULL, *cur = NULL;
1010 TDSCOLUMN *curcol;
1011 TDSRESULTINFO *info;
1012
1013 CHECK_TDS_EXTRA(tds);
1014
1015 hdrsize = tds_get_usmallint(tds);
1016
1017 if ((num_names = tds_read_namelist(tds, hdrsize, &head, 0)) < 0)
1018 return TDS_FAIL;
1019
1020 /* free results/computes/params etc... */
1021 tds_free_all_results(tds);
1022 tds->rows_affected = TDS_NO_COUNT;
1023
1024 if ((info = tds_alloc_results(num_names)) == NULL)
1025 goto memory_error;
1026
1027 tds->res_info = info;
1028 tds_set_current_results(tds, info);
1029
1030 cur = head;
1031 for (col = 0; col < num_names; ++col) {
1032 curcol = info->columns[col];
1033 if (!tds_dstr_copy(&curcol->column_name, cur->name))
1034 goto memory_error;
1035 cur = cur->next;
1036 }
1037 tds_free_namelist(head);
1038 return TDS_SUCCESS;
1039
1040 memory_error:
1041 tds_free_namelist(head);
1042 return TDS_FAIL;
1043 }
1044
1045 /**
1046 * tds_process_col_fmt() is the other half of result set processing
1047 * under TDS 4.2. It follows tds_process_col_name(). It contains all the
1048 * column type and size information.
1049 * This is a 4.2 only function
1050 * \tds
1051 */
1052 static TDSRET
tds_process_col_fmt(TDSSOCKET * tds)1053 tds_process_col_fmt(TDSSOCKET * tds)
1054 {
1055 unsigned int col;
1056 TDSCOLUMN *curcol;
1057 TDSRESULTINFO *info;
1058 TDS_USMALLINT flags;
1059
1060 CHECK_TDS_EXTRA(tds);
1061
1062 tds_get_usmallint(tds); /* hdrsize */
1063
1064 /* TODO use current_results instead of res_info ?? */
1065 info = tds->res_info;
1066 for (col = 0; col < info->num_cols; col++) {
1067 curcol = info->columns[col];
1068 /* In Sybase all 4 byte are used for usertype, while mssql place 2 byte as usertype and 2 byte as flags */
1069 if (TDS_IS_MSSQL(tds)) {
1070 curcol->column_usertype = tds_get_smallint(tds);
1071 flags = tds_get_usmallint(tds);
1072 curcol->column_nullable = flags & 0x01;
1073 curcol->column_writeable = (flags & 0x08) > 0;
1074 curcol->column_identity = (flags & 0x10) > 0;
1075 } else {
1076 curcol->column_usertype = tds_get_int(tds);
1077 }
1078 /* on with our regularly scheduled code (mlilback, 11/7/01) */
1079 tds_set_column_type(tds->conn, curcol, tds_get_byte(tds));
1080
1081 tdsdump_log(TDS_DBG_INFO1, "processing result. type = %d(%s), varint_size %d\n",
1082 curcol->column_type, tds_prtype(curcol->column_type), curcol->column_varint_size);
1083
1084 curcol->funcs->get_info(tds, curcol);
1085
1086 /* Adjust column size according to client's encoding */
1087 curcol->on_server.column_size = curcol->column_size;
1088 adjust_character_column_size(tds, curcol);
1089 }
1090
1091 return tds_alloc_row(info);
1092 }
1093
1094 /**
1095 * Reads table names for TDS 7.1+.
1096 * TDS 7.1+ return table names as an array of names
1097 * (so database.schema.owner.name as separate names)
1098 * \tds
1099 * \param remainder bytes left to read
1100 * \param p_head pointer to list head to return
1101 * \return number of element returned or -1 on error
1102 */
1103 static int
tds71_read_table_names(TDSSOCKET * tds,int remainder,struct namelist ** p_head)1104 tds71_read_table_names(TDSSOCKET *tds, int remainder, struct namelist **p_head)
1105 {
1106 struct namelist *head = NULL, *cur = NULL, *prev;
1107 int num_names = 0;
1108
1109 /*
1110 * this is a little messy...TDS 5.0 gives the number of columns
1111 * upfront, while in TDS 4.2, you're expected to figure it out
1112 * by the size of the message. So, I use a link list to get the
1113 * colum names and then allocate the result structure, copy
1114 * and delete the linked list
1115 */
1116 while (remainder > 0) {
1117 int elements, i;
1118 size_t len;
1119 char *partials[4], *p;
1120
1121 prev = cur;
1122 if (!(cur = (struct namelist *) malloc(sizeof(struct namelist)))) {
1123 tds_free_namelist(head);
1124 return -1;
1125 }
1126
1127 cur->name = NULL;
1128 cur->next = NULL;
1129 if (prev)
1130 prev->next = cur;
1131 else
1132 head = cur;
1133
1134 elements = tds_get_byte(tds);
1135 --remainder;
1136 if (elements <= 0 || elements > 4) {
1137 tds_free_namelist(head);
1138 return -1;
1139 }
1140
1141 /* read partials IDs and compute full length */
1142 len = 0;
1143 for (i = 0; i < elements; ++i) {
1144 TDS_USMALLINT namelen = tds_get_usmallint(tds);
1145 remainder -= 2 + 2 * namelen;
1146 if (tds_alloc_get_string(tds, &partials[i], namelen) < 0) {
1147 while (i > 0)
1148 free(partials[--i]);
1149 tds_free_namelist(head);
1150 return -1;
1151 }
1152 len += tds_quote_id(tds, NULL, partials[i], -1) + 1;
1153 }
1154
1155 /* allocate full name */
1156 p = (char *) malloc(len);
1157 if (!p) {
1158 i = elements;
1159 while (i > 0)
1160 free(partials[--i]);
1161 tds_free_namelist(head);
1162 return -1;
1163 }
1164
1165 /* compose names */
1166 cur->name = p;
1167 for (i = 0; i < elements; ++i) {
1168 p += tds_quote_id(tds, p, partials[i], -1);
1169 *p++ = '.';
1170 free(partials[i]);
1171 }
1172 *--p = 0;
1173
1174 num_names++;
1175 }
1176
1177 *p_head = head;
1178 return num_names;
1179 }
1180
1181 /**
1182 * Process list of table from network.
1183 * This token is only TDS 4.2
1184 * \tds
1185 */
1186 static TDSRET
tds_process_tabname(TDSSOCKET * tds)1187 tds_process_tabname(TDSSOCKET *tds)
1188 {
1189 struct namelist *head, *cur;
1190 int num_names, hdrsize, i;
1191 char **names;
1192 unsigned char marker;
1193 TDSRET rc;
1194
1195 hdrsize = tds_get_usmallint(tds);
1196
1197 /* different structure for tds7.1 */
1198 /* hdrsize check is required for tds7.1 revision 1 (mssql without SPs) */
1199 /* TODO change tds_version ?? */
1200 if (IS_TDS71_PLUS(tds->conn) && (!IS_TDS71(tds->conn) || !tds->conn->tds71rev1))
1201 num_names = tds71_read_table_names(tds, hdrsize, &head);
1202 else
1203 num_names = tds_read_namelist(tds, hdrsize, &head, 1);
1204 if (num_names <= 0)
1205 return TDS_FAIL;
1206
1207 /* put in an array */
1208 names = (char **) malloc(num_names * sizeof(char*));
1209 if (!names) {
1210 tds_free_namelist(head);
1211 return TDS_FAIL;
1212 }
1213 for (cur = head, i = 0; i < num_names; ++i, cur = cur->next)
1214 names[i] = cur->name;
1215
1216 rc = TDS_SUCCESS;
1217 marker = tds_get_byte(tds);
1218 if (marker != TDS_COLINFO_TOKEN)
1219 tds_unget_byte(tds);
1220 else
1221 rc = tds_process_colinfo(tds, names, num_names);
1222
1223 free(names);
1224 tds_free_namelist(head);
1225 return rc;
1226 }
1227
1228 /**
1229 * Reads column information.
1230 * This token is only TDS 4.2
1231 * \tds
1232 * \param[in] names table names
1233 * \param[in] num_names number of table names
1234 */
1235 static TDSRET
tds_process_colinfo(TDSSOCKET * tds,char ** names,int num_names)1236 tds_process_colinfo(TDSSOCKET * tds, char **names, int num_names)
1237 {
1238 unsigned int hdrsize, l;
1239 TDSCOLUMN *curcol;
1240 TDSRESULTINFO *info;
1241 unsigned int bytes_read = 0;
1242 unsigned char col_info[3];
1243
1244 CHECK_TDS_EXTRA(tds);
1245
1246 hdrsize = tds_get_usmallint(tds);
1247
1248 info = tds->current_results;
1249
1250 while (bytes_read < hdrsize) {
1251
1252 tds_get_n(tds, col_info, 3);
1253 bytes_read += 3;
1254
1255 curcol = NULL;
1256 if (info && col_info[0] > 0 && col_info[0] <= info->num_cols)
1257 curcol = info->columns[col_info[0] - 1];
1258
1259 if (curcol) {
1260 curcol->column_writeable = (col_info[2] & 0x4) == 0;
1261 curcol->column_key = (col_info[2] & 0x8) > 0;
1262 curcol->column_hidden = (col_info[2] & 0x10) > 0;
1263
1264 if (names && col_info[1] > 0 && col_info[1] <= num_names)
1265 if (!tds_dstr_copy(&curcol->table_name, names[col_info[1] - 1]))
1266 return TDS_FAIL;
1267 }
1268 /* read real column name */
1269 if (col_info[2] & 0x20) {
1270 l = tds_get_byte(tds);
1271 if (curcol) {
1272 tds_dstr_get(tds, &curcol->table_column_name, l);
1273 if (IS_TDS7_PLUS(tds->conn))
1274 l *= 2;
1275 } else {
1276 if (IS_TDS7_PLUS(tds->conn))
1277 l *= 2;
1278 /* discard silently */
1279 tds_get_n(tds, NULL, l);
1280 }
1281 bytes_read += l + 1;
1282 }
1283 }
1284
1285 return TDS_SUCCESS;
1286 }
1287
1288 /**
1289 * process output parameters of a stored
1290 * procedure. This differs from regular row/compute results in that there
1291 * is no total number of parameters given, they just show up singly.
1292 * \tds
1293 * \param[out] pinfo output parameter.
1294 * Should point to a not allocated structure
1295 */
1296 static TDSRET
tds_process_param_result(TDSSOCKET * tds,TDSPARAMINFO ** pinfo)1297 tds_process_param_result(TDSSOCKET * tds, TDSPARAMINFO ** pinfo)
1298 {
1299 TDSCOLUMN *curparam;
1300 TDSPARAMINFO *info;
1301 TDSRET token;
1302
1303 tdsdump_log(TDS_DBG_FUNC, "tds_process_param_result(%p, %p)\n", tds, pinfo);
1304
1305 CHECK_TDS_EXTRA(tds);
1306 if (*pinfo)
1307 CHECK_PARAMINFO_EXTRA(*pinfo);
1308
1309 /* TODO check if current_results is a param result */
1310
1311 /* limited to 64K but possible types are always smaller (not TEXT/IMAGE) */
1312 tds_get_smallint(tds); /* header size */
1313 if ((info = tds_alloc_param_result(*pinfo)) == NULL)
1314 return TDS_FAIL;
1315
1316 *pinfo = info;
1317 curparam = info->columns[info->num_cols - 1];
1318
1319 /*
1320 * FIXME check support for tds7+ (seem to use same format of tds5 for data...)
1321 * perhaps varint_size can be 2 or collation can be specified ??
1322 */
1323 tds_get_data_info(tds, curparam, 1);
1324
1325 curparam->column_cur_size = curparam->column_size; /* needed ?? */
1326
1327 if (tds_alloc_param_data(curparam) == NULL)
1328 return TDS_FAIL;
1329
1330 token = curparam->funcs->get_data(tds, curparam);
1331 if (TDS_UNLIKELY(tds_write_dump))
1332 tdsdump_col(curparam);
1333
1334 /*
1335 * Real output parameters will either be unnamed or will have a valid
1336 * parameter name beginning with '@'. Ignore any other Spurious parameters
1337 * such as those returned from calls to writetext in the proc.
1338 */
1339 if (!tds_dstr_isempty(&curparam->column_name) && tds_dstr_cstr(&curparam->column_name)[0] != '@')
1340 tds_free_param_result(*pinfo);
1341
1342 return token;
1343 }
1344
1345 /**
1346 * Process parameters from networks.
1347 * Read all consecutives paramaters, not a single one.
1348 * Parameters are then stored in tds->param_info or tds->cur_dyn->res_info
1349 * depending if we are reading cursor results or normal parameters.
1350 * \tds
1351 */
1352 static TDSRET
tds_process_param_result_tokens(TDSSOCKET * tds)1353 tds_process_param_result_tokens(TDSSOCKET * tds)
1354 {
1355 int marker;
1356 TDSPARAMINFO **pinfo;
1357
1358 CHECK_TDS_EXTRA(tds);
1359
1360 if (tds->cur_dyn)
1361 pinfo = &(tds->cur_dyn->res_info);
1362 else
1363 pinfo = &(tds->param_info);
1364
1365 while ((marker = tds_get_byte(tds)) == TDS_PARAM_TOKEN) {
1366 tds_process_param_result(tds, pinfo);
1367 }
1368 if (!marker) {
1369 tdsdump_log(TDS_DBG_FUNC, "error: tds_process_param_result() returned TDS_FAIL\n");
1370 return TDS_FAIL;
1371 }
1372
1373 tds_set_current_results(tds, *pinfo);
1374 tds_unget_byte(tds);
1375 return TDS_SUCCESS;
1376 }
1377
1378 /**
1379 * tds_process_params_result_token() processes params on TDS5.
1380 * \tds
1381 */
1382 static TDSRET
tds_process_params_result_token(TDSSOCKET * tds)1383 tds_process_params_result_token(TDSSOCKET * tds)
1384 {
1385 unsigned int i;
1386 TDSPARAMINFO *info;
1387
1388 CHECK_TDS_EXTRA(tds);
1389
1390 /* TODO check if current_results is a param result */
1391 info = tds->current_results;
1392 if (!info)
1393 return TDS_FAIL;
1394
1395 for (i = 0; i < info->num_cols; i++) {
1396 TDSCOLUMN *curcol = info->columns[i];
1397 TDSRET rc = curcol->funcs->get_data(tds, curcol);
1398 if (TDS_FAILED(rc))
1399 return rc;
1400 }
1401 return TDS_SUCCESS;
1402 }
1403
1404 /**
1405 * tds_process_compute_result() processes compute result sets. These functions
1406 * need work but since they get little use, nobody has complained!
1407 * It is very similar to normal result sets.
1408 * \tds
1409 */
1410 static TDSRET
tds_process_compute_result(TDSSOCKET * tds)1411 tds_process_compute_result(TDSSOCKET * tds)
1412 {
1413 unsigned int col, num_cols;
1414 TDS_TINYINT by_cols = 0;
1415 TDS_SMALLINT *cur_by_col;
1416 TDS_SMALLINT compute_id = 0;
1417 TDSCOLUMN *curcol;
1418 TDSCOMPUTEINFO *info = NULL;
1419 unsigned int i;
1420
1421 CHECK_TDS_EXTRA(tds);
1422
1423 tds_get_smallint(tds); /* header size*/
1424
1425 /*
1426 * Compute statement id which this relates to.
1427 * You can have more than one compute clause in a SQL statement
1428 */
1429
1430 compute_id = tds_get_smallint(tds);
1431 num_cols = tds_get_byte(tds);
1432
1433 tdsdump_log(TDS_DBG_INFO1, "tds_process_compute_result(): compute_id %d for %d columns\n", compute_id, num_cols);
1434
1435 for (i=0; i < tds->num_comp_info; ++i) {
1436 if (tds->comp_info[i]->computeid == compute_id) {
1437 info = tds->comp_info[i];
1438 break;
1439 }
1440 }
1441 if (NULL == info) {
1442 tdsdump_log(TDS_DBG_FUNC, "logic error: compute_id (%d) from server not found in tds->comp_info\n", compute_id);
1443 return TDS_FAIL;
1444 }
1445
1446 tdsdump_log(TDS_DBG_FUNC, "found computeid %d in tds->comp_info\n", info->computeid);
1447 tds_set_current_results(tds, info);
1448
1449 tdsdump_log(TDS_DBG_INFO1, "processing compute result. num_cols = %d\n", num_cols);
1450
1451 /*
1452 * Iterate over compute columns returned,
1453 * e.g. COMPUTE SUM(x), AVG(x) would return num_cols = 2.
1454 */
1455 for (col = 0; col < num_cols; col++) {
1456 tdsdump_log(TDS_DBG_INFO1, "processing compute column %d\n", col);
1457 curcol = info->columns[col];
1458
1459 curcol->column_operator = tds_get_byte(tds);
1460 curcol->column_operand = tds_get_byte(tds);
1461
1462 /* If no name has been defined for the compute column, use "max", "avg" etc. */
1463 if (tds_dstr_isempty(&curcol->column_name))
1464 if (!tds_dstr_copy(&curcol->column_name, tds_pr_op(curcol->column_operator)))
1465 return TDS_FAIL;
1466
1467 /* User defined data type of the column */
1468 curcol->column_usertype = tds_get_int(tds);
1469
1470 tds_set_column_type(tds->conn, curcol, tds_get_byte(tds));
1471
1472 curcol->funcs->get_info(tds, curcol);
1473
1474 tdsdump_log(TDS_DBG_INFO1, "compute column_size is %d\n", curcol->column_size);
1475
1476 /* Adjust column size according to client's encoding */
1477 curcol->on_server.column_size = curcol->column_size;
1478 /* TODO check if this column can have collation information associated */
1479 adjust_character_column_size(tds, curcol);
1480
1481 /* skip locale */
1482 if (!IS_TDS42(tds->conn))
1483 tds_get_n(tds, NULL, tds_get_byte(tds));
1484 }
1485
1486 by_cols = tds_get_byte(tds);
1487
1488 tdsdump_log(TDS_DBG_INFO1, "processing tds compute result, by_cols = %d\n", by_cols);
1489
1490 if (by_cols) {
1491 if ((info->bycolumns = calloc(by_cols, sizeof(TDS_SMALLINT))) == NULL)
1492 return TDS_FAIL;
1493 }
1494 info->by_cols = by_cols;
1495
1496 cur_by_col = info->bycolumns;
1497 for (col = 0; col < by_cols; col++) {
1498 *cur_by_col = tds_get_byte(tds);
1499 cur_by_col++;
1500 }
1501
1502 return tds_alloc_compute_row(info);
1503 }
1504
1505 /**
1506 * Reads data information from wire
1507 * \tds
1508 * \param curcol column where to store information
1509 */
1510 static TDSRET
tds7_get_data_info(TDSSOCKET * tds,TDSCOLUMN * curcol)1511 tds7_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol)
1512 {
1513 CHECK_TDS_EXTRA(tds);
1514 CHECK_COLUMN_EXTRA(curcol);
1515
1516 /* User defined data type of the column */
1517 curcol->column_usertype = IS_TDS72_PLUS(tds->conn) ? tds_get_int(tds) : tds_get_smallint(tds);
1518
1519 curcol->column_flags = tds_get_smallint(tds); /* Flags */
1520
1521 curcol->column_nullable = curcol->column_flags & 0x01;
1522 curcol->column_writeable = (curcol->column_flags & 0x08) > 0;
1523 curcol->column_identity = (curcol->column_flags & 0x10) > 0;
1524
1525 tds_set_column_type(tds->conn, curcol, tds_get_byte(tds)); /* sets "cardinal" type */
1526
1527 curcol->column_timestamp = (curcol->column_type == SYBBINARY && curcol->column_usertype == TDS_UT_TIMESTAMP);
1528
1529 curcol->funcs->get_info(tds, curcol);
1530
1531 /* Adjust column size according to client's encoding */
1532 curcol->on_server.column_size = curcol->column_size;
1533
1534 /* NOTE adjustements must be done after curcol->char_conv initialization */
1535 adjust_character_column_size(tds, curcol);
1536
1537 /*
1538 * under 7.0 lengths are number of characters not
1539 * number of bytes...tds_get_string handles this
1540 */
1541 tds_dstr_get(tds, &curcol->column_name, tds_get_byte(tds));
1542
1543 tdsdump_log(TDS_DBG_INFO1, "tds7_get_data_info: \n"
1544 "\tcolname = %s\n"
1545 "\ttype = %d (%s)\n"
1546 "\tserver's type = %d (%s)\n"
1547 "\tcolumn_varint_size = %d\n"
1548 "\tcolumn_size = %d (%d on server)\n",
1549 tds_dstr_cstr(&curcol->column_name),
1550 curcol->column_type, tds_prtype(curcol->column_type),
1551 curcol->on_server.column_type, tds_prtype(curcol->on_server.column_type),
1552 curcol->column_varint_size,
1553 curcol->column_size, curcol->on_server.column_size);
1554
1555 CHECK_COLUMN_EXTRA(curcol);
1556
1557 return TDS_SUCCESS;
1558 }
1559
1560 /**
1561 * tds7_process_result() is the TDS 7.0 result set processing routine. It
1562 * is responsible for populating the tds->res_info structure.
1563 * This is a TDS 7.0 only function
1564 * \tds
1565 */
1566 static TDSRET
tds7_process_result(TDSSOCKET * tds)1567 tds7_process_result(TDSSOCKET * tds)
1568 {
1569 int col, num_cols;
1570 TDSRET result;
1571 TDSRESULTINFO *info;
1572
1573 CHECK_TDS_EXTRA(tds);
1574 tdsdump_log(TDS_DBG_INFO1, "processing TDS7 result metadata.\n");
1575
1576 /* read number of columns and allocate the columns structure */
1577
1578 num_cols = tds_get_smallint(tds);
1579
1580 /* This can be a DUMMY results token from a cursor fetch */
1581
1582 if (num_cols < 0) {
1583 tdsdump_log(TDS_DBG_INFO1, "no meta data\n");
1584 return TDS_SUCCESS;
1585 }
1586
1587 tds_free_all_results(tds);
1588 tds->rows_affected = TDS_NO_COUNT;
1589
1590 if ((info = tds_alloc_results(num_cols)) == NULL)
1591 return TDS_FAIL;
1592 tds_set_current_results(tds, info);
1593 if (tds->cur_cursor) {
1594 tds_free_results(tds->cur_cursor->res_info);
1595 tds->cur_cursor->res_info = info;
1596 tdsdump_log(TDS_DBG_INFO1, "set current_results to cursor->res_info\n");
1597 } else {
1598 tds->res_info = info;
1599 tdsdump_log(TDS_DBG_INFO1, "set current_results (%d column%s) to tds->res_info\n", num_cols, (num_cols==1? "":"s"));
1600 }
1601
1602 /*
1603 * loop through the columns populating COLINFO struct from
1604 * server response
1605 */
1606 tdsdump_log(TDS_DBG_INFO1, "setting up %d columns\n", num_cols);
1607 for (col = 0; col < num_cols; col++) {
1608 TDSCOLUMN *curcol = info->columns[col];
1609
1610 tds7_get_data_info(tds, curcol);
1611 }
1612
1613 if (num_cols > 0) {
1614 static const char dashes[31] = "------------------------------";
1615 tdsdump_log(TDS_DBG_INFO1, " %-20s %-15s %-15s %-7s\n", "name", "size/wsize", "type/wtype", "utype");
1616 tdsdump_log(TDS_DBG_INFO1, " %-20s %15s %15s %7s\n", dashes+10, dashes+30-15, dashes+30-15, dashes+30-7);
1617 }
1618 for (col = 0; col < num_cols; col++) {
1619 TDSCOLUMN *curcol = info->columns[col];
1620
1621 tdsdump_log(TDS_DBG_INFO1, " %-20s %7d/%-7d %7d/%-7d %7d\n",
1622 tds_dstr_cstr(&curcol->column_name),
1623 curcol->column_size, curcol->on_server.column_size,
1624 curcol->column_type, curcol->on_server.column_type,
1625 curcol->column_usertype);
1626 }
1627
1628 /* all done now allocate a row for tds_process_row to use */
1629 result = tds_alloc_row(info);
1630 CHECK_TDS_EXTRA(tds);
1631 return result;
1632 }
1633
1634 /**
1635 * Reads data metadata from wire
1636 * \param tds state information for the socket and the TDS protocol
1637 * \param curcol column where to store information
1638 * \param is_param true if metadata are for a parameter (false for normal
1639 * column)
1640 */
1641 static TDSRET
tds_get_data_info(TDSSOCKET * tds,TDSCOLUMN * curcol,int is_param)1642 tds_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int is_param)
1643 {
1644 CHECK_TDS_EXTRA(tds);
1645 CHECK_COLUMN_EXTRA(curcol);
1646
1647 tdsdump_log(TDS_DBG_INFO1, "tds_get_data_info(%p, %p, %d) %s\n", tds, curcol, is_param, is_param? "[for parameter]" : "");
1648
1649 tds_dstr_get(tds, &curcol->column_name, tds_get_byte(tds));
1650
1651 curcol->column_flags = tds_get_byte(tds); /* Flags */
1652 if (!is_param) {
1653 /* TODO check if all flags are the same for all TDS versions */
1654 if (IS_TDS50(tds->conn))
1655 curcol->column_hidden = curcol->column_flags & 0x1;
1656 curcol->column_key = (curcol->column_flags & 0x2) > 1;
1657 curcol->column_writeable = (curcol->column_flags & 0x10) > 1;
1658 curcol->column_nullable = (curcol->column_flags & 0x20) > 1;
1659 curcol->column_identity = (curcol->column_flags & 0x40) > 1;
1660 #if 0
1661 /****************************************
1662 * NumParts=BYTE; (introduced in TDS 7.2)
1663 * PartName=US_VARCHAR;(introduced in TDS 7.2)
1664 * TableName=NumParts, {PartName}-;
1665 * ColName= HYPERLINK \l "B_VARCHAR_Def" B_VARCHAR;
1666 * ColumnData=UserType, Flags, [TableName], // <Only specified if text, //ntext or image columns are included //in the rowset being described> ColName;
1667 * NoMetaData='0xFF', '0xFF';
1668 */
1669 enum column_flag_bits_according_to_microsoft {
1670 case_sensitive = 0x0001
1671 , nullable = 0x0002
1672 , updateable = 0x0004
1673 , might_be_updateable = 0x0008
1674 , identity = 0x0010
1675 , computed = 0x0020
1676 , us_reserved_odbc = 0x0040 | 0x0080
1677 , is_fixed_len_clr_type = 0x0100
1678 , is_hidden_browse_pk = 0x0200
1679 , is_browse_pk = 0x0400
1680 , might_be_nullable = 0x0800
1681 };
1682 /* TODO: implement members in TDSCOLUMN */
1683 if (IS_TDS72_PLUS(tds->conn)) {
1684 curcol->is_computed = (curcol->column_flags & (1 << 4)) > 1;
1685 curcol->us_reserved_odbc1 = (curcol->column_flags & (1 << 5)) > 1;
1686 curcol->us_reserved_odbc2 = (curcol->column_flags & (1 << 6)) > 1;
1687 curcol->is_fixed_len_clr_type = (curcol->column_flags & (1 << 7)) > 1;
1688 }
1689 #endif
1690 }
1691
1692 if (IS_TDS72_PLUS(tds->conn)) {
1693 tds_get_n(tds, NULL, 2);
1694 #if 0
1695 /* TODO: implement members in TDSCOLUMN, values untested */
1696 curcol->us_reserved1 = (curcol->column_flags & 0x01);
1697 curcol->us_reserved2 = (curcol->column_flags & 0x02);
1698 curcol->us_reserved3 = (curcol->column_flags & 0x04);
1699 curcol->us_reserved4 = (curcol->column_flags & 0x08);
1700 curcol->is_hidden = (curcol->column_flags & 0x10);
1701 curcol->is_key = (curcol->column_flags & 0x20);
1702 curcol->is_nullable_unknown = (curcol->column_flags & 0x40);
1703 #endif
1704 }
1705
1706 curcol->column_usertype = tds_get_int(tds);
1707 tds_set_column_type(tds->conn, curcol, tds_get_byte(tds));
1708
1709 tdsdump_log(TDS_DBG_INFO1, "processing result. type = %d(%s), varint_size %d\n",
1710 curcol->column_type, tds_prtype(curcol->column_type), curcol->column_varint_size);
1711
1712 curcol->funcs->get_info(tds, curcol);
1713
1714 tdsdump_log(TDS_DBG_INFO1, "processing result. column_size %d\n", curcol->column_size);
1715
1716 /* Adjust column size according to client's encoding */
1717 curcol->on_server.column_size = curcol->column_size;
1718 adjust_character_column_size(tds, curcol);
1719
1720 return TDS_SUCCESS;
1721 }
1722
1723 /**
1724 * tds_process_result() is the TDS 5.0 result set processing routine. It
1725 * is responsible for populating the tds->res_info structure.
1726 * This is a TDS 5.0 only function
1727 * \tds
1728 */
1729 static TDSRET
tds_process_result(TDSSOCKET * tds)1730 tds_process_result(TDSSOCKET * tds)
1731 {
1732 unsigned int col, num_cols;
1733 TDSCOLUMN *curcol;
1734 TDSRESULTINFO *info;
1735
1736 CHECK_TDS_EXTRA(tds);
1737
1738 tds_free_all_results(tds);
1739 tds->rows_affected = TDS_NO_COUNT;
1740
1741 tds_get_usmallint(tds); /* header size */
1742
1743 /* read number of columns and allocate the columns structure */
1744 num_cols = tds_get_usmallint(tds);
1745
1746 if ((info = tds_alloc_results(num_cols)) == NULL)
1747 return TDS_FAIL;
1748 tds_set_current_results(tds, info);
1749 if (tds->cur_cursor)
1750 tds->cur_cursor->res_info = info;
1751 else
1752 tds->res_info = info;
1753
1754 /*
1755 * loop through the columns populating COLINFO struct from
1756 * server response
1757 */
1758 for (col = 0; col < info->num_cols; col++) {
1759 curcol = info->columns[col];
1760
1761 tds_get_data_info(tds, curcol, 0);
1762
1763 /* skip locale information */
1764 /* NOTE do not put into tds_get_data_info, param do not have locale information */
1765 tds_get_n(tds, NULL, tds_get_byte(tds));
1766 }
1767 return tds_alloc_row(info);
1768 }
1769
1770 /**
1771 * tds5_process_result() is the new TDS 5.0 result set processing routine.
1772 * It is responsible for populating the tds->res_info structure.
1773 * This is a TDS 5.0 only function
1774 * \tds
1775 */
1776 static TDSRET
tds5_process_result(TDSSOCKET * tds)1777 tds5_process_result(TDSSOCKET * tds)
1778 {
1779 unsigned int colnamelen;
1780 TDS_USMALLINT col, num_cols;
1781 TDSCOLUMN *curcol;
1782 TDSRESULTINFO *info;
1783
1784 CHECK_TDS_EXTRA(tds);
1785
1786 tdsdump_log(TDS_DBG_INFO1, "tds5_process_result\n");
1787
1788 /*
1789 * free previous resultset
1790 */
1791 tds_free_all_results(tds);
1792 tds->rows_affected = TDS_NO_COUNT;
1793
1794 /*
1795 * read length of packet (4 bytes)
1796 */
1797 tds_get_uint(tds);
1798
1799 /* read number of columns and allocate the columns structure */
1800 num_cols = tds_get_usmallint(tds);
1801
1802 if ((info = tds_alloc_results(num_cols)) == NULL)
1803 return TDS_FAIL;
1804 tds_set_current_results(tds, info);
1805 if (tds->cur_cursor)
1806 tds->cur_cursor->res_info = info;
1807 else
1808 tds->res_info = info;
1809
1810 tdsdump_log(TDS_DBG_INFO1, "num_cols=%d\n", num_cols);
1811
1812 /* TODO reuse some code... */
1813 /*
1814 * loop through the columns populating COLINFO struct from
1815 * server response
1816 */
1817 for (col = 0; col < info->num_cols; col++) {
1818 curcol = info->columns[col];
1819
1820 /* label */
1821 tds_dstr_get(tds, &curcol->column_name, tds_get_byte(tds));
1822
1823 /* TODO save informations somewhere */
1824 /* database */
1825 colnamelen = tds_get_byte(tds);
1826 tds_get_n(tds, NULL, colnamelen);
1827 /*
1828 * tds_get_n(tds, curcol->catalog_name, colnamelen);
1829 * curcol->catalog_name[colnamelen] = '\0';
1830 */
1831
1832 /* owner */
1833 colnamelen = tds_get_byte(tds);
1834 tds_get_n(tds, NULL, colnamelen);
1835 /*
1836 * tds_get_n(tds, curcol->schema_name, colnamelen);
1837 * curcol->schema_name[colnamelen] = '\0';
1838 */
1839
1840 /* table */
1841 /* TODO use with owner and database */
1842 tds_dstr_get(tds, &curcol->table_name, tds_get_byte(tds));
1843
1844 /* table column name */
1845 tds_dstr_get(tds, &curcol->table_column_name, tds_get_byte(tds));
1846
1847 /* if label is empty, use the table column name */
1848 if (tds_dstr_isempty(&curcol->column_name))
1849 if (!tds_dstr_dup(&curcol->column_name, &curcol->table_column_name))
1850 return TDS_FAIL;
1851
1852 /* flags (4 bytes) */
1853 curcol->column_flags = tds_get_int(tds);
1854 curcol->column_hidden = curcol->column_flags & 0x1;
1855 curcol->column_key = (curcol->column_flags & 0x2) > 1;
1856 curcol->column_writeable = (curcol->column_flags & 0x10) > 1;
1857 curcol->column_nullable = (curcol->column_flags & 0x20) > 1;
1858 curcol->column_identity = (curcol->column_flags & 0x40) > 1;
1859
1860 curcol->column_usertype = tds_get_int(tds);
1861
1862 tds_set_column_type(tds->conn, curcol, tds_get_byte(tds));
1863
1864 curcol->funcs->get_info(tds, curcol);
1865
1866 /* Adjust column size according to client's encoding */
1867 curcol->on_server.column_size = curcol->column_size;
1868 adjust_character_column_size(tds, curcol);
1869
1870 /* discard Locale */
1871 tds_get_n(tds, NULL, tds_get_byte(tds));
1872
1873 /*
1874 * Dump all information on this column
1875 */
1876 tdsdump_log(TDS_DBG_INFO1, "col %d:\n", col);
1877 tdsdump_log(TDS_DBG_INFO1, "\tcolumn_name=[%s]\n", tds_dstr_cstr(&curcol->column_name));
1878 /*
1879 tdsdump_log(TDS_DBG_INFO1, "\tcolumn_name=[%s]\n", curcol->column_colname);
1880 tdsdump_log(TDS_DBG_INFO1, "\tcatalog=[%s] schema=[%s] table=[%s]\n",
1881 curcol->catalog_name, curcol->schema_name, curcol->table_name, curcol->column_colname);
1882 */
1883 tdsdump_log(TDS_DBG_INFO1, "\tflags=%x utype=%d type=%d varint=%d\n",
1884 curcol->column_flags, curcol->column_usertype, curcol->column_type, curcol->column_varint_size);
1885
1886 tdsdump_log(TDS_DBG_INFO1, "\tcolsize=%d prec=%d scale=%d\n",
1887 curcol->column_size, curcol->column_prec, curcol->column_scale);
1888 }
1889 return tds_alloc_row(info);
1890 }
1891
1892 /**
1893 * tds_process_compute() processes compute rows and places them in the row
1894 * buffer.
1895 * \tds
1896 */
1897 static TDSRET
tds_process_compute(TDSSOCKET * tds)1898 tds_process_compute(TDSSOCKET * tds)
1899 {
1900 unsigned int i;
1901 TDSCOLUMN *curcol;
1902 TDSCOMPUTEINFO *info;
1903 TDS_INT id;
1904
1905 CHECK_TDS_EXTRA(tds);
1906
1907 id = tds_get_smallint(tds);
1908
1909 tdsdump_log(TDS_DBG_INFO1, "tds_process_compute() found compute id %d\n", id);
1910
1911 for (i = 0;; ++i) {
1912 if (i >= tds->num_comp_info) {
1913 tdsdump_log(TDS_DBG_INFO1, "tds_process_compute() FAIL: id exceeds bound (%d)\n", tds->num_comp_info);
1914 return TDS_FAIL;
1915 }
1916 info = tds->comp_info[i];
1917 if (info->computeid == id)
1918 break;
1919 }
1920 tds_set_current_results(tds, info);
1921
1922 for (i = 0; i < info->num_cols; i++) {
1923 curcol = info->columns[i];
1924 if (TDS_FAILED(curcol->funcs->get_data(tds, curcol))) {
1925 tdsdump_log(TDS_DBG_INFO1, "tds_process_compute() FAIL: get_data() failed\n");
1926 return TDS_FAIL;
1927 }
1928 }
1929 return TDS_SUCCESS;
1930 }
1931
1932 /**
1933 * tds_process_row() processes rows and places them in the row buffer.
1934 * \tds
1935 */
1936 static TDSRET
tds_process_row(TDSSOCKET * tds)1937 tds_process_row(TDSSOCKET * tds)
1938 {
1939 unsigned int i;
1940 TDSCOLUMN *curcol;
1941 TDSRESULTINFO *info;
1942
1943 CHECK_TDS_EXTRA(tds);
1944
1945 info = tds->current_results;
1946 if (!info)
1947 return TDS_FAIL;
1948
1949 assert(info->num_cols > 0);
1950
1951 for (i = 0; i < info->num_cols; i++) {
1952 tdsdump_log(TDS_DBG_INFO1, "tds_process_row(): reading column %d \n", i);
1953 curcol = info->columns[i];
1954 if (TDS_FAILED(curcol->funcs->get_data(tds, curcol)))
1955 return TDS_FAIL;
1956 }
1957 return TDS_SUCCESS;
1958 }
1959
1960 /**
1961 * tds_process_nbcrow() processes rows and places them in the row buffer.
1962 */
1963 static TDSRET
tds_process_nbcrow(TDSSOCKET * tds)1964 tds_process_nbcrow(TDSSOCKET * tds)
1965 {
1966 unsigned int i;
1967 TDSCOLUMN *curcol;
1968 TDSRESULTINFO *info;
1969 char *nbcbuf;
1970
1971 CHECK_TDS_EXTRA(tds);
1972
1973 info = tds->current_results;
1974 if (!info)
1975 return TDS_FAIL;
1976
1977 assert(info->num_cols > 0);
1978
1979 nbcbuf = alloca((info->num_cols + 7) / 8);
1980 tds_get_n(tds, nbcbuf, (info->num_cols + 7) / 8);
1981 for (i = 0; i < info->num_cols; i++) {
1982 curcol = info->columns[i];
1983 tdsdump_log(TDS_DBG_INFO1, "tds_process_nbcrow(): reading column %d \n", i);
1984 if (nbcbuf[i / 8] & (1 << (i % 8))) {
1985 curcol->column_cur_size = -1;
1986 } else {
1987 if (TDS_FAILED(curcol->funcs->get_data(tds, curcol)))
1988 return TDS_FAIL;
1989 }
1990 }
1991 return TDS_SUCCESS;
1992 }
1993
1994 /**
1995 * Attempt to close all deferred closes (dynamics and cursors).
1996 * \tds
1997 */
1998 static void
tds_process_pending_closes(TDSSOCKET * tds)1999 tds_process_pending_closes(TDSSOCKET *tds)
2000 {
2001 TDSDYNAMIC *dyn, *next_dyn;
2002 TDSCURSOR *cursor, *next_cursor;
2003 int all_closed = 1;
2004
2005 /* avoid recursions */
2006 tds->conn->pending_close = 0;
2007
2008 /* scan all cursors to close */
2009 cursor = tds->conn->cursors;
2010 if (cursor)
2011 ++cursor->ref_count;
2012 for (; cursor; cursor = next_cursor) {
2013 next_cursor = cursor->next;
2014 if (next_cursor)
2015 ++next_cursor->ref_count;
2016
2017 if (cursor->defer_close) {
2018 cursor->status.dealloc = TDS_CURSOR_STATE_REQUESTED;
2019 if (TDS_FAILED(tds_cursor_close(tds, cursor))
2020 || TDS_FAILED(tds_process_simple_query(tds))) {
2021 all_closed = 0;
2022 } else {
2023 cursor->defer_close = 0;
2024 tds_cursor_dealloc(tds, cursor);
2025 }
2026 }
2027 tds_release_cursor(&cursor);
2028 }
2029
2030 /* scan all dynamic to close */
2031 dyn = tds->conn->dyns;
2032 if (dyn)
2033 ++dyn->ref_count;
2034 for (; dyn; dyn = next_dyn) {
2035 next_dyn = dyn->next;
2036 if (next_dyn)
2037 ++next_dyn->ref_count;
2038
2039 if (dyn->defer_close) {
2040 if (TDS_FAILED(tds_submit_unprepare(tds, dyn))
2041 || TDS_FAILED(tds_process_simple_query(tds))) {
2042 all_closed = 0;
2043 } else {
2044 dyn->defer_close = 0;
2045 }
2046 }
2047 tds_release_dynamic(&dyn);
2048 }
2049
2050 if (!all_closed)
2051 tds->conn->pending_close = 1;
2052 }
2053
2054 /**
2055 * tds_process_end() processes any of the DONE, DONEPROC, or DONEINPROC
2056 * tokens.
2057 * \param tds state information for the socket and the TDS protocol
2058 * \param marker TDS token number
2059 * \param flags_parm filled with bit flags (see TDS_DONE_ constants).
2060 * Is NULL nothing is returned
2061 */
2062 static TDSRET
tds_process_end(TDSSOCKET * tds,int marker,int * flags_parm)2063 tds_process_end(TDSSOCKET * tds, int marker, int *flags_parm)
2064 {
2065 int more_results, was_cancelled, error, done_count_valid;
2066 int tmp;
2067 TDS_INT8 rows_affected;
2068
2069 CHECK_TDS_EXTRA(tds);
2070
2071 tmp = tds_get_usmallint(tds);
2072
2073 tds_get_smallint(tds); /* state */
2074
2075 more_results = (tmp & TDS_DONE_MORE_RESULTS) != 0;
2076 was_cancelled = (tmp & TDS_DONE_CANCELLED) != 0;
2077 error = (tmp & TDS_DONE_ERROR) != 0;
2078 done_count_valid = (tmp & TDS_DONE_COUNT) != 0;
2079
2080
2081 tdsdump_log(TDS_DBG_FUNC, "tds_process_end: more_results = %d\n"
2082 "\t\twas_cancelled = %d\n"
2083 "\t\terror = %d\n"
2084 "\t\tdone_count_valid = %d\n", more_results, was_cancelled, error, done_count_valid);
2085
2086 if (tds->res_info) {
2087 tds->res_info->more_results = more_results;
2088 /* FIXME this should not happen !!! */
2089 if (tds->current_results == NULL)
2090 tds_set_current_results(tds, tds->res_info);
2091
2092 }
2093
2094 if (flags_parm)
2095 *flags_parm = tmp;
2096
2097 rows_affected = IS_TDS72_PLUS(tds->conn) ? tds_get_int8(tds) : tds_get_int(tds);
2098 tdsdump_log(TDS_DBG_FUNC, " rows_affected = %" PRId64 "\n", rows_affected);
2099
2100 if (was_cancelled || (!more_results && !tds->in_cancel)) {
2101 tdsdump_log(TDS_DBG_FUNC, "tds_process_end() state set to TDS_IDLE\n");
2102 /* reset of in_cancel should must done before setting IDLE */
2103 tds->in_cancel = 0;
2104 if (tds->bulk_query) {
2105 tds->out_flag = TDS_BULK;
2106 tds_set_state(tds, TDS_SENDING);
2107 tds->bulk_query = 0;
2108 } else {
2109 tds_set_state(tds, TDS_IDLE);
2110 if (tds->conn->pending_close)
2111 tds_process_pending_closes(tds);
2112 }
2113 }
2114
2115 if (IS_TDSDEAD(tds))
2116 return TDS_FAIL;
2117
2118 /*
2119 * rows affected is in the tds struct because a query may affect rows but
2120 * have no result set.
2121 */
2122
2123 if (done_count_valid)
2124 tds->rows_affected = rows_affected;
2125 else
2126 tds->rows_affected = TDS_NO_COUNT;
2127
2128 if (IS_TDSDEAD(tds))
2129 return TDS_FAIL;
2130
2131 return was_cancelled ? TDS_CANCELLED : TDS_SUCCESS;
2132 }
2133
2134 /**
2135 * tds_process_env_chg()
2136 * when ever certain things change on the server, such as database, character
2137 * set, language, or block size. A environment change message is generated
2138 * There is no action taken currently, but certain functions at the CLI level
2139 * that return the name of the current database will need to use this.
2140 * \tds
2141 */
2142 static TDSRET
tds_process_env_chg(TDSSOCKET * tds)2143 tds_process_env_chg(TDSSOCKET * tds)
2144 {
2145 unsigned int size;
2146 TDS_TINYINT type;
2147 char *oldval = NULL;
2148 char *newval = NULL;
2149 char **dest;
2150 int new_block_size;
2151 int lcid;
2152 int memrc = 0;
2153
2154 CHECK_TDS_EXTRA(tds);
2155
2156 size = tds_get_usmallint(tds);
2157 if (TDS_UNLIKELY(size < 1)) {
2158 tdsdump_log(TDS_DBG_ERROR, "Got invalid size %u\n", size);
2159 tds_close_socket(tds);
2160 return TDS_FAIL;
2161 }
2162
2163 /*
2164 * this came in a patch, apparently someone saw an env message
2165 * that was different from what we are handling? -- brian
2166 * changed back because it won't handle multibyte chars -- 7.0
2167 */
2168 /* tds_get_n(tds,NULL,size); */
2169
2170 type = tds_get_byte(tds);
2171
2172 /*
2173 * handle collate default change (if you change db or during login)
2174 * this environment is not a string so need different handles
2175 */
2176 if (type == TDS_ENV_SQLCOLLATION) {
2177 /* save new collation */
2178 size = tds_get_byte(tds);
2179 tdsdump_log(TDS_DBG_ERROR, "tds_process_env_chg(): %d bytes of collation data received\n", size);
2180 tdsdump_dump_buf(TDS_DBG_NETWORK, "tds->conn->collation was", tds->conn->collation, 5);
2181 memset(tds->conn->collation, 0, 5);
2182 if (size < 5) {
2183 tds_get_n(tds, tds->conn->collation, size);
2184 } else {
2185 tds_get_n(tds, tds->conn->collation, 5);
2186 tds_get_n(tds, NULL, size - 5);
2187 lcid = (tds->conn->collation[0] + ((int) tds->conn->collation[1] << 8) + ((int) tds->conn->collation[2] << 16)) & 0xffffflu;
2188 tds7_srv_charset_changed(tds->conn, tds->conn->collation[4], lcid);
2189 }
2190 tdsdump_dump_buf(TDS_DBG_NETWORK, "tds->conn->collation now", tds->conn->collation, 5);
2191 /* discard old one */
2192 tds_get_n(tds, NULL, tds_get_byte(tds));
2193 return TDS_SUCCESS;
2194 }
2195
2196 if (type == TDS_ENV_BEGINTRANS) {
2197 /* TODO check size */
2198 size = tds_get_byte(tds);
2199 tds_get_n(tds, tds->conn->tds72_transaction, 8);
2200 tds_get_n(tds, NULL, tds_get_byte(tds));
2201 return TDS_SUCCESS;
2202 }
2203
2204 if (type == TDS_ENV_COMMITTRANS || type == TDS_ENV_ROLLBACKTRANS) {
2205 memset(tds->conn->tds72_transaction, 0, 8);
2206 tds_get_n(tds, NULL, tds_get_byte(tds));
2207 tds_get_n(tds, NULL, tds_get_byte(tds));
2208 return TDS_SUCCESS;
2209 }
2210
2211 /* discard byte values, not still supported */
2212 /* TODO support them */
2213 if (IS_TDS71_PLUS(tds->conn) && type > TDS_ENV_PACKSIZE) {
2214 /* discard rest of the packet */
2215 tds_get_n(tds, NULL, size - 1);
2216 return TDS_SUCCESS;
2217 }
2218
2219 /* fetch the new value */
2220 memrc += tds_alloc_get_string(tds, &newval, tds_get_byte(tds));
2221
2222 /* fetch the old value */
2223 memrc += tds_alloc_get_string(tds, &oldval, tds_get_byte(tds));
2224
2225 if (memrc != 0) {
2226 free(newval);
2227 free(oldval);
2228 return TDS_FAIL;
2229 }
2230
2231 dest = NULL;
2232 switch (type) {
2233 case TDS_ENV_PACKSIZE:
2234 new_block_size = atoi(newval);
2235 if (new_block_size >= 512) {
2236 tdsdump_log(TDS_DBG_INFO1, "changing block size from %s to %d\n", oldval, new_block_size);
2237 /*
2238 * Is possible to have a shrink if server limits packet
2239 * size more than what we specified
2240 */
2241 /* Reallocate buffer if possible (strange values from server or out of memory) use older buffer */
2242 tds_realloc_socket(tds, new_block_size);
2243 }
2244 break;
2245 case TDS_ENV_DATABASE:
2246 dest = &tds->conn->env.database;
2247 break;
2248 case TDS_ENV_LANG:
2249 dest = &tds->conn->env.language;
2250 break;
2251 case TDS_ENV_CHARSET:
2252 tdsdump_log(TDS_DBG_FUNC, "server indicated charset change to \"%s\"\n", newval);
2253 dest = &tds->conn->env.charset;
2254 tds_srv_charset_changed(tds->conn, newval);
2255 break;
2256 }
2257 if (tds->env_chg_func) {
2258 (*(tds->env_chg_func)) (tds, type, oldval, newval);
2259 }
2260
2261 free(oldval);
2262 if (newval) {
2263 if (dest) {
2264 if (*dest)
2265 free(*dest);
2266 *dest = newval;
2267 } else
2268 free(newval);
2269 }
2270
2271 return TDS_SUCCESS;
2272 }
2273
2274 /**
2275 * tds_process_msg() is called for MSG, ERR, or EED tokens and is responsible
2276 * for calling the CLI's message handling routine
2277 * \returns TDS_SUCCESS if informational, TDS_FAIL if error.
2278 */
2279 static TDSRET
tds_process_msg(TDSSOCKET * tds,int marker)2280 tds_process_msg(TDSSOCKET * tds, int marker)
2281 {
2282 int rc;
2283 unsigned int len_sqlstate;
2284 int has_eed = 0;
2285 TDSMESSAGE msg;
2286
2287 CHECK_TDS_EXTRA(tds);
2288
2289 /* make sure message has been freed */
2290 memset(&msg, 0, sizeof(TDSMESSAGE));
2291
2292 /* packet length */
2293 tds_get_smallint(tds);
2294
2295 /* message number */
2296 msg.msgno = tds_get_int(tds);
2297
2298 /* msg state */
2299 msg.state = tds_get_byte(tds);
2300
2301 /* msg level */
2302 msg.severity = tds_get_byte(tds);
2303
2304 /* determine if msg or error */
2305 switch (marker) {
2306 case TDS_EED_TOKEN:
2307 if (msg.severity <= 10)
2308 msg.priv_msg_type = 0;
2309 else
2310 msg.priv_msg_type = 1;
2311
2312 /* read SQL state */
2313 len_sqlstate = tds_get_byte(tds);
2314 msg.sql_state = (char *) malloc(len_sqlstate + 1);
2315 if (!msg.sql_state) {
2316 tds_free_msg(&msg);
2317 return TDS_FAIL;
2318 }
2319
2320 tds_get_n(tds, msg.sql_state, len_sqlstate);
2321 msg.sql_state[len_sqlstate] = '\0';
2322
2323 /* do a better mapping using native errors */
2324 if (strcmp(msg.sql_state, "ZZZZZ") == 0)
2325 TDS_ZERO_FREE(msg.sql_state);
2326
2327 /* if has_eed = 1, extended error data follows */
2328 has_eed = tds_get_byte(tds);
2329
2330 /* junk status and transaction state */
2331 tds_get_smallint(tds);
2332 break;
2333 case TDS_INFO_TOKEN:
2334 msg.priv_msg_type = 0;
2335 break;
2336 case TDS_ERROR_TOKEN:
2337 msg.priv_msg_type = 1;
2338 break;
2339 default:
2340 tdsdump_log(TDS_DBG_ERROR, "tds_process_msg() called with unknown marker '%d'!\n", (int) marker);
2341 tds_free_msg(&msg);
2342 return TDS_FAIL;
2343 }
2344
2345 tdsdump_log(TDS_DBG_ERROR, "tds_process_msg() reading message %d from server\n", msg.msgno);
2346
2347 rc = 0;
2348 /* the message */
2349 rc += tds_alloc_get_string(tds, &msg.message, tds_get_usmallint(tds));
2350
2351 /* server name */
2352 rc += tds_alloc_get_string(tds, &msg.server, tds_get_byte(tds));
2353
2354 if ((!msg.server || !msg.server[0]) && tds->login) {
2355 TDS_ZERO_FREE(msg.server);
2356 if (-1 == asprintf(&msg.server, "[%s]", tds_dstr_cstr(&tds->login->server_name))) {
2357 tdsdump_log(TDS_DBG_ERROR, "out of memory (%d), %s\n", errno, strerror(errno));
2358 return TDS_FAIL;
2359 }
2360 }
2361
2362 /* stored proc name if available */
2363 rc += tds_alloc_get_string(tds, &msg.proc_name, tds_get_byte(tds));
2364
2365 /* line number in the sql statement where the problem occured */
2366 msg.line_number = IS_TDS72_PLUS(tds->conn) ? tds_get_int(tds) : tds_get_smallint(tds);
2367
2368 /*
2369 * If the server doesen't provide an sqlstate, map one via server native errors
2370 * I'm assuming there is not a protocol I'm missing to fetch these from the server?
2371 * I know sybase has an sqlstate column in it's sysmessages table, mssql doesn't and
2372 * TDS_EED_TOKEN is not being called for me.
2373 */
2374 if (msg.sql_state == NULL)
2375 msg.sql_state = tds_alloc_lookup_sqlstate(tds, msg.msgno);
2376
2377
2378 /* In case extended error data is sent, we just try to discard it */
2379 if (has_eed == 1) {
2380 int next_marker;
2381 for (;;) {
2382 switch (next_marker = tds_get_byte(tds)) {
2383 case TDS5_PARAMFMT_TOKEN:
2384 case TDS5_PARAMFMT2_TOKEN:
2385 case TDS5_PARAMS_TOKEN:
2386 if (TDS_FAILED(tds_process_default_tokens(tds, next_marker)))
2387 --rc;
2388 continue;
2389 }
2390 break;
2391 }
2392 tds_unget_byte(tds);
2393 }
2394
2395 /*
2396 * call the msg_handler that was set by an upper layer
2397 * (dblib, ctlib or some other one). Call it with the pointer to
2398 * the "parent" structure.
2399 */
2400
2401 if (rc != 0) {
2402 tds_free_msg(&msg);
2403 return TDS_FAIL;
2404 }
2405
2406 /* special case, */
2407 if (marker == TDS_EED_TOKEN && tds->cur_dyn && !TDS_IS_MSSQL(tds) && msg.msgno == 2782) {
2408 /* we must emulate prepare */
2409 tds->cur_dyn->emulated = 1;
2410 tds_dynamic_deallocated(tds->conn, tds->cur_dyn);
2411 } else if (marker == TDS_INFO_TOKEN && msg.msgno == 16954 && TDS_IS_MSSQL(tds)
2412 && tds->current_op == TDS_OP_CURSOROPEN && tds->cur_cursor) {
2413 /* here mssql say "Executing SQL directly; no cursor." opening cursor */
2414 } else {
2415
2416 if (tds_get_ctx(tds)->msg_handler) {
2417 tdsdump_log(TDS_DBG_ERROR, "tds_process_msg() calling client msg handler\n");
2418 tds_get_ctx(tds)->msg_handler(tds_get_ctx(tds), tds, &msg);
2419 } else if (msg.msgno) {
2420 tdsdump_log(TDS_DBG_WARN,
2421 "Msg %d, Severity %d, State %d, Server %s, Line %d\n%s\n",
2422 msg.msgno,
2423 msg.severity ,
2424 msg.state, msg.server, msg.line_number, msg.message);
2425 }
2426 }
2427
2428 tds_free_msg(&msg);
2429
2430 tdsdump_log(TDS_DBG_ERROR, "tds_process_msg() returning TDS_SUCCESS\n");
2431
2432 return TDS_SUCCESS;
2433 }
2434
2435 /**
2436 * Reads a string from wire in a new allocated buffer
2437 * \tds
2438 * \param string output string
2439 * \param len length of string to read
2440 * \returns 0 for success, -1 on error.
2441 */
2442 static int
tds_alloc_get_string(TDSSOCKET * tds,char ** string,size_t len)2443 tds_alloc_get_string(TDSSOCKET * tds, char **string, size_t len)
2444 {
2445 char *s;
2446 size_t out_len;
2447
2448 CHECK_TDS_EXTRA(tds);
2449
2450 /* assure sufficient space for every conversion */
2451 s = (char *) malloc(len * 4 + 1);
2452 out_len = tds_get_string(tds, len, s, len * 4);
2453 if (!s) {
2454 *string = NULL;
2455 return -1;
2456 }
2457 s = (char*) realloc(s, out_len + 1);
2458 s[out_len] = '\0';
2459 *string = s;
2460 return 0;
2461 }
2462
2463 /**
2464 * \remarks Process the incoming token stream until it finds
2465 * an end token (DONE, DONEPROC, DONEINPROC) with the cancel flag set.
2466 * At that point the connection should be ready to handle a new query.
2467 * \tds
2468 */
2469 TDSRET
tds_process_cancel(TDSSOCKET * tds)2470 tds_process_cancel(TDSSOCKET * tds)
2471 {
2472 CHECK_TDS_EXTRA(tds);
2473
2474 /* silly cases, nothing to do */
2475 if (!tds->in_cancel)
2476 return TDS_SUCCESS;
2477 /* TODO handle cancellation sending data */
2478 if (tds->state != TDS_PENDING)
2479 return TDS_SUCCESS;
2480
2481 /* TODO support TDS5 cancel, wait for cancel packet first, then wait for done */
2482 for (;;) {
2483 TDS_INT result_type;
2484
2485 switch (tds_process_tokens(tds, &result_type, NULL, 0)) {
2486 case TDS_FAIL:
2487 return TDS_FAIL;
2488 case TDS_CANCELLED:
2489 case TDS_SUCCESS:
2490 case TDS_NO_MORE_RESULTS:
2491 return TDS_SUCCESS;
2492 }
2493 }
2494 }
2495
2496 /**
2497 * Finds a dynamic given string id
2498 * \return dynamic or NULL is not found
2499 * \param conn state information for the socket and the TDS protocol
2500 * \param id dynamic id to search
2501 */
2502 TDSDYNAMIC *
tds_lookup_dynamic(TDSCONNECTION * conn,const char * id)2503 tds_lookup_dynamic(TDSCONNECTION * conn, const char *id)
2504 {
2505 TDSDYNAMIC *curr;
2506
2507 CHECK_CONN_EXTRA(conn);
2508
2509 for (curr = conn->dyns; curr != NULL; curr = curr->next) {
2510 if (!strcmp(curr->id, id))
2511 return curr;
2512 }
2513 return NULL;
2514 }
2515
2516 /**
2517 * tds_process_dynamic()
2518 * finds the element of the dyns array for the id
2519 * \tds
2520 * \return allocated dynamic or NULL on failure.
2521 */
2522 static TDSDYNAMIC *
tds_process_dynamic(TDSSOCKET * tds)2523 tds_process_dynamic(TDSSOCKET * tds)
2524 {
2525 unsigned int token_sz;
2526 unsigned char type;
2527 TDS_TINYINT id_len, drain = 0;
2528 char id[TDS_MAX_DYNID_LEN + 1];
2529
2530 CHECK_TDS_EXTRA(tds);
2531
2532 token_sz = tds_get_usmallint(tds);
2533 type = tds_get_byte(tds);
2534 tds_get_byte(tds); /* status */
2535 /* handle only acknowledge */
2536 if (type != TDS_DYN_ACK) {
2537 tdsdump_log(TDS_DBG_ERROR, "Unrecognized TDS5_DYN type %x\n", type);
2538 tds_get_n(tds, NULL, token_sz - 2);
2539 return NULL;
2540 }
2541 id_len = tds_get_byte(tds);
2542 if (id_len > TDS_MAX_DYNID_LEN) {
2543 drain = id_len - TDS_MAX_DYNID_LEN;
2544 id_len = TDS_MAX_DYNID_LEN;
2545 }
2546 id_len = (TDS_TINYINT) tds_get_string(tds, id_len, id, TDS_MAX_DYNID_LEN);
2547 id[id_len] = '\0';
2548 if (drain) {
2549 tds_get_n(tds, NULL, drain);
2550 }
2551 return tds_lookup_dynamic(tds->conn, id);
2552 }
2553
2554 /**
2555 * Process results from dynamic.
2556 * \tds
2557 */
2558 static TDSRET
tds_process_dyn_result(TDSSOCKET * tds)2559 tds_process_dyn_result(TDSSOCKET * tds)
2560 {
2561 unsigned int col, num_cols;
2562 TDSCOLUMN *curcol;
2563 TDSPARAMINFO *info;
2564 TDSDYNAMIC *dyn;
2565
2566 CHECK_TDS_EXTRA(tds);
2567
2568 tds_get_usmallint(tds); /* header size */
2569 num_cols = tds_get_usmallint(tds);
2570
2571 /* read number of columns and allocate the columns structure */
2572 if ((info = tds_alloc_results(num_cols)) == NULL)
2573 return TDS_FAIL;
2574 if (tds->cur_dyn) {
2575 dyn = tds->cur_dyn;
2576 tds_free_param_results(dyn->res_info);
2577 dyn->res_info = info;
2578 } else {
2579 tds_free_param_results(tds->param_info);
2580 tds->param_info = info;
2581 }
2582 tds_set_current_results(tds, info);
2583
2584 for (col = 0; col < info->num_cols; col++) {
2585 curcol = info->columns[col];
2586
2587 tds_get_data_info(tds, curcol, 1);
2588
2589 /* skip locale information */
2590 tds_get_n(tds, NULL, tds_get_byte(tds));
2591 }
2592
2593 return tds_alloc_row(info);
2594 }
2595
2596 /**
2597 * Process new TDS 5.0 token for describing output parameters
2598 * \tds
2599 */
2600 static TDSRET
tds5_process_dyn_result2(TDSSOCKET * tds)2601 tds5_process_dyn_result2(TDSSOCKET * tds)
2602 {
2603 unsigned int col, num_cols;
2604 TDSCOLUMN *curcol;
2605 TDSPARAMINFO *info;
2606 TDSDYNAMIC *dyn = NULL;
2607
2608 CHECK_TDS_EXTRA(tds);
2609
2610 tds_get_uint(tds); /* header size */
2611 num_cols = tds_get_usmallint(tds);
2612
2613 /* read number of columns and allocate the columns structure */
2614 if ((info = tds_alloc_results(num_cols)) == NULL)
2615 return TDS_FAIL;
2616 if (tds->cur_dyn) {
2617 dyn = tds->cur_dyn;
2618 tds_free_param_results(dyn->res_info);
2619 dyn->res_info = info;
2620 } else {
2621 tds_free_param_results(tds->param_info);
2622 tds->param_info = info;
2623 }
2624 tds_set_current_results(tds, info);
2625
2626 for (col = 0; col < info->num_cols; col++) {
2627 curcol = info->columns[col];
2628
2629 /* TODO reuse tds_get_data_info code, sligthly different */
2630
2631 /* column name */
2632 tds_dstr_get(tds, &curcol->column_name, tds_get_byte(tds));
2633
2634 /* column status */
2635 curcol->column_flags = tds_get_int(tds);
2636 curcol->column_nullable = (curcol->column_flags & 0x20) > 0;
2637
2638 /* user type */
2639 curcol->column_usertype = tds_get_int(tds);
2640
2641 /* column type */
2642 tds_set_column_type(tds->conn, curcol, tds_get_byte(tds));
2643
2644 curcol->funcs->get_info(tds, curcol);
2645
2646 /* Adjust column size according to client's encoding */
2647 curcol->on_server.column_size = curcol->column_size;
2648 adjust_character_column_size(tds, curcol);
2649
2650 /* discard Locale */
2651 tds_get_n(tds, NULL, tds_get_byte(tds));
2652
2653 tdsdump_log(TDS_DBG_INFO1, "elem %d:\n", col);
2654 tdsdump_log(TDS_DBG_INFO1, "\tcolumn_name=[%s]\n", tds_dstr_cstr(&curcol->column_name));
2655 tdsdump_log(TDS_DBG_INFO1, "\tflags=%x utype=%d type=%d varint=%d\n",
2656 curcol->column_flags, curcol->column_usertype, curcol->column_type, curcol->column_varint_size);
2657 tdsdump_log(TDS_DBG_INFO1, "\tcolsize=%d prec=%d scale=%d\n",
2658 curcol->column_size, curcol->column_prec, curcol->column_scale);
2659
2660 /*
2661 * As of ASE 16.0, Sybase servers have started allowing dynamic
2662 * query (prepared statement) declarations with IMAGE or (N)TEXT
2663 * parameters. However, subsequent attempts to instantiate
2664 * these queries have been failing with message 3805, "The token
2665 * datastream length was not correct." In such cases, switch on
2666 * dynamic query emulation (as already needed for older Sybase
2667 * versions that immediately reject these declarations) and
2668 * explicitly discard column information to avoid misconstruing
2669 * the status of subsequent queries that yield no row results.
2670 */
2671 if (dyn != NULL && !TDS_IS_MSSQL(tds) && is_blob_col(curcol)) {
2672 dyn->emulated = 1;
2673 tds_dynamic_deallocated(tds->conn, tds->cur_dyn);
2674 dyn = NULL;
2675 }
2676 }
2677
2678 if (tds->cur_dyn != NULL && tds->cur_dyn->emulated) {
2679 tds_set_current_results(tds, NULL);
2680 }
2681
2682 return tds_alloc_row(info);
2683 }
2684
2685 /**
2686 * tds_get_token_size() returns the size of a fixed length token
2687 * used by tds_process_cancel() to determine how to read past a token
2688 * \param marker token type.
2689 */
2690 int
tds_get_token_size(int marker)2691 tds_get_token_size(int marker)
2692 {
2693 /* TODO finish */
2694 switch (marker) {
2695 case TDS_DONE_TOKEN:
2696 case TDS_DONEPROC_TOKEN:
2697 case TDS_DONEINPROC_TOKEN:
2698 return 8;
2699 case TDS_RETURNSTATUS_TOKEN:
2700 return 4;
2701 case TDS_PROCID_TOKEN:
2702 return 8;
2703 default:
2704 return 0;
2705 }
2706 }
2707
2708
2709 /**
2710 * tds_process_compute_names() processes compute result sets.
2711 * \tds
2712 */
2713 static TDSRET
tds_process_compute_names(TDSSOCKET * tds)2714 tds_process_compute_names(TDSSOCKET * tds)
2715 {
2716 int hdrsize;
2717 int num_cols = 0;
2718 TDS_USMALLINT compute_id = 0;
2719 TDSCOMPUTEINFO *info;
2720 int col;
2721
2722 struct namelist *head = NULL, *cur;
2723
2724 CHECK_TDS_EXTRA(tds);
2725
2726 hdrsize = tds_get_usmallint(tds);
2727 tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. hdrsize = %d\n", hdrsize);
2728
2729 /*
2730 * compute statement id which this relates
2731 * to. You can have more than one compute
2732 * statement in a SQL statement
2733 */
2734 compute_id = tds_get_usmallint(tds);
2735
2736 if ((num_cols = tds_read_namelist(tds, hdrsize - 2, &head, 0)) <= 0)
2737 return TDS_FAIL;
2738
2739 tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. num_cols = %d\n", num_cols);
2740
2741 if ((tds->comp_info = tds_alloc_compute_results(tds, num_cols, 0)) == NULL)
2742 goto memory_error;
2743
2744 tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. num_comp_info = %d\n", tds->num_comp_info);
2745
2746 info = tds->comp_info[tds->num_comp_info - 1];
2747 tds_set_current_results(tds, info);
2748
2749 info->computeid = compute_id;
2750
2751 cur = head;
2752 for (col = 0; col < num_cols; col++) {
2753 TDSCOLUMN *curcol = info->columns[col];
2754
2755 if (!tds_dstr_copy(&curcol->column_name, cur->name))
2756 goto memory_error;
2757
2758 cur = cur->next;
2759 }
2760 tds_free_namelist(head);
2761 return TDS_SUCCESS;
2762
2763 memory_error:
2764 tds_free_namelist(head);
2765 return TDS_FAIL;
2766 }
2767
2768 /**
2769 * tds7_process_compute_result() processes compute result sets for TDS 7/8.
2770 * They is are very similar to normal result sets.
2771 * \tds
2772 */
2773 static TDSRET
tds7_process_compute_result(TDSSOCKET * tds)2774 tds7_process_compute_result(TDSSOCKET * tds)
2775 {
2776 unsigned int col, num_cols;
2777 TDS_TINYINT by_cols;
2778 TDS_SMALLINT *cur_by_col;
2779 TDS_USMALLINT compute_id;
2780 TDSCOLUMN *curcol;
2781 TDSCOMPUTEINFO *info;
2782
2783 CHECK_TDS_EXTRA(tds);
2784
2785 /*
2786 * number of compute columns returned - so
2787 * COMPUTE SUM(x), AVG(x)... would return
2788 * num_cols = 2
2789 */
2790
2791 num_cols = tds_get_usmallint(tds);
2792
2793 tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. num_cols = %u\n", num_cols);
2794
2795 /*
2796 * compute statement id which this relates
2797 * to. You can have more than one compute
2798 * statement in a SQL statement
2799 */
2800
2801 compute_id = tds_get_usmallint(tds);
2802
2803 tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. compute_id = %u\n", compute_id);
2804 /*
2805 * number of "by" columns in compute - so
2806 * COMPUTE SUM(x) BY a, b, c would return
2807 * by_cols = 3
2808 */
2809
2810 by_cols = tds_get_byte(tds);
2811 tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. by_cols = %d\n", by_cols);
2812
2813 if ((tds->comp_info = tds_alloc_compute_results(tds, num_cols, by_cols)) == NULL)
2814 return TDS_FAIL;
2815
2816 tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. num_comp_info = %d\n", tds->num_comp_info);
2817
2818 info = tds->comp_info[tds->num_comp_info - 1];
2819 tds_set_current_results(tds, info);
2820
2821 tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 0\n");
2822
2823 info->computeid = compute_id;
2824
2825 /*
2826 * the by columns are a list of the column
2827 * numbers in the select statement
2828 */
2829
2830 cur_by_col = info->bycolumns;
2831 for (col = 0; col < by_cols; col++) {
2832 *cur_by_col = tds_get_smallint(tds);
2833 cur_by_col++;
2834 }
2835 tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 1\n");
2836
2837 for (col = 0; col < num_cols; col++) {
2838 tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 2\n");
2839 curcol = info->columns[col];
2840
2841 curcol->column_operator = tds_get_byte(tds);
2842 curcol->column_operand = tds_get_smallint(tds);
2843
2844 tds7_get_data_info(tds, curcol);
2845
2846 if (tds_dstr_isempty(&curcol->column_name))
2847 if (!tds_dstr_copy(&curcol->column_name, tds_pr_op(curcol->column_operator)))
2848 return TDS_FAIL;
2849 }
2850
2851 /* all done now allocate a row for tds_process_row to use */
2852 tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 5 \n");
2853 return tds_alloc_compute_row(info);
2854 }
2855
2856 /**
2857 * Reads cursor command results.
2858 * This contains status of cursors.
2859 * \tds
2860 */
2861 static TDSRET
tds_process_cursor_tokens(TDSSOCKET * tds)2862 tds_process_cursor_tokens(TDSSOCKET * tds)
2863 {
2864 TDS_USMALLINT hdrsize;
2865 TDS_INT cursor_id;
2866 TDS_TINYINT namelen;
2867 TDS_USMALLINT cursor_status;
2868 TDSCURSOR *cursor;
2869
2870 CHECK_TDS_EXTRA(tds);
2871
2872 hdrsize = tds_get_usmallint(tds);
2873 cursor_id = tds_get_int(tds);
2874 hdrsize -= sizeof(TDS_INT);
2875 if (cursor_id == 0){
2876 namelen = tds_get_byte(tds);
2877 hdrsize -= 1;
2878 /* discard name */
2879 tds_get_n(tds, NULL, namelen);
2880 hdrsize -= namelen;
2881 }
2882 tds_get_byte(tds); /* cursor command */
2883 cursor_status = tds_get_usmallint(tds);
2884 hdrsize -= 3;
2885
2886 if (hdrsize == sizeof(TDS_INT))
2887 tds_get_int(tds); /* row count TODO useless ?? */
2888
2889 if (tds->cur_cursor) {
2890 cursor = tds->cur_cursor;
2891 cursor->cursor_id = cursor_id;
2892 cursor->srv_status = cursor_status;
2893 if ((cursor_status & TDS_CUR_ISTAT_DEALLOC) != 0)
2894 tds_cursor_deallocated(tds->conn, cursor);
2895 }
2896 return TDS_SUCCESS;
2897 }
2898
2899 /**
2900 * Process option cmd results.
2901 * This token is available only on TDS 5.0 (Sybase).
2902 * \tds
2903 */
2904 static TDSRET
tds5_process_optioncmd(TDSSOCKET * tds)2905 tds5_process_optioncmd(TDSSOCKET * tds)
2906 {
2907 TDS_INT command;
2908 TDS_TINYINT option;
2909 TDS_TINYINT argsize;
2910 TDS_INT arg;
2911
2912 CHECK_TDS_EXTRA(tds);
2913
2914 tdsdump_log(TDS_DBG_INFO1, "tds5_process_optioncmd()\n");
2915
2916 assert(IS_TDS50(tds->conn));
2917
2918 tds_get_usmallint(tds); /* length */
2919 command = tds_get_byte(tds);
2920 option = tds_get_byte(tds);
2921 argsize = tds_get_byte(tds);
2922
2923 switch (argsize) {
2924 case 0:
2925 arg = 0;
2926 break;
2927 case 1:
2928 arg = tds_get_byte(tds);
2929 break;
2930 case 4:
2931 arg = tds_get_int(tds);
2932 break;
2933 default:
2934 tdsdump_log(TDS_DBG_INFO1, "oops: cannot process option %d of size %d\n", option, argsize);
2935 /* ignore argument */
2936 tds_get_n(tds, NULL, argsize);
2937 return TDS_FAIL;
2938 }
2939 tdsdump_log(TDS_DBG_INFO1, "received option %d value %d\n", option, arg);
2940
2941 if (command != TDS_OPT_INFO)
2942 return TDS_FAIL;
2943
2944 tds->option_value = arg;
2945
2946 return TDS_SUCCESS;
2947 }
2948
2949 /**
2950 * Returns string representation for a given operation
2951 * \param op operation code
2952 * \return string representation. Empty if not found.
2953 */
2954 static const char *
tds_pr_op(int op)2955 tds_pr_op(int op)
2956 {
2957 /** \cond HIDDEN_SYMBOLS */
2958 #define TYPE(con, s) case con: return s; break
2959 /** \endcond */
2960 switch (op) {
2961 TYPE(SYBAOPAVG, "avg");
2962 TYPE(SYBAOPAVGU, "avg");
2963 TYPE(SYBAOPCNT, "count");
2964 TYPE(SYBAOPCNTU, "count");
2965 TYPE(SYBAOPMAX, "max");
2966 TYPE(SYBAOPMIN, "min");
2967 TYPE(SYBAOPSUM, "sum");
2968 TYPE(SYBAOPSUMU, "sum");
2969 TYPE(SYBAOPCHECKSUM_AGG, "checksum_agg");
2970 TYPE(SYBAOPCNT_BIG, "count");
2971 TYPE(SYBAOPSTDEV, "stdevp");
2972 TYPE(SYBAOPSTDEVP, "stdevp");
2973 TYPE(SYBAOPVAR, "var");
2974 TYPE(SYBAOPVARP, "varp");
2975 default:
2976 break;
2977 }
2978 return "";
2979 #undef TYPE
2980 }
2981
2982 /**
2983 * Returns string representation of the given type.
2984 * \param type data type
2985 * \return type as string. Empty if not found.
2986 */
2987 const char *
tds_prtype(int type)2988 tds_prtype(int type)
2989 {
2990 /** \cond HIDDEN_SYMBOLS */
2991 #define TYPE(con, s) case con: return s; break
2992 /** \endcond */
2993 switch (type) {
2994 TYPE(SYBAOPAVG, "avg");
2995 TYPE(SYBAOPCNT, "count");
2996 TYPE(SYBAOPMAX, "max");
2997 TYPE(SYBAOPMIN, "min");
2998 TYPE(SYBAOPSUM, "sum");
2999
3000 TYPE(SYBBINARY, "binary");
3001 TYPE(SYBLONGBINARY, "longbinary");
3002 TYPE(SYBBIT, "bit");
3003 TYPE(SYBBITN, "bit-null");
3004 TYPE(SYBCHAR, "char");
3005 TYPE(SYBDATETIME4, "smalldatetime");
3006 TYPE(SYBDATETIME, "datetime");
3007 TYPE(SYBDATETIMN, "datetime-null");
3008 TYPE(SYBDECIMAL, "decimal");
3009 TYPE(SYBFLT8, "float");
3010 TYPE(SYBFLTN, "float-null");
3011 TYPE(SYBIMAGE, "image");
3012 TYPE(SYBINT1, "tinyint");
3013 TYPE(SYBINT2, "smallint");
3014 TYPE(SYBINT4, "int");
3015 TYPE(SYBINT8, "bigint");
3016 TYPE(SYBUINT1, "unsigned tinyint");
3017 TYPE(SYBUINT2, "unsigned smallint");
3018 TYPE(SYBUINT4, "unsigned int");
3019 TYPE(SYBUINT8, "unsigned bigint");
3020 TYPE(SYBINTN, "integer-null");
3021 TYPE(SYBMONEY4, "smallmoney");
3022 TYPE(SYBMONEY, "money");
3023 TYPE(SYBMONEYN, "money-null");
3024 TYPE(SYBNTEXT, "UCS-2 text");
3025 TYPE(SYBNVARCHAR, "UCS-2 varchar");
3026 TYPE(SYBNUMERIC, "numeric");
3027 TYPE(SYBREAL, "real");
3028 TYPE(SYBTEXT, "text");
3029 TYPE(SYBUNIQUE, "uniqueidentifier");
3030 TYPE(SYBVARBINARY, "varbinary");
3031 TYPE(SYBVARCHAR, "varchar");
3032 TYPE(SYBVARIANT, "variant");
3033 TYPE(SYBVOID, "void");
3034 TYPE(XSYBBINARY, "xbinary");
3035 TYPE(XSYBCHAR, "xchar");
3036 TYPE(XSYBNCHAR, "x UCS-2 char");
3037 TYPE(XSYBNVARCHAR, "x UCS-2 varchar");
3038 TYPE(XSYBVARBINARY, "xvarbinary");
3039 TYPE(XSYBVARCHAR, "xvarchar");
3040 TYPE(SYBMSXML, "xml");
3041 TYPE(SYBMSDATE, "date");
3042 TYPE(SYBMSTIME, "time");
3043 TYPE(SYBMSDATETIME2, "datetime2");
3044 TYPE(SYBMSDATETIMEOFFSET, "datetimeoffset");
3045 default:
3046 break;
3047 }
3048 return "";
3049 #undef TYPE
3050 }
3051
3052 /**
3053 * Returns string representation for a given token type
3054 * \param marker token type
3055 * \return string representation. Empty if not token not valid.
3056 */
3057 static const char *
tds_token_name(unsigned char marker)3058 tds_token_name(unsigned char marker)
3059 {
3060 switch (marker) {
3061
3062 case TDS5_PARAMFMT2_TOKEN:
3063 return "TDS5_PARAMFMT2";
3064 case TDS_ORDERBY2_TOKEN:
3065 return "ORDERBY2";
3066 case TDS_ROWFMT2_TOKEN:
3067 return "ROWFMT2";
3068 case TDS_LOGOUT_TOKEN:
3069 return "LOGOUT";
3070 case TDS_RETURNSTATUS_TOKEN:
3071 return "RETURNSTATUS";
3072 case TDS_PROCID_TOKEN:
3073 return "PROCID";
3074 case TDS7_RESULT_TOKEN:
3075 return "TDS7_RESULT";
3076 case TDS_CURINFO_TOKEN:
3077 return "TDS_CURINFO";
3078 case TDS7_COMPUTE_RESULT_TOKEN:
3079 return "TDS7_COMPUTE_RESULT";
3080 case TDS_COLNAME_TOKEN:
3081 return "COLNAME";
3082 case TDS_COLFMT_TOKEN:
3083 return "COLFMT";
3084 case TDS_DYNAMIC2_TOKEN:
3085 return "DYNAMIC2";
3086 case TDS_TABNAME_TOKEN:
3087 return "TABNAME";
3088 case TDS_COLINFO_TOKEN:
3089 return "COLINFO";
3090 case TDS_COMPUTE_NAMES_TOKEN:
3091 return "COMPUTE_NAMES";
3092 case TDS_COMPUTE_RESULT_TOKEN:
3093 return "COMPUTE_RESULT";
3094 case TDS_ORDERBY_TOKEN:
3095 return "ORDERBY";
3096 case TDS_ERROR_TOKEN:
3097 return "ERROR";
3098 case TDS_INFO_TOKEN:
3099 return "INFO";
3100 case TDS_PARAM_TOKEN:
3101 return "PARAM";
3102 case TDS_LOGINACK_TOKEN:
3103 return "LOGINACK";
3104 case TDS_CONTROL_TOKEN:
3105 return "CONTROL";
3106 case TDS_ROW_TOKEN:
3107 return "ROW";
3108 case TDS_NBC_ROW_TOKEN:
3109 return "NBC_ROW";
3110 case TDS_CMP_ROW_TOKEN:
3111 return "CMP_ROW";
3112 case TDS5_PARAMS_TOKEN:
3113 return "TDS5_PARAMS";
3114 case TDS_CAPABILITY_TOKEN:
3115 return "CAPABILITY";
3116 case TDS_ENVCHANGE_TOKEN:
3117 return "ENVCHANGE";
3118 case TDS_EED_TOKEN:
3119 return "EED";
3120 case TDS_DBRPC_TOKEN:
3121 return "DBRPC";
3122 case TDS5_DYNAMIC_TOKEN:
3123 return "TDS5_DYNAMIC";
3124 case TDS5_PARAMFMT_TOKEN:
3125 return "TDS5_PARAMFMT";
3126 case TDS_AUTH_TOKEN:
3127 return "AUTH";
3128 case TDS_RESULT_TOKEN:
3129 return "RESULT";
3130 case TDS_DONE_TOKEN:
3131 return "DONE";
3132 case TDS_DONEPROC_TOKEN:
3133 return "DONEPROC";
3134 case TDS_DONEINPROC_TOKEN:
3135 return "DONEINPROC";
3136
3137 default:
3138 break;
3139 }
3140
3141 return "";
3142 }
3143
3144 /**
3145 * Adjust column size according to client's encoding
3146 * \tds
3147 * \param curcol column to adjust
3148 */
3149 static void
adjust_character_column_size(TDSSOCKET * tds,TDSCOLUMN * curcol)3150 adjust_character_column_size(TDSSOCKET * tds, TDSCOLUMN * curcol)
3151 {
3152 CHECK_TDS_EXTRA(tds);
3153 CHECK_COLUMN_EXTRA(curcol);
3154
3155 if (is_unicode_type(curcol->on_server.column_type))
3156 curcol->char_conv = tds->conn->char_convs[client2ucs2];
3157
3158 /* Sybase UNI(VAR)CHAR fields are transmitted via SYBLONGBINARY and in UTF-16 */
3159 if (curcol->on_server.column_type == SYBLONGBINARY && (
3160 curcol->column_usertype == USER_UNICHAR_TYPE ||
3161 curcol->column_usertype == USER_UNIVARCHAR_TYPE)) {
3162 #ifdef WORDS_BIGENDIAN
3163 static const char sybase_utf[] = "UTF-16BE";
3164 #else
3165 static const char sybase_utf[] = "UTF-16LE";
3166 #endif
3167
3168 curcol->char_conv = tds_iconv_get(tds->conn, tds->conn->char_convs[client2ucs2]->from.charset.name, sybase_utf);
3169
3170 /* fallback to UCS-2LE */
3171 /* FIXME should be useless. Does not works always */
3172 if (!curcol->char_conv)
3173 curcol->char_conv = tds->conn->char_convs[client2ucs2];
3174 }
3175
3176 /* FIXME: and sybase ?? */
3177 if (!curcol->char_conv && IS_TDS7_PLUS(tds->conn) && is_ascii_type(curcol->on_server.column_type))
3178 curcol->char_conv = tds->conn->char_convs[client2server_chardata];
3179
3180 if (!USE_ICONV || !curcol->char_conv)
3181 return;
3182
3183 curcol->on_server.column_size = curcol->column_size;
3184 curcol->column_size = determine_adjusted_size(curcol->char_conv, curcol->column_size);
3185
3186 tdsdump_log(TDS_DBG_INFO1, "adjust_character_column_size:\n"
3187 "\tServer charset: %s\n"
3188 "\tServer column_size: %d\n"
3189 "\tClient charset: %s\n"
3190 "\tClient column_size: %d\n",
3191 curcol->char_conv->to.charset.name,
3192 curcol->on_server.column_size,
3193 curcol->char_conv->from.charset.name,
3194 curcol->column_size);
3195 }
3196
3197 /**
3198 * Allow for maximum possible size of converted data,
3199 * while being careful about integer division truncation.
3200 * All character data pass through iconv. It doesn't matter if the server side
3201 * is Unicode or not; even Latin1 text need conversion if,
3202 * for example, the client is UTF-8.
3203 * \param char_conv conversion structure
3204 * \param size unconverted byte size
3205 * \return maximum size for converted string
3206 */
3207 int
determine_adjusted_size(const TDSICONV * char_conv,int size)3208 determine_adjusted_size(const TDSICONV * char_conv, int size)
3209 {
3210 if (!char_conv)
3211 return size;
3212
3213 /* avoid possible overflow */
3214 if (size >= 0x10000000)
3215 return 0x7fffffff;
3216
3217 size *= char_conv->from.charset.max_bytes_per_char;
3218 if (size % char_conv->to.charset.min_bytes_per_char)
3219 size += char_conv->to.charset.min_bytes_per_char;
3220 size /= char_conv->to.charset.min_bytes_per_char;
3221
3222 return size;
3223 }
3224
3225 /** @} */
3226