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