Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns
3 : * Copyright (C) 2005-2015 Frediano Ziglio
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Library General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2 of the License, or (at your option) any later version.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Library General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Library General Public
16 : * License along with this library; if not, write to the
17 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 : * Boston, MA 02111-1307, USA.
19 : */
20 :
21 : /**
22 : * \file
23 : * \brief Contains all routines to get replies from server
24 : */
25 : #include <config.h>
26 :
27 : #if HAVE_STRING_H
28 : #include <string.h>
29 : #endif /* HAVE_STRING_H */
30 :
31 : #if HAVE_STDLIB_H
32 : #include <stdlib.h>
33 : #endif /* HAVE_STDLIB_H */
34 :
35 : #include <assert.h>
36 :
37 : #if HAVE_MALLOC_H
38 : #include <malloc.h>
39 : #endif /* HAVE_MALLOC_H */
40 :
41 : #include <freetds/tds.h>
42 : #include <freetds/utils/string.h>
43 : #include <freetds/convert.h>
44 : #include <freetds/iconv.h>
45 : #include <freetds/checks.h>
46 : #include <freetds/bytes.h>
47 : #include <freetds/alloca.h>
48 : #include <freetds/encodings.h>
49 : #include <freetds/enum_cap.h>
50 : #include <freetds/replacements.h>
51 :
52 : /** \cond HIDDEN_SYMBOLS */
53 : #define USE_ICONV (tds->conn->use_iconv)
54 :
55 : #define TDS_GET_COLUMN_TYPE(col) do { \
56 : TDS_TINYINT _tds_type = tds_get_byte(tds); \
57 : if (!is_tds_type_valid(_tds_type)) \
58 : return TDS_FAIL; \
59 : tds_set_column_type(tds->conn, col, (TDS_SERVER_TYPE) _tds_type); \
60 : } while(0)
61 :
62 : #define TDS_GET_COLUMN_INFO(tds, col) \
63 : TDS_PROPAGATE(col->funcs->get_info(tds, col))
64 :
65 : /** \endcond */
66 :
67 : static TDSRET tds_process_info(TDSSOCKET * tds, int marker);
68 : static TDSRET tds_process_compute_result(TDSSOCKET * tds);
69 : static TDSRET tds_process_compute_names(TDSSOCKET * tds);
70 : static TDSRET tds7_process_compute_result(TDSSOCKET * tds);
71 : static TDSRET tds5_process_result(TDSSOCKET * tds);
72 : static TDSRET tds_process_col_name(TDSSOCKET * tds);
73 : static TDSRET tds_process_col_fmt(TDSSOCKET * tds);
74 : static TDSRET tds_process_tabname(TDSSOCKET *tds);
75 : static TDSRET tds_process_colinfo(TDSSOCKET * tds, char **names, int num_names);
76 : static TDSRET tds_process_compute(TDSSOCKET * tds);
77 : static TDSRET tds_process_cursor_tokens(TDSSOCKET * tds);
78 : static TDSRET tds_process_row(TDSSOCKET * tds);
79 : static TDSRET tds_process_nbcrow(TDSSOCKET * tds);
80 : static TDSRET tds_process_featureextack(TDSSOCKET * tds);
81 : static TDSRET tds_process_param_result(TDSSOCKET * tds, TDSPARAMINFO ** info);
82 : static TDSRET tds7_process_result(TDSSOCKET * tds);
83 : static TDSDYNAMIC *tds_process_dynamic(TDSSOCKET * tds);
84 : static TDSRET tds_process_auth(TDSSOCKET * tds);
85 : static TDSRET tds_process_env_chg(TDSSOCKET * tds);
86 : static TDSRET tds_process_param_result_tokens(TDSSOCKET * tds);
87 : static TDSRET tds_process_params_result_token(TDSSOCKET * tds);
88 : static TDSRET tds_process_dyn_result(TDSSOCKET * tds);
89 : static TDSRET tds5_process_result2(TDSSOCKET * tds);
90 : static TDSRET tds5_process_dyn_result2(TDSSOCKET * tds);
91 : static TDSRET tds_process_default_tokens(TDSSOCKET * tds, int marker);
92 : static TDSRET tds5_process_optioncmd(TDSSOCKET * tds);
93 : static TDSRET tds_process_end(TDSSOCKET * tds, int marker, /*@out@*/ int *flags_parm);
94 :
95 : static TDSRET tds_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int is_param);
96 : static /*@observer@*/ const char *tds_token_name(unsigned char marker);
97 : static void adjust_character_column_size(TDSSOCKET * tds, TDSCOLUMN * curcol);
98 : static int determine_adjusted_size(const TDSICONV * char_conv, int size);
99 : static /*@observer@*/ const char *tds_pr_op(int op);
100 : static int tds_alloc_get_string(TDSSOCKET * tds, /*@special@*/ char **string, size_t len) /*allocates *string*/;
101 :
102 : /**
103 : * \ingroup libtds
104 : * \defgroup token Results processing
105 : * Handle tokens in packets. Many PDU (packets data unit) contain tokens.
106 : * (like result description, rows, data, errors and many other).
107 : */
108 :
109 :
110 : /**
111 : * \addtogroup token
112 : * @{
113 : */
114 :
115 : /**
116 : * tds_process_default_tokens() is a catch all function that is called to
117 : * process tokens not known to other tds_process_* routines
118 : * @tds
119 : * @param marker Token type
120 : */
121 : static TDSRET
122 59516 : tds_process_default_tokens(TDSSOCKET * tds, int marker)
123 : {
124 : int tok_size;
125 : int done_flags;
126 : TDS_INT ret_status;
127 : TDS_CAPABILITY_TYPE *cap;
128 :
129 59516 : CHECK_TDS_EXTRA(tds);
130 :
131 59516 : tdsdump_log(TDS_DBG_FUNC, "tds_process_default_tokens() marker is %x(%s)\n", marker, tds_token_name(marker));
132 :
133 59516 : if (IS_TDSDEAD(tds)) {
134 56 : tdsdump_log(TDS_DBG_FUNC, "leaving tds_process_default_tokens() connection dead\n");
135 56 : tds_close_socket(tds);
136 56 : return TDS_FAIL;
137 : }
138 :
139 59460 : switch (marker) {
140 716 : case TDS_AUTH_TOKEN:
141 716 : return tds_process_auth(tds);
142 : break;
143 20409 : case TDS_ENVCHANGE_TOKEN:
144 20409 : return tds_process_env_chg(tds);
145 : break;
146 3648 : case TDS_DONE_TOKEN:
147 : case TDS_DONEPROC_TOKEN:
148 : case TDS_DONEINPROC_TOKEN:
149 3648 : return tds_process_end(tds, marker, &done_flags);
150 : break;
151 0 : case TDS_PROCID_TOKEN:
152 0 : tds_get_n(tds, NULL, 8);
153 0 : break;
154 0 : case TDS_RETURNSTATUS_TOKEN:
155 0 : ret_status = tds_get_int(tds);
156 0 : marker = tds_peek(tds);
157 0 : if (marker != TDS_PARAM_TOKEN && marker != TDS_DONEPROC_TOKEN && marker != TDS_DONE_TOKEN
158 0 : && marker != TDS5_PARAMFMT_TOKEN)
159 : break;
160 0 : tds->has_status = true;
161 0 : tds->ret_status = ret_status;
162 0 : tdsdump_log(TDS_DBG_INFO1, "tds_process_default_tokens: return status is %d\n", tds->ret_status);
163 : break;
164 15637 : case TDS_ERROR_TOKEN:
165 : case TDS_INFO_TOKEN:
166 : case TDS_EED_TOKEN:
167 15637 : return tds_process_info(tds, marker);
168 : break;
169 716 : case TDS_CAPABILITY_TOKEN:
170 716 : tok_size = tds_get_usmallint(tds);
171 716 : cap = tds->conn->capabilities.types;
172 716 : memset(cap, 0, 2*sizeof(*cap));
173 716 : cap[0].type = 1;
174 716 : cap[0].len = sizeof(cap[0].values);
175 716 : cap[1].type = 2;
176 716 : cap[1].len = sizeof(cap[1].values);
177 2864 : while (tok_size > 1) {
178 : unsigned char type, size, *p;
179 :
180 1432 : type = tds_get_byte(tds);
181 1432 : size = tds_get_byte(tds);
182 1432 : tok_size -= 2 + size;
183 1432 : if (type != 1 && type != 2) {
184 0 : tds_get_n(tds, NULL, size);
185 0 : continue;
186 : }
187 1432 : if (size > sizeof(cap->values)) {
188 0 : tds_get_n(tds, NULL, size - sizeof(cap->values));
189 0 : size = sizeof(cap->values);
190 : }
191 1432 : p = (unsigned char *) &cap[type];
192 1432 : if (!tds_get_n(tds, p-size, size))
193 : return TDS_FAIL;
194 : /*
195 : * Sybase 11.0 servers return the wrong length in the capability packet,
196 : * causing us to read past the done packet.
197 : */
198 1432 : if (tds->conn->product_version < TDS_SYB_VER(12, 0, 0) && type == 2)
199 : break;
200 : }
201 : break;
202 : /* PARAM_TOKEN can be returned inserting text in db, to return new timestamp */
203 0 : case TDS_PARAM_TOKEN:
204 0 : tds_unget_byte(tds);
205 0 : return tds_process_param_result_tokens(tds);
206 : break;
207 0 : case TDS7_RESULT_TOKEN:
208 0 : return tds7_process_result(tds);
209 : break;
210 8 : case TDS_OPTIONCMD_TOKEN:
211 8 : return tds5_process_optioncmd(tds);
212 : break;
213 0 : case TDS_RESULT_TOKEN:
214 0 : return tds5_process_result(tds);
215 : break;
216 0 : case TDS_ROWFMT2_TOKEN:
217 0 : return tds5_process_result2(tds);
218 : break;
219 0 : case TDS_COLNAME_TOKEN:
220 0 : return tds_process_col_name(tds);
221 : break;
222 0 : case TDS_COLFMT_TOKEN:
223 0 : return tds_process_col_fmt(tds);
224 : break;
225 0 : case TDS_ROW_TOKEN:
226 0 : return tds_process_row(tds);
227 : break;
228 2 : case TDS5_PARAMFMT_TOKEN:
229 : /* store discarded parameters in param_info, not in old dynamic */
230 2 : tds_release_cur_dyn(tds);
231 2 : return tds_process_dyn_result(tds);
232 : break;
233 0 : case TDS5_PARAMFMT2_TOKEN:
234 0 : tds_release_cur_dyn(tds);
235 0 : return tds5_process_dyn_result2(tds);
236 : break;
237 2 : case TDS5_PARAMS_TOKEN:
238 : /* save params */
239 2 : return tds_process_params_result_token(tds);
240 : break;
241 0 : case TDS_CURINFO_TOKEN:
242 0 : return tds_process_cursor_tokens(tds);
243 : break;
244 7468 : case TDS_CONTROL_FEATUREEXTACK_TOKEN:
245 7468 : if (IS_TDS74_PLUS(tds->conn))
246 714 : return tds_process_featureextack(tds);
247 : /* fall through */
248 : case TDS5_DYNAMIC_TOKEN:
249 : case TDS_LOGINACK_TOKEN:
250 : case TDS_ORDERBY_TOKEN:
251 17600 : tdsdump_log(TDS_DBG_WARN, "Eating %s token\n", tds_token_name(marker));
252 17600 : tds_get_n(tds, NULL, tds_get_usmallint(tds));
253 17600 : break;
254 0 : case TDS_MSG_TOKEN:
255 0 : tok_size = tds_get_byte(tds);
256 0 : if (tok_size >= 3) {
257 0 : tds_get_byte(tds);
258 0 : tds5_negotiate_set_msg_type(tds->conn->authentication, tds_get_usmallint(tds));
259 0 : tok_size -= 3;
260 : }
261 0 : tds_get_n(tds, NULL, tok_size);
262 0 : break;
263 2 : case TDS_TABNAME_TOKEN: /* used for FOR BROWSE query */
264 2 : return tds_process_tabname(tds);
265 : break;
266 6 : case TDS_COLINFO_TOKEN:
267 6 : return tds_process_colinfo(tds, NULL, 0);
268 : break;
269 0 : case TDS_SESSIONSTATE_TOKEN:
270 : case TDS_ORDERBY2_TOKEN:
271 0 : tdsdump_log(TDS_DBG_WARN, "Eating %s token\n", tds_token_name(marker));
272 0 : tds_get_n(tds, NULL, tds_get_uint(tds));
273 0 : break;
274 0 : case TDS_NBC_ROW_TOKEN:
275 0 : return tds_process_nbcrow(tds);
276 : break;
277 0 : default:
278 0 : tds_close_socket(tds);
279 0 : tdserror(tds_get_ctx(tds), tds, TDSEBTOK, 0);
280 0 : tdsdump_log(TDS_DBG_ERROR, "Unknown marker: %d(%x)!!\n", marker, (unsigned char) marker);
281 : return TDS_FAIL;
282 : }
283 0 : return TDS_SUCCESS;
284 : }
285 :
286 : static TDSRET
287 3640 : tds_process_loginack(TDSSOCKET *tds, TDSRET *login_succeeded)
288 : {
289 : unsigned int len;
290 : unsigned char ack;
291 : TDS_UINT product_version;
292 3640 : int memrc = 0;
293 3640 : TDS_USMALLINT orig_tds_version = tds->conn->tds_version;
294 :
295 : struct { unsigned char major, minor, tiny[2];
296 : unsigned int reported;
297 : const char *name;
298 : } ver;
299 :
300 3640 : tds->conn->tds71rev1 = 0;
301 3640 : len = tds_get_usmallint(tds);
302 3640 : if (len < 10)
303 : return TDS_FAIL;
304 3640 : ack = tds_get_byte(tds);
305 :
306 3640 : ver.major = tds_get_byte(tds);
307 3640 : ver.minor = tds_get_byte(tds);
308 3640 : ver.tiny[0] = tds_get_byte(tds);
309 3640 : ver.tiny[1] = tds_get_byte(tds);
310 3640 : ver.reported = (ver.major << 24) | (ver.minor << 16) | (ver.tiny[0] << 8) | ver.tiny[1];
311 :
312 3640 : if (ver.reported == 0x07010000)
313 0 : tds->conn->tds71rev1 = 1;
314 :
315 : /* Log reported server product name, cf. MS-TDS LOGINACK documentation. */
316 3640 : switch (ver.reported) {
317 0 : case 0x07000000:
318 0 : ver.name = "7.0";
319 0 : tds->conn->tds_version = 0x700;
320 0 : break;
321 0 : case 0x07010000:
322 0 : ver.name = "2000";
323 0 : tds->conn->tds_version = 0x701;
324 0 : break;
325 1472 : case 0x71000001:
326 1472 : ver.name = "2000 SP1";
327 1472 : tds->conn->tds_version = 0x701;
328 1472 : break;
329 0 : case 0x72090002:
330 0 : ver.name = "2005";
331 0 : tds->conn->tds_version = 0x702;
332 0 : break;
333 0 : case 0x730A0003:
334 0 : ver.name = "2008 (no NBCROW or fSparseColumnSet)";
335 0 : tds->conn->tds_version = 0x703;
336 0 : break;
337 736 : case 0x730B0003:
338 736 : ver.name = "2008";
339 736 : tds->conn->tds_version = 0x703;
340 736 : break;
341 714 : case 0x74000004:
342 714 : ver.name = "2012-2019";
343 714 : tds->conn->tds_version = 0x704;
344 714 : break;
345 : default:
346 : ver.name = "unknown";
347 : break;
348 : }
349 :
350 3640 : tdsdump_log(TDS_DBG_FUNC, "server reports TDS version %x.%x.%x.%x\n",
351 : ver.major, ver.minor, ver.tiny[0], ver.tiny[1]);
352 3640 : tdsdump_log(TDS_DBG_FUNC, "Product name for 0x%x is %s\n", ver.reported, ver.name);
353 :
354 : /* Get server product name. */
355 : /* Ignore product name length; some servers seem to set it incorrectly. */
356 3640 : tds_get_byte(tds);
357 3640 : product_version = 0;
358 : /* Compute product name length from packet length. */
359 3640 : len -= 10;
360 3640 : free(tds->conn->product_name);
361 3640 : if (ver.major >= 7u) {
362 2922 : product_version = 0x80u;
363 2922 : memrc += tds_alloc_get_string(tds, &tds->conn->product_name, len / 2);
364 718 : } else if (ver.major >= 5) {
365 718 : memrc += tds_alloc_get_string(tds, &tds->conn->product_name, len);
366 : } else {
367 0 : memrc += tds_alloc_get_string(tds, &tds->conn->product_name, len);
368 0 : if (tds->conn->product_name != NULL && strstr(tds->conn->product_name, "Microsoft") != NULL)
369 0 : product_version = 0x80u;
370 : }
371 3640 : if (memrc != 0)
372 : return TDS_FAIL;
373 :
374 3640 : product_version |= tds_get_byte(tds); product_version <<= 8;
375 3640 : product_version |= tds_get_byte(tds); product_version <<= 8;
376 3640 : product_version |= tds_get_byte(tds); product_version <<= 8;
377 3640 : product_version |= tds_get_byte(tds);
378 :
379 : /*
380 : * MSSQL 6.5 and 7.0 seem to return strange values for this
381 : * using TDS 4.2, something like 5F 06 32 FF for 6.50
382 : */
383 3640 : if (ver.major == 4 && ver.minor == 2 && (product_version & 0xff0000ffu) == 0x5f0000ffu)
384 0 : product_version = ((product_version & 0xffff00u) | 0x800000u) << 8;
385 3640 : tds->conn->product_version = product_version;
386 3640 : tdsdump_log(TDS_DBG_FUNC, "Product version %lX\n", (unsigned long) product_version);
387 :
388 : /* internal version is ignored for TDS 8.0+ */
389 3640 : if (orig_tds_version >= 0x800)
390 0 : tds->conn->tds_version = orig_tds_version;
391 :
392 : /*
393 : * TDS 5.0 reports 5 on success 6 on failure
394 : * TDS 4.2 reports 1 on success and is not
395 : * present on failure
396 : */
397 3640 : if (ack == 5 || ack == 1 || (IS_TDS50(tds->conn) && ack == 0x85)) {
398 3638 : *login_succeeded = TDS_SUCCESS;
399 : /* authentication is now useless */
400 3638 : if (tds->conn->authentication) {
401 716 : tds->conn->authentication->free(tds->conn, tds->conn->authentication);
402 716 : tds->conn->authentication = NULL;
403 : }
404 : }
405 :
406 : return TDS_SUCCESS;
407 : }
408 :
409 : /**
410 : * tds_process_login_tokens() is called after sending the login packet
411 : * to the server. It returns the success or failure of the login
412 : * dependent on the protocol version. 4.2 sends an ACK token only when
413 : * successful, TDS 5.0 sends it always with a success byte within
414 : * @tds
415 : */
416 : TDSRET
417 3660 : tds_process_login_tokens(TDSSOCKET * tds)
418 : {
419 3660 : TDSRET succeed = TDS_FAIL;
420 : int marker;
421 :
422 3660 : CHECK_TDS_EXTRA(tds);
423 :
424 3660 : tdsdump_log(TDS_DBG_FUNC, "tds_process_login_tokens()\n");
425 : do {
426 30220 : marker = tds_get_byte(tds);
427 :
428 30220 : tdsdump_log(TDS_DBG_FUNC, "looking for login token, got %x(%s)\n", marker, tds_token_name(marker));
429 :
430 30220 : switch (marker) {
431 3640 : case TDS_LOGINACK_TOKEN:
432 3640 : TDS_PROPAGATE(tds_process_loginack(tds, &succeed));
433 : break;
434 26580 : default:
435 26580 : TDS_PROPAGATE(tds_process_default_tokens(tds, marker));
436 : break;
437 : }
438 30208 : if (marker == TDS_DONE_TOKEN && IS_TDS50(tds->conn) && tds->conn->authentication) {
439 0 : TDSAUTHENTICATION *auth = tds->conn->authentication;
440 0 : if (TDS_SUCCEED(auth->handle_next(tds, auth, 0))) {
441 0 : marker = 0;
442 0 : continue;
443 : }
444 : }
445 30208 : } while (marker != TDS_DONE_TOKEN);
446 :
447 : /* set the spid */
448 3648 : if (TDS_IS_MSSQL(tds))
449 2922 : tds->conn->spid = TDS_GET_A2BE(tds->in_buf+4);
450 :
451 3652 : tdsdump_log(TDS_DBG_FUNC, "tds_process_login_tokens() returning %s\n",
452 4 : (succeed == TDS_SUCCESS)? "TDS_SUCCESS" : "TDS_FAIL");
453 :
454 3648 : return succeed;
455 : }
456 :
457 : /**
458 : * Process authentication token.
459 : * This token is only TDS 7.0+.
460 : * \tds
461 : */
462 : static TDSRET
463 716 : tds_process_auth(TDSSOCKET * tds)
464 : {
465 : unsigned int pdu_size;
466 :
467 716 : CHECK_TDS_EXTRA(tds);
468 :
469 : #if ENABLE_EXTRA_CHECKS
470 716 : if (!IS_TDS7_PLUS(tds->conn))
471 0 : tdsdump_log(TDS_DBG_ERROR, "Called auth on TDS version < 7\n");
472 : #endif
473 :
474 716 : pdu_size = tds_get_usmallint(tds);
475 716 : tdsdump_log(TDS_DBG_INFO1, "TDS_AUTH_TOKEN PDU size %u\n", pdu_size);
476 :
477 716 : if (!tds->conn->authentication)
478 : return TDS_FAIL;
479 :
480 716 : return tds->conn->authentication->handle_next(tds, tds->conn->authentication, pdu_size);
481 : }
482 :
483 : /**
484 : * process all streams.
485 : * tds_process_tokens() is called after submitting a query with
486 : * tds_submit_query() and is responsible for calling the routines to
487 : * populate tds->res_info if appropriate (some query have no result sets)
488 : * @tds
489 : * @param result_type A pointer to an integer variable which
490 : * tds_process_tokens sets to indicate the current type of result.
491 : * @par
492 : * <b>Values that indicate command status</b>
493 : * <table>
494 : * <tr><td>TDS_DONE_RESULT</td><td>The results of a command have been completely processed.
495 : * This command returned no rows.</td></tr>
496 : * <tr><td>TDS_DONEPROC_RESULT</td><td>The results of a command have been completely processed.
497 : * This command returned rows.</td></tr>
498 : * <tr><td>TDS_DONEINPROC_RESULT</td><td>The results of a command have been completely processed.
499 : * This command returned rows.</td></tr>
500 : * </table>
501 : * <b>Values that indicate results information is available</b>
502 : * <table><tr>
503 : * <td>TDS_ROWFMT_RESULT</td><td>Regular Data format information</td>
504 : * <td>tds->res_info now contains the result details ; tds->current_results now points to that data</td>
505 : * </tr><tr>
506 : * <td>TDS_COMPUTEFMT_ RESULT</td><td>Compute data format information</td>
507 : * <td>tds->comp_info now contains the result data; tds->current_results now points to that data</td>
508 : * </tr><tr>
509 : * <td>TDS_DESCRIBE_RESULT</td><td></td>
510 : * <td></td>
511 : * </tr></table>
512 : * <b>Values that indicate data is available</b>
513 : * <table><tr>
514 : * <td><b>Value</b></td><td><b>Meaning</b></td><td><b>Information returned</b></td>
515 : * </tr><tr>
516 : * <td>TDS_ROW_RESULT</td><td>Regular row results</td>
517 : * <td>1 or more rows of regular data can now be retrieved</td>
518 : * </tr><tr>
519 : * <td>TDS_COMPUTE_RESULT</td><td>Compute row results</td>
520 : * <td>A single row of compute data can now be retrieved</td>
521 : * </tr><tr>
522 : * <td>TDS_PARAM_RESULT</td><td>Return parameter results</td>
523 : * <td>param_info or cur_dyn->params contain returned parameters</td>
524 : * </tr><tr>
525 : * <td>TDS_STATUS_RESULT</td><td>Stored procedure status results</td>
526 : * <td>tds->ret_status contain the returned code</td>
527 : * </tr></table>
528 : * @param done_flags Flags contained in the TDS_DONE*_TOKEN readed
529 : * @param flag Flags to select token type to stop/return
530 : * @todo Complete TDS_DESCRIBE_RESULT description
531 : * @retval TDS_SUCCESS if a result set is available for processing.
532 : * @retval TDS_FAIL on error.
533 : * @retval TDS_NO_MORE_RESULTS if all results have been completely processed.
534 : * @retval anything returned by one of the many functions it calls. :-(
535 : */
536 : TDSRET
537 543406 : tds_process_tokens(TDSSOCKET *tds, TDS_INT *result_type, int *done_flags, unsigned flag)
538 : {
539 : int marker;
540 543406 : TDSPARAMINFO *pinfo = NULL;
541 : TDSCOLUMN *curcol;
542 : TDSRET rc;
543 543406 : TDS_INT8 saved_rows_affected = tds->rows_affected;
544 : TDS_INT ret_status;
545 543406 : int cancel_seen = 0;
546 543406 : unsigned return_flag = 0;
547 :
548 : /** \cond HIDDEN_SYMBOLS */
549 : #define SET_RETURN(ret, f) do { \
550 : *result_type = ret; \
551 : return_flag = TDS_RETURN_##f | TDS_STOPAT_##f; \
552 : if (flag & TDS_STOPAT_##f) {\
553 : tds_unget_byte(tds); \
554 : tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens::SET_RETURN stopping on current token\n"); \
555 : goto set_return_exit; \
556 : } } while(0)
557 : /** \endcond */
558 :
559 543406 : CHECK_TDS_EXTRA(tds);
560 :
561 543406 : tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens(%p, %p, %p, 0x%x)\n", tds, result_type, done_flags, flag);
562 :
563 543406 : if (tds->state == TDS_IDLE || tds->state == TDS_SENDING) {
564 57881 : tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens() state is COMPLETED\n");
565 57881 : *result_type = TDS_DONE_RESULT;
566 57881 : return TDS_NO_MORE_RESULTS;
567 : }
568 :
569 485525 : if (tds_set_state(tds, TDS_READING) != TDS_READING)
570 : return TDS_FAIL;
571 :
572 : rc = TDS_SUCCESS;
573 : for (;;) {
574 :
575 654895 : marker = tds_get_byte(tds);
576 654895 : tdsdump_log(TDS_DBG_INFO1, "processing result tokens. marker is %x(%s)\n", marker, tds_token_name(marker));
577 :
578 654895 : switch (marker) {
579 25561 : case TDS7_RESULT_TOKEN:
580 :
581 : /*
582 : * If we're processing the results of a cursor fetch
583 : * from sql server we don't want to pass back the
584 : * TDS_ROWFMT_RESULT to the calling API
585 : */
586 25561 : if (tds->current_op != TDS_OP_CURSORFETCH)
587 24737 : SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
588 :
589 25321 : rc = tds7_process_result(tds);
590 25321 : if (TDS_FAILED(rc))
591 : break;
592 : /* handle browse information (if present) */
593 25321 : marker = tds_get_byte(tds);
594 25321 : if (marker != TDS_TABNAME_TOKEN)
595 25031 : tds_unget_byte(tds);
596 : else
597 290 : rc = tds_process_tabname(tds);
598 : break;
599 58 : case TDS_RESULT_TOKEN:
600 58 : SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
601 58 : rc = tds5_process_result(tds);
602 58 : break;
603 6822 : case TDS_ROWFMT2_TOKEN:
604 6822 : SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
605 6798 : rc = tds5_process_result2(tds);
606 6798 : break;
607 0 : case TDS_COLNAME_TOKEN:
608 0 : rc = tds_process_col_name(tds);
609 0 : break;
610 0 : case TDS_COLFMT_TOKEN:
611 0 : SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
612 0 : rc = tds_process_col_fmt(tds);
613 0 : if (TDS_FAILED(rc))
614 : break;
615 : /* handle browse information (if present) */
616 0 : marker = tds_get_byte(tds);
617 0 : if (marker != TDS_TABNAME_TOKEN)
618 0 : tds_unget_byte(tds);
619 : else
620 0 : rc = tds_process_tabname(tds);
621 : break;
622 5250 : case TDS_PARAM_TOKEN:
623 5250 : tds_unget_byte(tds);
624 5250 : if (tds->current_op) {
625 4416 : tdsdump_log(TDS_DBG_FUNC, "processing parameters for op %d\n", tds->current_op);
626 15870 : while ((marker = tds_get_byte(tds)) == TDS_PARAM_TOKEN) {
627 11454 : tdsdump_log(TDS_DBG_INFO1, "calling tds_process_param_result\n");
628 11454 : rc = tds_process_param_result(tds, &pinfo);
629 11454 : if (TDS_FAILED(rc))
630 : goto set_return_exit;
631 : }
632 4416 : tds_unget_byte(tds);
633 4416 : tdsdump_log(TDS_DBG_FUNC, "%d hidden return parameters\n", pinfo ? pinfo->num_cols : -1);
634 4416 : if (pinfo && pinfo->num_cols > 0) {
635 4416 : curcol = pinfo->columns[0];
636 4416 : if (tds->current_op == TDS_OP_CURSOROPEN && tds->cur_cursor) {
637 2346 : TDSCURSOR *cursor = tds->cur_cursor;
638 :
639 2346 : cursor->cursor_id = *(TDS_INT *) curcol->column_data;
640 2346 : tdsdump_log(TDS_DBG_FUNC, "stored internal cursor id %d\n", cursor->cursor_id);
641 2346 : cursor->srv_status &= ~(TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_OPEN|TDS_CUR_ISTAT_DEALLOC);
642 2346 : cursor->srv_status |= cursor->cursor_id ? TDS_CUR_ISTAT_OPEN : TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_DEALLOC;
643 : }
644 4416 : if ((tds->current_op == TDS_OP_PREPARE || tds->current_op == TDS_OP_PREPEXEC)
645 2070 : && tds->cur_dyn && tds->cur_dyn->num_id == 0 && curcol->column_cur_size > 0) {
646 2060 : tds->cur_dyn->num_id = *(TDS_INT *) curcol->column_data;
647 : }
648 4416 : if (tds->current_op == TDS_OP_UNPREPARE)
649 0 : tds_dynamic_deallocated(tds->conn, tds->cur_dyn);
650 : }
651 4416 : tds_free_param_results(pinfo);
652 : } else {
653 834 : SET_RETURN(TDS_PARAM_RESULT, PROC);
654 834 : rc = tds_process_param_result_tokens(tds);
655 : }
656 : break;
657 18 : case TDS_COMPUTE_NAMES_TOKEN:
658 18 : rc = tds_process_compute_names(tds);
659 18 : break;
660 18 : case TDS_COMPUTE_RESULT_TOKEN:
661 18 : SET_RETURN(TDS_COMPUTEFMT_RESULT, COMPUTEFMT);
662 18 : rc = tds_process_compute_result(tds);
663 18 : break;
664 54 : case TDS7_COMPUTE_RESULT_TOKEN:
665 54 : SET_RETURN(TDS_COMPUTEFMT_RESULT, COMPUTEFMT);
666 54 : rc = tds7_process_compute_result(tds);
667 54 : break;
668 392227 : case TDS_ROW_TOKEN:
669 : case TDS_NBC_ROW_TOKEN:
670 : /* overstepped the mark... */
671 392227 : if (tds->cur_cursor) {
672 2288 : tds_set_current_results(tds, tds->cur_cursor->res_info);
673 2288 : tdsdump_log(TDS_DBG_INFO1, "tds_process_tokens(). set current_results to cursor->res_info\n");
674 : } else {
675 : /* assure that we point to row, not to compute */
676 389939 : if (tds->res_info)
677 389939 : tds_set_current_results(tds, tds->res_info);
678 : }
679 : /* I don't know when this it's false but it happened, also server can send garbage... */
680 392227 : if (tds->current_results)
681 392227 : tds->current_results->rows_exist = true;
682 392227 : SET_RETURN(TDS_ROW_RESULT, ROW);
683 :
684 363825 : switch (marker) {
685 358885 : case TDS_ROW_TOKEN:
686 358885 : rc = tds_process_row(tds);
687 358885 : break;
688 4940 : case TDS_NBC_ROW_TOKEN:
689 4940 : rc = tds_process_nbcrow(tds);
690 4940 : break;
691 : }
692 : break;
693 184 : case TDS_CMP_ROW_TOKEN:
694 : /* I don't know when this it's false but it happened, also server can send garbage... */
695 184 : if (tds->res_info)
696 184 : tds->res_info->rows_exist = true;
697 184 : SET_RETURN(TDS_COMPUTE_RESULT, COMPUTE);
698 104 : rc = tds_process_compute(tds);
699 104 : break;
700 11852 : case TDS_RETURNSTATUS_TOKEN:
701 11852 : ret_status = tds_get_int(tds);
702 11852 : marker = tds_peek(tds);
703 11852 : if (marker != TDS_PARAM_TOKEN && marker != TDS_DONEPROC_TOKEN && marker != TDS_DONE_TOKEN && marker != TDS5_PARAMFMT_TOKEN && marker != TDS5_PARAMFMT2_TOKEN)
704 : break;
705 11696 : if (tds->current_op) {
706 : /* TODO perhaps we should use ret_status ?? */
707 : } else {
708 : /* TODO optimize */
709 1798 : flag &= ~TDS_STOPAT_PROC;
710 1798 : SET_RETURN(TDS_STATUS_RESULT, PROC);
711 1798 : tds->has_status = true;
712 1798 : tds->ret_status = ret_status;
713 1798 : tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens: return status is %d\n", tds->ret_status);
714 : rc = TDS_SUCCESS;
715 : }
716 : break;
717 962 : case TDS5_DYNAMIC_TOKEN:
718 : /* process acknowledge dynamic */
719 962 : tds_set_cur_dyn(tds, tds_process_dynamic(tds));
720 : /* special case, prepared statement cannot be prepared */
721 1924 : if (!tds->cur_dyn || tds->cur_dyn->emulated)
722 : break;
723 962 : marker = tds_get_byte(tds);
724 962 : if (marker != TDS_EED_TOKEN) {
725 896 : tds_unget_byte(tds);
726 896 : break;
727 : }
728 66 : tds_process_info(tds, marker);
729 130 : if (!tds->cur_dyn || !tds->cur_dyn->emulated)
730 : break;
731 60 : marker = tds_get_byte(tds);
732 60 : if (marker != TDS_DONE_TOKEN) {
733 0 : tds_unget_byte(tds);
734 0 : break;
735 : }
736 60 : rc = tds_process_end(tds, marker, done_flags);
737 60 : if (done_flags)
738 60 : *done_flags &= ~TDS_DONE_ERROR;
739 : /* FIXME warning to macro expansion */
740 60 : SET_RETURN(TDS_DONE_RESULT, DONE);
741 : break;
742 18 : case TDS5_PARAMFMT_TOKEN:
743 18 : SET_RETURN(TDS_DESCRIBE_RESULT, PARAMFMT);
744 18 : rc = tds_process_dyn_result(tds);
745 18 : break;
746 334 : case TDS5_PARAMFMT2_TOKEN:
747 334 : SET_RETURN(TDS_DESCRIBE_RESULT, PARAMFMT);
748 334 : rc = tds5_process_dyn_result2(tds);
749 334 : break;
750 98 : case TDS5_PARAMS_TOKEN:
751 98 : SET_RETURN(TDS_PARAM_RESULT, PROC);
752 98 : rc = tds_process_params_result_token(tds);
753 98 : break;
754 40 : case TDS_CURINFO_TOKEN:
755 40 : rc = tds_process_cursor_tokens(tds);
756 40 : break;
757 139863 : case TDS_DONE_TOKEN:
758 139863 : SET_RETURN(TDS_DONE_RESULT, DONE);
759 139467 : rc = tds_process_end(tds, marker, done_flags);
760 139467 : switch (tds->current_op) {
761 272 : case TDS_OP_DYN_DEALLOC:
762 272 : if (done_flags && (*done_flags & TDS_DONE_ERROR) == 0)
763 270 : tds_dynamic_deallocated(tds->conn, tds->cur_dyn);
764 : break;
765 : default:
766 : break;
767 : }
768 : break;
769 12008 : case TDS_DONEPROC_TOKEN:
770 12008 : SET_RETURN(TDS_DONEPROC_RESULT, DONE);
771 12000 : rc = tds_process_end(tds, marker, done_flags);
772 12000 : tds->rows_affected = saved_rows_affected;
773 12000 : switch (tds->current_op) {
774 : default:
775 : break;
776 2346 : case TDS_OP_CURSOROPEN:
777 2346 : *result_type = TDS_DONE_RESULT;
778 2346 : break;
779 280 : case TDS_OP_CURSORCLOSE:
780 280 : tdsdump_log(TDS_DBG_FUNC, "TDS_OP_CURSORCLOSE\n");
781 280 : if (tds->cur_cursor) {
782 :
783 280 : TDSCURSOR *cursor = tds->cur_cursor;
784 :
785 280 : cursor->srv_status &= ~TDS_CUR_ISTAT_OPEN;
786 280 : cursor->srv_status |= TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_DECLARED;
787 280 : if (cursor->status.dealloc == TDS_CURSOR_STATE_SENT) {
788 0 : tds_cursor_deallocated(tds->conn, cursor);
789 : }
790 : }
791 280 : *result_type = TDS_NO_MORE_RESULTS;
792 280 : rc = TDS_NO_MORE_RESULTS;
793 280 : break;
794 2036 : case TDS_OP_UNPREPARE:
795 2036 : if (done_flags && (*done_flags & TDS_DONE_ERROR) == 0)
796 2036 : tds_dynamic_deallocated(tds->conn, tds->cur_dyn);
797 2036 : *result_type = TDS_NO_MORE_RESULTS;
798 2036 : rc = TDS_NO_MORE_RESULTS;
799 2036 : break;
800 1392 : case TDS_OP_CURSOR:
801 : case TDS_OP_CURSORPREPARE:
802 : case TDS_OP_CURSOREXECUTE:
803 : case TDS_OP_CURSORPREPEXEC:
804 : case TDS_OP_CURSORUNPREPARE:
805 : case TDS_OP_CURSORFETCH:
806 : case TDS_OP_CURSOROPTION:
807 : case TDS_OP_PREPEXECRPC:
808 1392 : *result_type = TDS_NO_MORE_RESULTS;
809 1392 : rc = TDS_NO_MORE_RESULTS;
810 1392 : break;
811 : }
812 : break;
813 26492 : case TDS_DONEINPROC_TOKEN:
814 26492 : switch(tds->current_op) {
815 3494 : case TDS_OP_CURSOROPEN:
816 : case TDS_OP_CURSORFETCH:
817 : case TDS_OP_PREPARE:
818 : case TDS_OP_CURSORCLOSE:
819 3494 : rc = tds_process_end(tds, marker, done_flags);
820 3494 : if (tds->rows_affected != TDS_NO_COUNT) {
821 3082 : saved_rows_affected = tds->rows_affected;
822 : }
823 : break;
824 22998 : default:
825 22998 : SET_RETURN(TDS_DONEINPROC_RESULT, DONE);
826 22958 : rc = tds_process_end(tds, marker, done_flags);
827 22958 : break;
828 : }
829 : break;
830 8593 : case TDS_ERROR_TOKEN:
831 : case TDS_INFO_TOKEN:
832 : case TDS_EED_TOKEN:
833 8593 : SET_RETURN(TDS_MSG_RESULT, MSG);
834 8525 : rc = tds_process_default_tokens(tds, marker);
835 8525 : break;
836 6747 : case TDS_ENVCHANGE_TOKEN:
837 6747 : SET_RETURN(TDS_MSG_RESULT, ENV);
838 6747 : rc = tds_process_default_tokens(tds, marker);
839 6747 : break;
840 17696 : default:
841 17696 : SET_RETURN(TDS_OTHERS_RESULT, OTHERS);
842 17660 : rc = tds_process_default_tokens(tds, marker);
843 17660 : break;
844 : }
845 :
846 655057 : set_return_exit:
847 654895 : if (TDS_FAILED(rc)) {
848 1822 : if (rc == TDS_CANCELLED)
849 1778 : tds_set_state(tds, TDS_PENDING);
850 : else
851 44 : tds_close_socket(tds);
852 : return rc;
853 : }
854 :
855 653073 : cancel_seen |= tds->in_cancel;
856 653073 : if (cancel_seen) {
857 : /* during cancel handle all tokens */
858 73407 : flag = TDS_HANDLE_ALL;
859 : }
860 :
861 653073 : if ((return_flag & flag) != 0) {
862 483001 : tds_set_state(tds, TDS_PENDING);
863 483001 : return rc;
864 : }
865 :
866 170072 : if (tds->state == TDS_IDLE || tds->state == TDS_SENDING)
867 692 : return cancel_seen ? TDS_CANCELLED : TDS_NO_MORE_RESULTS;
868 :
869 169380 : if (tds->state == TDS_DEAD) {
870 : /* TODO free all results ?? */
871 : return TDS_FAIL;
872 : }
873 : }
874 : }
875 :
876 : /**
877 : * Process results for simple query as "SET TEXTSIZE" or "USE dbname"
878 : * If the statement returns results, beware they are discarded.
879 : *
880 : * This function was written to avoid direct calls to tds_process_default_tokens
881 : * (which caused problems such as ignoring query errors).
882 : * Results are read until idle state or severe failure (do not stop for
883 : * statement failure).
884 : * @return see tds_process_tokens for results (TDS_NO_MORE_RESULTS is never returned)
885 : */
886 : TDSRET
887 7476 : tds_process_simple_query(TDSSOCKET * tds)
888 : {
889 : TDS_INT res_type;
890 : TDS_INT done_flags;
891 : TDSRET rc;
892 7476 : TDSRET ret = TDS_SUCCESS;
893 :
894 7476 : CHECK_TDS_EXTRA(tds);
895 :
896 21930 : while ((rc = tds_process_tokens(tds, &res_type, &done_flags, TDS_RETURN_DONE)) == TDS_SUCCESS) {
897 6978 : switch (res_type) {
898 :
899 6978 : case TDS_DONE_RESULT:
900 : case TDS_DONEPROC_RESULT:
901 : case TDS_DONEINPROC_RESULT:
902 6978 : if ((done_flags & TDS_DONE_ERROR) != 0)
903 18 : ret = TDS_FAIL;
904 : break;
905 :
906 : default:
907 : break;
908 : }
909 : }
910 7476 : if (TDS_FAILED(rc))
911 14 : ret = rc;
912 :
913 7476 : return ret;
914 : }
915 :
916 : /**
917 : * Holds list of names
918 : */
919 : struct namelist
920 : {
921 : /** string name */
922 : char *name;
923 : /** next element in the list */
924 : struct namelist *next;
925 : };
926 :
927 : /**
928 : * Frees list of names
929 : * \param head list head to free
930 : */
931 : static void
932 310 : tds_free_namelist(struct namelist *head)
933 : {
934 310 : struct namelist *cur = head, *prev;
935 :
936 958 : while (cur != NULL) {
937 338 : prev = cur;
938 338 : cur = cur->next;
939 338 : free(prev->name);
940 338 : free(prev);
941 : }
942 310 : }
943 :
944 : /**
945 : * Reads list of names (usually table names)
946 : * \tds
947 : * \param remainder bytes left to read
948 : * \param p_head list head to return
949 : * \param large true if name length from network are 2 byte (usually 1)
950 : */
951 : static int
952 20 : tds_read_namelist(TDSSOCKET * tds, int remainder, struct namelist **p_head, int large)
953 : {
954 20 : struct namelist *head = NULL, *cur = NULL, *prev;
955 20 : int num_names = 0;
956 :
957 : /*
958 : * this is a little messy...TDS 5.0 gives the number of columns
959 : * upfront, while in TDS 4.2, you're expected to figure it out
960 : * by the size of the message. So, I use a link list to get the
961 : * colum names and then allocate the result structure, copy
962 : * and delete the linked list
963 : */
964 64 : while (remainder > 0) {
965 : TDS_USMALLINT namelen;
966 :
967 24 : prev = cur;
968 24 : if (!(cur = tds_new(struct namelist, 1))) {
969 0 : tds_free_namelist(head);
970 0 : return -1;
971 : }
972 :
973 24 : cur->next = NULL;
974 24 : if (prev)
975 4 : prev->next = cur;
976 : else
977 : head = cur;
978 :
979 24 : if (large) {
980 0 : namelen = tds_get_usmallint(tds);
981 0 : remainder -= 2;
982 : } else {
983 24 : namelen = tds_get_byte(tds);
984 24 : --remainder;
985 : }
986 :
987 24 : if (tds_alloc_get_string(tds, &cur->name, namelen) < 0) {
988 0 : tds_free_namelist(head);
989 0 : return -1;
990 : }
991 :
992 24 : remainder -= namelen;
993 24 : if (IS_TDS7_PLUS(tds->conn))
994 0 : remainder -= namelen;
995 24 : num_names++;
996 : }
997 :
998 20 : *p_head = head;
999 20 : return num_names;
1000 : }
1001 :
1002 : /**
1003 : * tds_process_col_name() is one half of the result set under TDS 4.2
1004 : * it contains all the column names, a TDS_COLFMT_TOKEN should
1005 : * immediately follow this token with the datatype/size information
1006 : * This is a 4.2 only function
1007 : * \tds
1008 : */
1009 : static TDSRET
1010 0 : tds_process_col_name(TDSSOCKET * tds)
1011 : {
1012 : int hdrsize;
1013 0 : int col, num_names = 0;
1014 0 : struct namelist *head = NULL, *cur = NULL;
1015 : TDSCOLUMN *curcol;
1016 : TDSRESULTINFO *info;
1017 :
1018 0 : CHECK_TDS_EXTRA(tds);
1019 :
1020 0 : hdrsize = tds_get_usmallint(tds);
1021 :
1022 0 : if ((num_names = tds_read_namelist(tds, hdrsize, &head, 0)) < 0)
1023 : return TDS_FAIL;
1024 :
1025 : /* free results/computes/params etc... */
1026 0 : tds_free_all_results(tds);
1027 0 : tds->rows_affected = TDS_NO_COUNT;
1028 :
1029 0 : if ((info = tds_alloc_results(num_names)) == NULL)
1030 : goto memory_error;
1031 :
1032 0 : tds->res_info = info;
1033 0 : tds_set_current_results(tds, info);
1034 :
1035 0 : cur = head;
1036 0 : for (col = 0; col < num_names; ++col) {
1037 0 : curcol = info->columns[col];
1038 0 : if (!tds_dstr_copy(&curcol->column_name, cur->name))
1039 : goto memory_error;
1040 0 : cur = cur->next;
1041 : }
1042 0 : tds_free_namelist(head);
1043 0 : return TDS_SUCCESS;
1044 :
1045 0 : memory_error:
1046 0 : tds_free_namelist(head);
1047 0 : return TDS_FAIL;
1048 : }
1049 :
1050 : /**
1051 : * tds_process_col_fmt() is the other half of result set processing
1052 : * under TDS 4.2. It follows tds_process_col_name(). It contains all the
1053 : * column type and size information.
1054 : * This is a 4.2 only function
1055 : * \tds
1056 : */
1057 : static TDSRET
1058 0 : tds_process_col_fmt(TDSSOCKET * tds)
1059 : {
1060 : unsigned int col;
1061 : TDSCOLUMN *curcol;
1062 : TDSRESULTINFO *info;
1063 : TDS_USMALLINT flags;
1064 :
1065 0 : CHECK_TDS_EXTRA(tds);
1066 :
1067 0 : tds_get_usmallint(tds); /* hdrsize */
1068 :
1069 : /* TODO use current_results instead of res_info ?? */
1070 0 : info = tds->res_info;
1071 0 : if (!info || info->num_cols < 0)
1072 : return TDS_FAIL;
1073 0 : for (col = 0; col < info->num_cols; col++) {
1074 0 : curcol = info->columns[col];
1075 : /* In Sybase all 4 byte are used for usertype, while mssql place 2 byte as usertype and 2 byte as flags */
1076 0 : if (TDS_IS_MSSQL(tds)) {
1077 0 : curcol->column_usertype = tds_get_smallint(tds);
1078 0 : flags = tds_get_usmallint(tds);
1079 0 : curcol->column_nullable = flags & 0x01;
1080 0 : curcol->column_writeable = (flags & 0x08) > 0;
1081 0 : curcol->column_identity = (flags & 0x10) > 0;
1082 : } else {
1083 0 : curcol->column_usertype = tds_get_int(tds);
1084 : }
1085 : /* on with our regularly scheduled code (mlilback, 11/7/01) */
1086 0 : TDS_GET_COLUMN_TYPE(curcol);
1087 :
1088 0 : tdsdump_log(TDS_DBG_INFO1, "processing result. type = %d(%s), varint_size %d\n",
1089 0 : curcol->column_type, tds_prtype(curcol->column_type), curcol->column_varint_size);
1090 :
1091 0 : TDS_GET_COLUMN_INFO(tds, curcol);
1092 :
1093 : /* Adjust column size according to client's encoding */
1094 0 : curcol->on_server.column_size = curcol->column_size;
1095 0 : adjust_character_column_size(tds, curcol);
1096 : }
1097 :
1098 0 : return tds_alloc_row(info);
1099 : }
1100 :
1101 : /**
1102 : * Reads table names for TDS 7.1+.
1103 : * TDS 7.1+ return table names as an array of names
1104 : * (so database.schema.owner.name as separate names)
1105 : * \tds
1106 : * \param remainder bytes left to read
1107 : * \param p_head pointer to list head to return
1108 : * \return number of element returned or -1 on error
1109 : */
1110 : static int
1111 290 : tds71_read_table_names(TDSSOCKET *tds, int remainder, struct namelist **p_head)
1112 : {
1113 290 : struct namelist *head = NULL, *cur = NULL, *prev;
1114 290 : int num_names = 0;
1115 :
1116 : /*
1117 : * this is a little messy...TDS 5.0 gives the number of columns
1118 : * upfront, while in TDS 4.2, you're expected to figure it out
1119 : * by the size of the message. So, I use a link list to get the
1120 : * colum names and then allocate the result structure, copy
1121 : * and delete the linked list
1122 : */
1123 894 : while (remainder > 0) {
1124 : int elements, i;
1125 : size_t len;
1126 : char *partials[4], *p;
1127 :
1128 314 : prev = cur;
1129 314 : if (!(cur = tds_new(struct namelist, 1))) {
1130 0 : tds_free_namelist(head);
1131 0 : return -1;
1132 : }
1133 :
1134 314 : cur->name = NULL;
1135 314 : cur->next = NULL;
1136 314 : if (prev)
1137 24 : prev->next = cur;
1138 : else
1139 : head = cur;
1140 :
1141 314 : elements = tds_get_byte(tds);
1142 314 : --remainder;
1143 314 : if (elements <= 0 || elements > 4) {
1144 0 : tds_free_namelist(head);
1145 0 : return -1;
1146 : }
1147 :
1148 : /* read partials IDs and compute full length */
1149 : len = 0;
1150 314 : for (i = 0; i < elements; ++i) {
1151 314 : TDS_USMALLINT namelen = tds_get_usmallint(tds);
1152 314 : remainder -= 2 + 2 * namelen;
1153 314 : if (tds_alloc_get_string(tds, &partials[i], namelen) < 0) {
1154 0 : while (i > 0)
1155 0 : free(partials[--i]);
1156 0 : tds_free_namelist(head);
1157 0 : return -1;
1158 : }
1159 314 : len += tds_quote_id(tds, NULL, partials[i], -1) + 1;
1160 : }
1161 :
1162 : /* allocate full name */
1163 314 : p = tds_new(char, len);
1164 314 : if (!p) {
1165 : i = elements;
1166 0 : while (i > 0)
1167 0 : free(partials[--i]);
1168 0 : tds_free_namelist(head);
1169 0 : return -1;
1170 : }
1171 :
1172 : /* compose names */
1173 314 : cur->name = p;
1174 628 : for (i = 0; i < elements; ++i) {
1175 314 : p += tds_quote_id(tds, p, partials[i], -1);
1176 314 : *p++ = '.';
1177 314 : free(partials[i]);
1178 : }
1179 314 : *--p = 0;
1180 :
1181 314 : num_names++;
1182 : }
1183 :
1184 290 : *p_head = head;
1185 290 : return num_names;
1186 : }
1187 :
1188 : /**
1189 : * Process list of table from network.
1190 : * This token is only TDS 4.2
1191 : * \tds
1192 : */
1193 : static TDSRET
1194 292 : tds_process_tabname(TDSSOCKET *tds)
1195 : {
1196 : struct namelist *head, *cur;
1197 : int num_names, hdrsize, i;
1198 : char **names;
1199 : unsigned char marker;
1200 : TDSRET rc;
1201 :
1202 292 : hdrsize = tds_get_usmallint(tds);
1203 :
1204 : /* different structure for tds7.1 */
1205 : /* hdrsize check is required for tds7.1 revision 1 (mssql without SPs) */
1206 : /* TODO change tds_version ?? */
1207 292 : if (IS_TDS71_PLUS(tds->conn) && (!IS_TDS71(tds->conn) || !tds->conn->tds71rev1))
1208 290 : num_names = tds71_read_table_names(tds, hdrsize, &head);
1209 : else
1210 2 : num_names = tds_read_namelist(tds, hdrsize, &head, IS_TDS7_PLUS(tds->conn));
1211 292 : if (num_names <= 0)
1212 : return TDS_FAIL;
1213 :
1214 : /* put in an array */
1215 292 : names = tds_new(char*, num_names);
1216 292 : if (!names) {
1217 0 : tds_free_namelist(head);
1218 0 : return TDS_FAIL;
1219 : }
1220 612 : for (cur = head, i = 0; i < num_names; ++i, cur = cur->next)
1221 320 : names[i] = cur->name;
1222 :
1223 292 : rc = TDS_SUCCESS;
1224 292 : marker = tds_get_byte(tds);
1225 292 : if (marker != TDS_COLINFO_TOKEN)
1226 0 : tds_unget_byte(tds);
1227 : else
1228 292 : rc = tds_process_colinfo(tds, names, num_names);
1229 :
1230 292 : free(names);
1231 292 : tds_free_namelist(head);
1232 292 : return rc;
1233 : }
1234 :
1235 : /**
1236 : * Reads column information.
1237 : * This token is only TDS 4.2
1238 : * \tds
1239 : * \param[in] names table names
1240 : * \param[in] num_names number of table names
1241 : */
1242 : static TDSRET
1243 298 : tds_process_colinfo(TDSSOCKET * tds, char **names, int num_names)
1244 : {
1245 : unsigned int hdrsize, l;
1246 : TDSCOLUMN *curcol;
1247 : TDSRESULTINFO *info;
1248 298 : unsigned int bytes_read = 0;
1249 : struct {
1250 : unsigned char num_col;
1251 : unsigned char num_table;
1252 : unsigned char flags;
1253 : } col_info;
1254 :
1255 298 : CHECK_TDS_EXTRA(tds);
1256 :
1257 298 : hdrsize = tds_get_usmallint(tds);
1258 :
1259 298 : info = tds->current_results;
1260 :
1261 1722 : while (bytes_read < hdrsize) {
1262 :
1263 1126 : tds_get_n(tds, &col_info, 3);
1264 1126 : bytes_read += 3;
1265 :
1266 1126 : curcol = NULL;
1267 1126 : if (info && col_info.num_col > 0 && col_info.num_col <= info->num_cols)
1268 1126 : curcol = info->columns[col_info.num_col - 1];
1269 :
1270 1126 : if (curcol) {
1271 1126 : curcol->column_writeable = (col_info.flags & 0x4) == 0;
1272 1126 : curcol->column_key = (col_info.flags & 0x8) > 0;
1273 1126 : curcol->column_hidden = (col_info.flags & 0x10) > 0;
1274 :
1275 1126 : if (names && col_info.num_table > 0 && col_info.num_table <= num_names)
1276 814 : if (!tds_dstr_copy(&curcol->table_name, names[col_info.num_table - 1]))
1277 : return TDS_FAIL;
1278 : }
1279 : /* read real column name */
1280 1126 : if (col_info.flags & 0x20) {
1281 36 : l = tds_get_byte(tds);
1282 36 : if (curcol) {
1283 36 : tds_dstr_get(tds, &curcol->table_column_name, l);
1284 36 : if (IS_TDS7_PLUS(tds->conn))
1285 32 : l *= 2;
1286 : } else {
1287 0 : if (IS_TDS7_PLUS(tds->conn))
1288 0 : l *= 2;
1289 : /* discard silently */
1290 0 : tds_get_n(tds, NULL, l);
1291 : }
1292 36 : bytes_read += l + 1;
1293 : }
1294 : }
1295 :
1296 : return TDS_SUCCESS;
1297 : }
1298 :
1299 : /**
1300 : * process output parameters of a stored
1301 : * procedure. This differs from regular row/compute results in that there
1302 : * is no total number of parameters given, they just show up singly.
1303 : * \tds
1304 : * \param[out] pinfo output parameter.
1305 : * Should point to a not allocated structure
1306 : */
1307 : static TDSRET
1308 12544 : tds_process_param_result(TDSSOCKET * tds, TDSPARAMINFO ** pinfo)
1309 : {
1310 : TDSCOLUMN *curparam;
1311 : TDSPARAMINFO *info;
1312 : TDSRET token;
1313 :
1314 12544 : tdsdump_log(TDS_DBG_FUNC, "tds_process_param_result(%p, %p)\n", tds, pinfo);
1315 :
1316 12544 : CHECK_TDS_EXTRA(tds);
1317 12544 : if (*pinfo)
1318 7294 : CHECK_PARAMINFO_EXTRA(*pinfo);
1319 :
1320 : /* TODO check if current_results is a param result */
1321 :
1322 : /* limited to 64K but possible types are always smaller (not TEXT/IMAGE) */
1323 12544 : tds_get_smallint(tds); /* header size */
1324 12544 : if ((info = tds_alloc_param_result(*pinfo)) == NULL)
1325 : return TDS_FAIL;
1326 :
1327 12544 : *pinfo = info;
1328 12544 : curparam = info->columns[info->num_cols - 1];
1329 :
1330 : /*
1331 : * FIXME check support for tds7+ (seem to use same format of tds5 for data...)
1332 : * perhaps varint_size can be 2 or collation can be specified ??
1333 : */
1334 12544 : TDS_PROPAGATE(tds_get_data_info(tds, curparam, 1));
1335 :
1336 12544 : curparam->column_cur_size = curparam->column_size; /* needed ?? */
1337 :
1338 12544 : if (tds_alloc_param_data(curparam) == NULL)
1339 : return TDS_FAIL;
1340 :
1341 12544 : token = curparam->funcs->get_data(tds, curparam);
1342 12544 : if (TDS_UNLIKELY(tds_write_dump))
1343 0 : tdsdump_col(curparam);
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 25088 : if (!tds_dstr_isempty(&curparam->column_name) && tds_dstr_cstr(&curparam->column_name)[0] != '@')
1351 18 : 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 834 : tds_process_param_result_tokens(TDSSOCKET * tds)
1365 : {
1366 : int marker;
1367 : TDSPARAMINFO **pinfo;
1368 :
1369 834 : CHECK_TDS_EXTRA(tds);
1370 :
1371 834 : if (tds->cur_dyn)
1372 0 : pinfo = &(tds->cur_dyn->res_info);
1373 : else
1374 834 : pinfo = &(tds->param_info);
1375 :
1376 1924 : while ((marker = tds_get_byte(tds)) == TDS_PARAM_TOKEN) {
1377 1090 : TDS_PROPAGATE(tds_process_param_result(tds, pinfo));
1378 : }
1379 834 : 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 834 : tds_set_current_results(tds, *pinfo);
1385 834 : tds_unget_byte(tds);
1386 834 : return TDS_SUCCESS;
1387 : }
1388 :
1389 : /**
1390 : * tds_process_params_result_token() processes params on TDS5.
1391 : * \tds
1392 : */
1393 : static TDSRET
1394 100 : tds_process_params_result_token(TDSSOCKET * tds)
1395 : {
1396 : unsigned int i;
1397 : TDSPARAMINFO *info;
1398 :
1399 100 : CHECK_TDS_EXTRA(tds);
1400 :
1401 : /* TODO check if current_results is a param result */
1402 100 : info = tds->current_results;
1403 100 : if (!info)
1404 : return TDS_FAIL;
1405 :
1406 158 : for (i = 0; i < info->num_cols; i++) {
1407 158 : TDSCOLUMN *curcol = info->columns[i];
1408 158 : 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 43907 : tds7_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol)
1521 : {
1522 43907 : CHECK_TDS_EXTRA(tds);
1523 43907 : CHECK_COLUMN_EXTRA(curcol);
1524 :
1525 : /* User defined data type of the column */
1526 43907 : curcol->column_usertype = IS_TDS72_PLUS(tds->conn) ? tds_get_int(tds) : tds_get_smallint(tds);
1527 :
1528 43907 : curcol->column_flags = tds_get_smallint(tds); /* Flags */
1529 :
1530 43907 : curcol->column_nullable = curcol->column_flags & 0x01;
1531 43907 : curcol->column_writeable = (curcol->column_flags & 0x08) > 0;
1532 43907 : curcol->column_identity = (curcol->column_flags & 0x10) > 0;
1533 43907 : curcol->column_computed = (curcol->column_flags & 0x20) > 0;
1534 :
1535 87814 : TDS_GET_COLUMN_TYPE(curcol); /* sets "cardinal" type */
1536 :
1537 43907 : curcol->column_timestamp = (curcol->column_type == SYBBINARY && curcol->column_usertype == TDS_UT_TIMESTAMP);
1538 :
1539 43907 : TDS_GET_COLUMN_INFO(tds, curcol);
1540 :
1541 : /* Adjust column size according to client's encoding */
1542 43907 : curcol->on_server.column_size = curcol->column_size;
1543 :
1544 : /* NOTE adjustements must be done after curcol->char_conv initialization */
1545 43907 : 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 43907 : tds_dstr_get(tds, &curcol->column_name, tds_get_byte(tds));
1552 :
1553 43927 : 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 4 : tds_dstr_cstr(&curcol->column_name),
1560 4 : curcol->column_type, tds_prtype(curcol->column_type),
1561 4 : curcol->on_server.column_type, tds_prtype(curcol->on_server.column_type),
1562 4 : curcol->column_varint_size,
1563 : curcol->column_size, curcol->on_server.column_size);
1564 :
1565 43907 : CHECK_COLUMN_EXTRA(curcol);
1566 :
1567 43907 : 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 25321 : tds7_process_result(TDSSOCKET * tds)
1578 : {
1579 : int col, num_cols;
1580 : TDSRET result;
1581 : TDSRESULTINFO *info;
1582 :
1583 25321 : CHECK_TDS_EXTRA(tds);
1584 25321 : tdsdump_log(TDS_DBG_INFO1, "processing TDS7 result metadata.\n");
1585 :
1586 : /* read number of columns and allocate the columns structure */
1587 :
1588 25321 : num_cols = tds_get_smallint(tds);
1589 :
1590 : /* This can be a DUMMY results token from a cursor fetch */
1591 :
1592 25321 : if (num_cols < 0) {
1593 824 : tdsdump_log(TDS_DBG_INFO1, "no meta data\n");
1594 : return TDS_SUCCESS;
1595 : }
1596 :
1597 24497 : tds_free_all_results(tds);
1598 24497 : tds->rows_affected = TDS_NO_COUNT;
1599 :
1600 24497 : if ((info = tds_alloc_results(num_cols)) == NULL)
1601 : return TDS_FAIL;
1602 24497 : tds_set_current_results(tds, info);
1603 24497 : 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 24193 : tds->res_info = info;
1609 24193 : 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 24497 : tdsdump_log(TDS_DBG_INFO1, "setting up %d columns\n", num_cols);
1617 43853 : for (col = 0; col < num_cols; col++) {
1618 43853 : TDSCOLUMN *curcol = info->columns[col];
1619 :
1620 43853 : TDS_PROPAGATE(tds7_get_data_info(tds, curcol));
1621 : }
1622 :
1623 24497 : if (num_cols > 0) {
1624 : static const char dashes[31] = "------------------------------";
1625 24497 : tdsdump_log(TDS_DBG_INFO1, " %-20s %-15s %-15s %-7s\n", "name", "size/wsize", "type/wtype", "utype");
1626 24497 : tdsdump_log(TDS_DBG_INFO1, " %-20s %15s %15s %7s\n", dashes+10, dashes+30-15, dashes+30-15, dashes+30-7);
1627 : }
1628 43853 : for (col = 0; col < num_cols; col++) {
1629 43853 : TDSCOLUMN *curcol = info->columns[col];
1630 :
1631 43865 : tdsdump_log(TDS_DBG_INFO1, " %-20s %7d/%-7d %7d/%-7d %7d\n",
1632 4 : tds_dstr_cstr(&curcol->column_name),
1633 : curcol->column_size, curcol->on_server.column_size,
1634 4 : 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 24497 : result = tds_alloc_row(info);
1640 24497 : CHECK_TDS_EXTRA(tds);
1641 24497 : 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 13268 : tds_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int is_param)
1653 : {
1654 13268 : CHECK_TDS_EXTRA(tds);
1655 13268 : CHECK_COLUMN_EXTRA(curcol);
1656 :
1657 13268 : tdsdump_log(TDS_DBG_INFO1, "tds_get_data_info(%p, %p, %d) %s\n", tds, curcol, is_param, is_param? "[for parameter]" : "");
1658 :
1659 13268 : tds_dstr_get(tds, &curcol->column_name, tds_get_byte(tds));
1660 :
1661 13268 : curcol->column_flags = tds_get_byte(tds); /* Flags */
1662 13268 : if (!is_param) {
1663 : /* TODO check if all flags are the same for all TDS versions */
1664 696 : if (IS_TDS50(tds->conn))
1665 696 : curcol->column_hidden = curcol->column_flags & 0x1;
1666 696 : curcol->column_key = (curcol->column_flags & 0x2) > 1;
1667 696 : curcol->column_writeable = (curcol->column_flags & 0x10) > 1;
1668 696 : curcol->column_nullable = (curcol->column_flags & 0x20) > 1;
1669 696 : 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 13268 : if (IS_TDS72_PLUS(tds->conn)) {
1703 6328 : 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 13268 : curcol->column_usertype = tds_get_int(tds);
1717 26536 : TDS_GET_COLUMN_TYPE(curcol);
1718 :
1719 13268 : tdsdump_log(TDS_DBG_INFO1, "processing result. type = %d(%s), varint_size %d\n",
1720 0 : curcol->column_type, tds_prtype(curcol->column_type), curcol->column_varint_size);
1721 :
1722 13268 : TDS_GET_COLUMN_INFO(tds, curcol);
1723 :
1724 13268 : 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 13268 : curcol->on_server.column_size = curcol->column_size;
1728 13268 : adjust_character_column_size(tds, curcol);
1729 :
1730 13268 : 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 58 : tds5_process_result(TDSSOCKET * tds)
1741 : {
1742 : unsigned int col, num_cols;
1743 : TDSCOLUMN *curcol;
1744 : TDSRESULTINFO *info;
1745 :
1746 58 : CHECK_TDS_EXTRA(tds);
1747 :
1748 58 : tds_free_all_results(tds);
1749 58 : tds->rows_affected = TDS_NO_COUNT;
1750 :
1751 58 : tds_get_usmallint(tds); /* header size */
1752 :
1753 : /* read number of columns and allocate the columns structure */
1754 58 : num_cols = tds_get_usmallint(tds);
1755 :
1756 58 : if ((info = tds_alloc_results(num_cols)) == NULL)
1757 : return TDS_FAIL;
1758 58 : tds_set_current_results(tds, info);
1759 58 : if (tds->cur_cursor)
1760 0 : tds->cur_cursor->res_info = info;
1761 : else
1762 58 : tds->res_info = info;
1763 :
1764 : /*
1765 : * loop through the columns populating COLINFO struct from
1766 : * server response
1767 : */
1768 696 : for (col = 0; col < info->num_cols; col++) {
1769 696 : curcol = info->columns[col];
1770 :
1771 696 : 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 696 : tds_get_n(tds, NULL, tds_get_byte(tds));
1776 : }
1777 58 : 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 6798 : tds5_process_result2(TDSSOCKET * tds)
1788 : {
1789 : unsigned int colnamelen;
1790 : TDS_USMALLINT col, num_cols;
1791 : TDSCOLUMN *curcol;
1792 : TDSRESULTINFO *info;
1793 :
1794 6798 : CHECK_TDS_EXTRA(tds);
1795 :
1796 6798 : tdsdump_log(TDS_DBG_INFO1, "tds5_process_result2\n");
1797 :
1798 : /*
1799 : * free previous resultset
1800 : */
1801 6798 : tds_free_all_results(tds);
1802 6798 : tds->rows_affected = TDS_NO_COUNT;
1803 :
1804 : /*
1805 : * read length of packet (4 bytes)
1806 : */
1807 6798 : tds_get_uint(tds);
1808 :
1809 : /* read number of columns and allocate the columns structure */
1810 6798 : num_cols = tds_get_usmallint(tds);
1811 :
1812 6798 : if ((info = tds_alloc_results(num_cols)) == NULL)
1813 : return TDS_FAIL;
1814 6798 : tds_set_current_results(tds, info);
1815 6798 : if (tds->cur_cursor)
1816 8 : tds->cur_cursor->res_info = info;
1817 : else
1818 6790 : tds->res_info = info;
1819 :
1820 6798 : 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 11488 : for (col = 0; col < info->num_cols; col++) {
1828 11488 : curcol = info->columns[col];
1829 :
1830 : /* label */
1831 11488 : tds_dstr_get(tds, &curcol->column_name, tds_get_byte(tds));
1832 :
1833 : /* TODO save information somewhere */
1834 : /* database */
1835 11488 : colnamelen = tds_get_byte(tds);
1836 11488 : 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 11488 : colnamelen = tds_get_byte(tds);
1844 11488 : 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 11488 : tds_dstr_get(tds, &curcol->table_name, tds_get_byte(tds));
1853 :
1854 : /* table column name */
1855 11488 : tds_dstr_get(tds, &curcol->table_column_name, tds_get_byte(tds));
1856 :
1857 : /* if label is empty, use the table column name */
1858 22976 : if (tds_dstr_isempty(&curcol->column_name))
1859 8472 : if (!tds_dstr_dup(&curcol->column_name, &curcol->table_column_name))
1860 : return TDS_FAIL;
1861 :
1862 : /* flags (4 bytes) */
1863 11488 : curcol->column_flags = tds_get_int(tds);
1864 11488 : curcol->column_hidden = curcol->column_flags & 0x1;
1865 11488 : curcol->column_key = (curcol->column_flags & 0x2) > 1;
1866 11488 : curcol->column_writeable = (curcol->column_flags & 0x10) > 1;
1867 11488 : curcol->column_nullable = (curcol->column_flags & 0x20) > 1;
1868 11488 : curcol->column_identity = (curcol->column_flags & 0x40) > 1;
1869 :
1870 11488 : curcol->column_usertype = tds_get_int(tds);
1871 :
1872 22976 : TDS_GET_COLUMN_TYPE(curcol);
1873 :
1874 11488 : TDS_GET_COLUMN_INFO(tds, curcol);
1875 :
1876 : /* Adjust column size according to client's encoding */
1877 11488 : curcol->on_server.column_size = curcol->column_size;
1878 11488 : adjust_character_column_size(tds, curcol);
1879 :
1880 : /* discard Locale */
1881 11488 : tds_get_n(tds, NULL, tds_get_byte(tds));
1882 :
1883 : /*
1884 : * Dump all information on this column
1885 : */
1886 11488 : tdsdump_log(TDS_DBG_INFO1, "col %d:\n", col);
1887 11488 : 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 11488 : tdsdump_log(TDS_DBG_INFO1, "\tflags=%x utype=%d type=%d server type %d varint=%d\n",
1894 0 : curcol->column_flags, curcol->column_usertype, curcol->column_type, curcol->on_server.column_type,
1895 0 : curcol->column_varint_size);
1896 :
1897 11488 : tdsdump_log(TDS_DBG_INFO1, "\tcolsize=%d prec=%d scale=%d\n",
1898 0 : curcol->column_size, curcol->column_prec, curcol->column_scale);
1899 : }
1900 6798 : 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 358885 : tds_process_row(TDSSOCKET * tds)
1949 : {
1950 : unsigned int i;
1951 : TDSCOLUMN *curcol;
1952 : TDSRESULTINFO *info;
1953 :
1954 358885 : CHECK_TDS_EXTRA(tds);
1955 :
1956 358885 : info = tds->current_results;
1957 358885 : if (!info || info->num_cols <= 0)
1958 : return TDS_FAIL;
1959 :
1960 1150347 : for (i = 0; i < info->num_cols; i++) {
1961 1150347 : tdsdump_log(TDS_DBG_INFO1, "tds_process_row(): reading column %d \n", i);
1962 1150347 : curcol = info->columns[i];
1963 1150347 : 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 4940 : tds_process_nbcrow(TDSSOCKET * tds)
1973 : {
1974 : unsigned int i;
1975 : TDSCOLUMN *curcol;
1976 : TDSRESULTINFO *info;
1977 : char *nbcbuf;
1978 :
1979 4940 : CHECK_TDS_EXTRA(tds);
1980 :
1981 4940 : info = tds->current_results;
1982 4940 : if (!info || info->num_cols <= 0)
1983 : return TDS_FAIL;
1984 :
1985 4940 : nbcbuf = (char *) alloca((info->num_cols + 7) / 8);
1986 4940 : tds_get_n(tds, nbcbuf, (info->num_cols + 7) / 8);
1987 41748 : for (i = 0; i < info->num_cols; i++) {
1988 36808 : curcol = info->columns[i];
1989 36808 : tdsdump_log(TDS_DBG_INFO1, "tds_process_nbcrow(): reading column %d \n", i);
1990 36808 : if (nbcbuf[i / 8] & (1 << (i % 8))) {
1991 9828 : curcol->column_cur_size = -1;
1992 : } else {
1993 26980 : TDS_PROPAGATE(curcol->funcs->get_data(tds, curcol));
1994 : }
1995 : }
1996 : return TDS_SUCCESS;
1997 : }
1998 :
1999 : static TDSRET
2000 714 : tds_process_featureextack(TDSSOCKET * tds)
2001 : {
2002 714 : CHECK_TDS_EXTRA(tds);
2003 :
2004 : /* TODO do something with it */
2005 714 : for (;;) {
2006 : TDS_UINT data_len;
2007 : TDS_TINYINT feature_id;
2008 :
2009 1428 : feature_id = tds_get_byte(tds);
2010 1428 : if (feature_id == 0xff)
2011 : break;
2012 :
2013 714 : data_len = tds_get_uint(tds);
2014 714 : tds_get_n(tds, NULL, data_len);
2015 : }
2016 714 : 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 181627 : 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 181627 : CHECK_TDS_EXTRA(tds);
2095 :
2096 181627 : status = tds_get_usmallint(tds);
2097 :
2098 181627 : tds_get_smallint(tds); /* state/command */
2099 :
2100 181627 : more_results = (status & TDS_DONE_MORE_RESULTS) != 0;
2101 181627 : was_cancelled = (status & TDS_DONE_CANCELLED) != 0;
2102 181627 : error = (status & TDS_DONE_ERROR) != 0;
2103 181627 : done_count_valid = (status & TDS_DONE_COUNT) != 0;
2104 :
2105 :
2106 181627 : 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 181627 : tds->in_row = false;
2112 :
2113 181627 : if (tds->res_info) {
2114 38242 : tds->res_info->more_results = more_results;
2115 : /* FIXME this should not happen !!! */
2116 38242 : if (tds->current_results == NULL)
2117 0 : tds_set_current_results(tds, tds->res_info);
2118 :
2119 : }
2120 :
2121 181627 : if (flags_parm)
2122 156955 : *flags_parm = status;
2123 :
2124 181627 : rows_affected = IS_TDS72_PLUS(tds->conn) ? tds_get_int8(tds) : tds_get_int(tds);
2125 181627 : tdsdump_log(TDS_DBG_FUNC, " rows_affected = %" PRId64 "\n", rows_affected);
2126 :
2127 181627 : if (was_cancelled || (!more_results && !tds->in_cancel)) {
2128 79441 : 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 79441 : tds->in_cancel = 0;
2131 79441 : if (tds->bulk_query) {
2132 400 : tds->out_flag = TDS_BULK;
2133 400 : tds_set_state(tds, TDS_SENDING);
2134 400 : tds->bulk_query = false;
2135 : } else {
2136 79041 : tds_set_state(tds, TDS_IDLE);
2137 79041 : if (tds->conn->pending_close)
2138 8 : tds_process_pending_closes(tds);
2139 : }
2140 : }
2141 :
2142 181627 : 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 181627 : if (done_count_valid)
2151 51895 : tds->rows_affected = rows_affected;
2152 : else
2153 129732 : tds->rows_affected = TDS_NO_COUNT;
2154 :
2155 : if (IS_TDSDEAD(tds))
2156 : return TDS_FAIL;
2157 :
2158 181627 : 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 20409 : tds_process_env_chg(TDSSOCKET * tds)
2202 : {
2203 : unsigned int size;
2204 : TDS_TINYINT type;
2205 20409 : char *oldval = NULL;
2206 20409 : char *newval = NULL;
2207 : char **dest;
2208 : int new_block_size;
2209 20409 : int memrc = 0;
2210 :
2211 20409 : CHECK_TDS_EXTRA(tds);
2212 :
2213 20409 : size = tds_get_usmallint(tds);
2214 20409 : 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 20409 : 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 20409 : if (type == TDS_ENV_SQLCOLLATION) {
2234 : /* save new collation */
2235 5522 : size = tds_get_byte(tds);
2236 5522 : tdsdump_log(TDS_DBG_ERROR, "tds_process_env_chg(): %d bytes of collation data received\n", size);
2237 5522 : tdsdump_dump_buf(TDS_DBG_NETWORK, "tds->conn->collation was", tds->conn->collation, 5);
2238 5522 : memset(tds->conn->collation, 0, 5);
2239 5522 : if (size < 5) {
2240 0 : tds_get_n(tds, tds->conn->collation, size);
2241 : } else {
2242 5522 : tds_get_n(tds, tds->conn->collation, 5);
2243 5522 : tds_get_n(tds, NULL, size - 5);
2244 5522 : tds7_srv_charset_changed(tds->conn, tds->conn->collation);
2245 : }
2246 5522 : tdsdump_dump_buf(TDS_DBG_NETWORK, "tds->conn->collation now", tds->conn->collation, 5);
2247 : /* discard old one */
2248 5522 : tds_get_n(tds, NULL, tds_get_byte(tds));
2249 5522 : return TDS_SUCCESS;
2250 : }
2251 :
2252 14887 : 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 14543 : 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 14233 : 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 14233 : 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 14233 : memrc += tds_alloc_get_string(tds, &newval, tds_get_byte(tds));
2280 :
2281 : /* fetch the old value */
2282 14233 : memrc += tds_alloc_get_string(tds, &oldval, tds_get_byte(tds));
2283 :
2284 14233 : if (memrc != 0) {
2285 0 : free(newval);
2286 0 : free(oldval);
2287 0 : return TDS_FAIL;
2288 : }
2289 :
2290 14233 : dest = NULL;
2291 14233 : switch (type) {
2292 3638 : case TDS_ENV_PACKSIZE:
2293 7276 : new_block_size = atoi(newval);
2294 3638 : if (new_block_size >= 512) {
2295 3638 : 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 3638 : tds_realloc_socket(tds, new_block_size);
2302 : }
2303 : break;
2304 7131 : case TDS_ENV_DATABASE:
2305 7131 : dest = &tds->conn->env.database;
2306 7131 : break;
2307 2922 : case TDS_ENV_LANG:
2308 2922 : dest = &tds->conn->env.language;
2309 2922 : break;
2310 542 : case TDS_ENV_CHARSET:
2311 542 : tdsdump_log(TDS_DBG_FUNC, "server indicated charset change to \"%s\"\n", newval);
2312 542 : dest = &tds->conn->env.charset;
2313 542 : tds_srv_charset_changed(tds->conn, newval);
2314 542 : break;
2315 : }
2316 14233 : if (tds->env_chg_func) {
2317 7905 : (*(tds->env_chg_func)) (tds, type, oldval, newval);
2318 : }
2319 :
2320 14233 : free(oldval);
2321 14233 : if (newval) {
2322 14233 : if (dest) {
2323 10595 : free(*dest);
2324 10595 : *dest = newval;
2325 : } else
2326 3638 : 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 15703 : tds_process_info(TDSSOCKET * tds, int marker)
2339 : {
2340 : int rc;
2341 : unsigned int len_sqlstate;
2342 15703 : int has_eed = 0;
2343 : TDSMESSAGE msg;
2344 15703 : unsigned int packet_len, char_len, readed_len = 10;
2345 :
2346 15703 : CHECK_TDS_EXTRA(tds);
2347 :
2348 15703 : if (!tds->in_row)
2349 15421 : tds_free_all_results(tds);
2350 :
2351 : /* make sure message has been freed */
2352 15703 : memset(&msg, 0, sizeof(TDSMESSAGE));
2353 :
2354 : /* packet length */
2355 15703 : packet_len = tds_get_usmallint(tds);
2356 :
2357 : /* message number */
2358 15703 : msg.msgno = tds_get_int(tds);
2359 :
2360 : /* msg state */
2361 15703 : msg.state = tds_get_byte(tds);
2362 :
2363 : /* msg level */
2364 15703 : msg.severity = tds_get_byte(tds);
2365 :
2366 : /* determine if msg or error */
2367 15703 : switch (marker) {
2368 2467 : case TDS_EED_TOKEN:
2369 2467 : if (msg.severity <= 10)
2370 2243 : msg.priv_msg_type = 0;
2371 : else
2372 224 : msg.priv_msg_type = 1;
2373 :
2374 : /* read SQL state */
2375 2467 : len_sqlstate = tds_get_byte(tds);
2376 2467 : msg.sql_state = tds_new(char, len_sqlstate + 1);
2377 2467 : if (!msg.sql_state) {
2378 0 : tds_free_msg(&msg);
2379 0 : return TDS_FAIL;
2380 : }
2381 :
2382 2467 : tds_get_n(tds, msg.sql_state, len_sqlstate);
2383 2467 : msg.sql_state[len_sqlstate] = '\0';
2384 :
2385 : /* do a better mapping using native errors */
2386 2467 : if (strcmp(msg.sql_state, "ZZZZZ") == 0)
2387 2364 : TDS_ZERO_FREE(msg.sql_state);
2388 :
2389 : /* if has_eed = 1, extended error data follows */
2390 2467 : has_eed = tds_get_byte(tds);
2391 :
2392 : /* junk status and transaction state */
2393 2467 : tds_get_smallint(tds);
2394 2467 : readed_len += 4 + len_sqlstate;
2395 2467 : break;
2396 11838 : case TDS_INFO_TOKEN:
2397 11838 : msg.priv_msg_type = 0;
2398 11838 : break;
2399 1398 : case TDS_ERROR_TOKEN:
2400 1398 : msg.priv_msg_type = 1;
2401 1398 : 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 15703 : 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 15703 : char_len = 0;
2417 15703 : rc = 0;
2418 :
2419 : /* the message */
2420 15703 : GET_STRING(&msg.message, usmallint);
2421 :
2422 : /* server name */
2423 15703 : GET_STRING(&msg.server, byte);
2424 :
2425 15703 : 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 15703 : GET_STRING(&msg.proc_name, byte);
2435 :
2436 15703 : 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 22813 : if (tds->conn->product_version == 0 ?
2441 7110 : IS_TDS7_PLUS(tds->conn) && readed_len + 4 <= packet_len :
2442 : IS_TDS72_PLUS(tds->conn)) {
2443 6714 : msg.line_number = tds_get_int(tds);
2444 6714 : readed_len += 4;
2445 : } else {
2446 8989 : msg.line_number = tds_get_smallint(tds);
2447 8989 : readed_len += 2;
2448 : }
2449 : /* discard additional bytes */
2450 15703 : 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 15703 : if (msg.sql_state == NULL)
2461 15600 : 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 15703 : if (has_eed == 1) {
2466 : int 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 15703 : if (rc != 0) {
2488 0 : tds_free_msg(&msg);
2489 0 : return TDS_FAIL;
2490 : }
2491 :
2492 : /* special case, */
2493 15703 : 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 15643 : } 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 13585 : if (tds_get_ctx(tds)->msg_handler) {
2503 13174 : tdsdump_log(TDS_DBG_ERROR, "tds_process_info() calling client msg handler\n");
2504 13174 : 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 0 : msg.severity ,
2510 0 : msg.state, msg.server, msg.line_number, msg.message);
2511 : }
2512 : }
2513 :
2514 15703 : if (!tds->conn->server) {
2515 3648 : tds->conn->server = msg.server;
2516 3648 : msg.server = NULL;
2517 : }
2518 15703 : tds_free_msg(&msg);
2519 :
2520 15703 : 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 79553 : tds_alloc_get_string(TDSSOCKET * tds, char **string, size_t len)
2534 : {
2535 : char *s;
2536 : size_t out_len;
2537 :
2538 79553 : CHECK_TDS_EXTRA(tds);
2539 :
2540 : /* assure sufficient space for every conversion */
2541 79553 : s = tds_new(char, len * 4 + 1);
2542 79553 : out_len = tds_get_string(tds, len, s, len * 4);
2543 79553 : if (!s) {
2544 0 : *string = NULL;
2545 0 : return -1;
2546 : }
2547 79553 : s = (char*) realloc(s, out_len + 1);
2548 79553 : s[out_len] = '\0';
2549 79553 : *string = s;
2550 79553 : 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 11642 : tds_process_cancel(TDSSOCKET * tds)
2561 : {
2562 11642 : CHECK_TDS_EXTRA(tds);
2563 :
2564 : /* silly cases, nothing to do */
2565 11642 : if (!tds->in_cancel)
2566 : return TDS_SUCCESS;
2567 : /* TODO handle cancellation sending data */
2568 1556 : 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 1540 : switch (tds_process_tokens(tds, &result_type, NULL, 0)) {
2576 : case TDS_FAIL:
2577 1540 : return TDS_FAIL;
2578 1540 : case TDS_CANCELLED:
2579 : case TDS_SUCCESS:
2580 : case TDS_NO_MORE_RESULTS:
2581 1540 : 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 657820 : tds_lookup_dynamic(TDSCONNECTION * conn, const char *id)
2594 : {
2595 : TDSDYNAMIC *curr;
2596 :
2597 : CHECK_CONN_EXTRA(conn);
2598 :
2599 1314234 : for (curr = conn->dyns; curr != NULL; curr = curr->next) {
2600 656424 : 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 962 : tds_process_dynamic(TDSSOCKET * tds)
2614 : {
2615 : unsigned int token_sz;
2616 : unsigned char type;
2617 962 : TDS_TINYINT id_len, drain = 0;
2618 : char id[TDS_MAX_DYNID_LEN + 1];
2619 :
2620 962 : CHECK_TDS_EXTRA(tds);
2621 :
2622 962 : token_sz = tds_get_usmallint(tds);
2623 962 : type = tds_get_byte(tds);
2624 962 : tds_get_byte(tds); /* status */
2625 : /* handle only acknowledge */
2626 962 : 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 962 : id_len = tds_get_byte(tds);
2632 962 : 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 962 : id_len = tds_get_string(tds, id_len, id, TDS_MAX_DYNID_LEN);
2637 962 : id[id_len] = '\0';
2638 962 : if (drain) {
2639 0 : tds_get_n(tds, NULL, drain);
2640 : }
2641 962 : return tds_lookup_dynamic(tds->conn, id);
2642 : }
2643 :
2644 : /**
2645 : * Process results from dynamic.
2646 : * \tds
2647 : */
2648 : static TDSRET
2649 20 : tds_process_dyn_result(TDSSOCKET * tds)
2650 : {
2651 : unsigned int col, num_cols;
2652 : TDSCOLUMN *curcol;
2653 : TDSPARAMINFO *info;
2654 : TDSDYNAMIC *dyn;
2655 :
2656 20 : CHECK_TDS_EXTRA(tds);
2657 :
2658 20 : tds_get_usmallint(tds); /* header size */
2659 20 : num_cols = tds_get_usmallint(tds);
2660 :
2661 : /* read number of columns and allocate the columns structure */
2662 20 : if ((info = tds_alloc_results(num_cols)) == NULL)
2663 : return TDS_FAIL;
2664 20 : 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 20 : tds_free_param_results(tds->param_info);
2670 20 : tds->param_info = info;
2671 : }
2672 20 : tds_set_current_results(tds, info);
2673 :
2674 48 : for (col = 0; col < info->num_cols; col++) {
2675 28 : curcol = info->columns[col];
2676 :
2677 28 : TDS_PROPAGATE(tds_get_data_info(tds, curcol, 1));
2678 :
2679 : /* skip locale information */
2680 28 : tds_get_n(tds, NULL, tds_get_byte(tds));
2681 : }
2682 :
2683 20 : 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 334 : tds5_process_dyn_result2(TDSSOCKET * tds)
2692 : {
2693 : unsigned int col, num_cols;
2694 : TDSCOLUMN *curcol;
2695 : TDSPARAMINFO *info;
2696 :
2697 334 : CHECK_TDS_EXTRA(tds);
2698 :
2699 334 : tds_get_uint(tds); /* header size */
2700 334 : num_cols = tds_get_usmallint(tds);
2701 :
2702 : /* read number of columns and allocate the columns structure */
2703 334 : if ((info = tds_alloc_results(num_cols)) == NULL)
2704 : return TDS_FAIL;
2705 334 : if (tds->cur_dyn) {
2706 254 : TDSDYNAMIC *dyn = tds->cur_dyn;
2707 254 : tds_free_param_results(dyn->res_info);
2708 254 : dyn->res_info = info;
2709 : } else {
2710 80 : tds_free_param_results(tds->param_info);
2711 80 : tds->param_info = info;
2712 : }
2713 334 : tds_set_current_results(tds, info);
2714 :
2715 768 : for (col = 0; col < info->num_cols; col++) {
2716 434 : curcol = info->columns[col];
2717 :
2718 : /* TODO reuse tds_get_data_info code, sligthly different */
2719 :
2720 : /* column name */
2721 434 : tds_dstr_get(tds, &curcol->column_name, tds_get_byte(tds));
2722 :
2723 : /* column status */
2724 434 : curcol->column_flags = tds_get_int(tds);
2725 434 : curcol->column_nullable = (curcol->column_flags & 0x20) > 0;
2726 :
2727 : /* user type */
2728 434 : curcol->column_usertype = tds_get_int(tds);
2729 :
2730 : /* column type */
2731 868 : TDS_GET_COLUMN_TYPE(curcol);
2732 :
2733 434 : TDS_GET_COLUMN_INFO(tds, curcol);
2734 :
2735 : /* Adjust column size according to client's encoding */
2736 434 : curcol->on_server.column_size = curcol->column_size;
2737 434 : adjust_character_column_size(tds, curcol);
2738 :
2739 : /* discard Locale */
2740 434 : tds_get_n(tds, NULL, tds_get_byte(tds));
2741 :
2742 434 : tdsdump_log(TDS_DBG_INFO1, "elem %d:\n", col);
2743 434 : tdsdump_log(TDS_DBG_INFO1, "\tcolumn_name=[%s]\n", tds_dstr_cstr(&curcol->column_name));
2744 434 : tdsdump_log(TDS_DBG_INFO1, "\tflags=%x utype=%d type=%d server type %d varint=%d\n",
2745 0 : curcol->column_flags, curcol->column_usertype, curcol->column_type, curcol->on_server.column_type,
2746 0 : curcol->column_varint_size);
2747 434 : tdsdump_log(TDS_DBG_INFO1, "\tcolsize=%d prec=%d scale=%d\n",
2748 0 : curcol->column_size, curcol->column_prec, curcol->column_scale);
2749 : }
2750 :
2751 334 : 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 69115 : adjust_character_column_size(TDSSOCKET * tds, TDSCOLUMN * curcol)
3211 : {
3212 69115 : CHECK_TDS_EXTRA(tds);
3213 69115 : CHECK_COLUMN_EXTRA(curcol);
3214 :
3215 69115 : if (is_ascii_type(curcol->on_server.column_type)) {
3216 : /* don't override setting from column collation */
3217 23304 : if (!curcol->char_conv)
3218 5526 : curcol->char_conv = tds->conn->char_convs[client2server_chardata];
3219 : goto compute;
3220 : }
3221 :
3222 45811 : if (IS_TDS7_PLUS(tds->conn)) {
3223 38453 : if (is_unicode_type(curcol->on_server.column_type))
3224 4081 : 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 7358 : if (is_unicode_type(curcol->on_server.column_type) ||
3230 806 : (curcol->on_server.column_type == SYBLONGBINARY && (
3231 806 : curcol->column_usertype == USER_UNICHAR_TYPE ||
3232 : curcol->column_usertype == USER_UNIVARCHAR_TYPE))) {
3233 798 : const int canonic_client = tds->conn->char_convs[client2ucs2]->from.charset.canonic;
3234 798 : const int sybase_utf16 = TDS_CHARSET_UTF_16LE;
3235 :
3236 1596 : 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 798 : 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 798 : if (!curcol->char_conv)
3246 0 : curcol->char_conv = tds->conn->char_convs[client2ucs2];
3247 : }
3248 :
3249 76473 : compute:
3250 69115 : if (!USE_ICONV || !curcol->char_conv)
3251 : return;
3252 :
3253 19520 : curcol->on_server.column_size = curcol->column_size;
3254 39040 : curcol->column_size = determine_adjusted_size(curcol->char_conv, curcol->column_size);
3255 :
3256 19520 : 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 19520 : if ((char_conv->flags & TDS_ENCODING_MEMCPY) != 0
3285 16130 : || char_conv->to.charset.canonic == char_conv->from.charset.canonic)
3286 : return size;
3287 :
3288 : /* avoid possible overflow */
3289 16130 : if (size >= 0x10000000)
3290 : return 0x7fffffff;
3291 :
3292 15986 : size *= char_conv->from.charset.max_bytes_per_char;
3293 15986 : if (size % char_conv->to.charset.min_bytes_per_char)
3294 0 : size += char_conv->to.charset.min_bytes_per_char;
3295 15986 : size /= char_conv->to.charset.min_bytes_per_char;
3296 :
3297 : return size;
3298 : }
3299 :
3300 : /** @} */
|