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