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