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