src/dbapi/driver/ftds64/freetds/tds/token.c

Go to the documentation of this file.
00001 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
00002  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Brian Bruns
00003  * Copyright (C) 2005 Frediano Ziglio
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public
00016  * License along with this library; if not, write to the
00017  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018  * Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #if HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif /* HAVE_CONFIG_H */
00024 
00025 #if HAVE_STRING_H
00026 #include <string.h>
00027 #endif /* HAVE_STRING_H */
00028 
00029 #if HAVE_STDLIB_H
00030 #include <stdlib.h>
00031 #endif /* HAVE_STDLIB_H */
00032 
00033 #include <assert.h>
00034 #include <limits.h>
00035 
00036 #include "tds.h"
00037 #include "tdsconvert.h"
00038 #include "tdsiconv.h"
00039 #include "tds_checks.h"
00040 #include "replacements.h"
00041 #ifdef DMALLOC
00042 #include <dmalloc.h>
00043 #endif
00044 
00045 TDS_RCSID(var, "$Id: token.c 174668 2009-10-29 19:44:31Z ivanovp $");
00046 
00047 static int tds_process_msg(TDSSOCKET * tds, int marker);
00048 static int tds_process_compute_result(TDSSOCKET * tds);
00049 static int tds_process_compute_names(TDSSOCKET * tds);
00050 static int tds7_process_compute_result(TDSSOCKET * tds);
00051 static int tds_process_result(TDSSOCKET * tds);
00052 static int tds_process_col_name(TDSSOCKET * tds);
00053 static int tds_process_col_fmt(TDSSOCKET * tds);
00054 static int tds_process_colinfo(TDSSOCKET * tds);
00055 static int tds_process_compute(TDSSOCKET * tds, TDS_INT * computeid);
00056 static int tds_process_cursor_tokens(TDSSOCKET * tds);
00057 static int tds_process_row(TDSSOCKET * tds);
00058 static int tds_process_param_result(TDSSOCKET * tds, TDSPARAMINFO ** info);
00059 static int tds7_process_result(TDSSOCKET * tds);
00060 static TDSDYNAMIC *tds_process_dynamic(TDSSOCKET * tds);
00061 static int tds_process_auth(TDSSOCKET * tds);
00062 static int tds_get_data(TDSSOCKET * tds, TDSCOLUMN * curcol, unsigned char *current_row, int i);
00063 static int tds_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int is_param);
00064 static int tds_process_env_chg(TDSSOCKET * tds);
00065 static /*@observer@*/ const char *_tds_token_name(unsigned char marker);
00066 static int tds_process_param_result_tokens(TDSSOCKET * tds);
00067 static int tds_process_params_result_token(TDSSOCKET * tds);
00068 static int tds_process_dyn_result(TDSSOCKET * tds);
00069 static int tds5_get_varint_size(int datatype);
00070 static int tds5_process_result(TDSSOCKET * tds);
00071 static int tds5_process_dyn_result2(TDSSOCKET * tds);
00072 static void adjust_character_column_size(const TDSSOCKET * tds, TDSCOLUMN * curcol);
00073 static int determine_adjusted_size(const TDSICONV * char_conv, int size);
00074 static int tds_process_default_tokens(TDSSOCKET * tds, int marker);
00075 static int tds_process_end(TDSSOCKET * tds, int marker, /*@out@*/ int *flags_parm);
00076 static int tds5_process_optioncmd(TDSSOCKET * tds);
00077 
00078 static int tds5_get_varint_size(int datatype);
00079 static /*@observer@*/ const char *tds_pr_op(int op);
00080 static int tds_alloc_get_string(TDSSOCKET * tds, /*@special@*/ char **string, int len) /*allocates *string*/;
00081 void generate_random_buffer(unsigned char *out, int len);
00082 
00083 
00084 /**
00085  * \ingroup libtds
00086  * \defgroup token Results processing
00087  * Handle tokens in packets. Many PDU (packets data unit) contain tokens.
00088  * (like result description, rows, data, errors and many other).
00089  */
00090 
00091 
00092 /**
00093  * \addtogroup token
00094  * \@{
00095  */
00096 
00097 /**
00098  * tds_process_default_tokens() is a catch all function that is called to
00099  * process tokens not known to other tds_process_* routines
00100  */
00101 static int
00102 tds_process_default_tokens(TDSSOCKET * tds, int marker)
00103 {
00104     int tok_size;
00105     int done_flags;
00106     TDS_INT ret_status;
00107 
00108     CHECK_TDS_EXTRA(tds);
00109 
00110     tdsdump_log(TDS_DBG_FUNC, "tds_process_default_tokens() marker is %x(%s)\n", marker, _tds_token_name(marker));
00111 
00112     if (IS_TDSDEAD(tds)) {
00113         tdsdump_log(TDS_DBG_FUNC, "leaving tds_process_default_tokens() connection dead\n");
00114         tds_set_state(tds, TDS_DEAD);
00115         return TDS_FAIL;
00116     }
00117 
00118     switch (marker) {
00119     case TDS_AUTH_TOKEN:
00120         return tds_process_auth(tds);
00121         break;
00122     case TDS_ENVCHANGE_TOKEN:
00123         return tds_process_env_chg(tds);
00124         break;
00125     case TDS_DONE_TOKEN:
00126     case TDS_DONEPROC_TOKEN:
00127     case TDS_DONEINPROC_TOKEN:
00128         return tds_process_end(tds, marker, &done_flags);
00129         break;
00130     case TDS_PROCID_TOKEN:
00131         tds_get_n(tds, NULL, 8);
00132         break;
00133     case TDS_RETURNSTATUS_TOKEN:
00134         ret_status = tds_get_int(tds);
00135         marker = tds_peek(tds);
00136         if (marker != TDS_PARAM_TOKEN && marker != TDS_DONEPROC_TOKEN && marker != TDS_DONE_TOKEN)
00137             break;
00138         tds->has_status = 1;
00139         tds->ret_status = ret_status;
00140         tdsdump_log(TDS_DBG_FUNC, "tds_process_default_tokens: return status is %d\n", tds->ret_status);
00141         break;
00142     case TDS_ERROR_TOKEN:
00143     case TDS_INFO_TOKEN:
00144     case TDS_EED_TOKEN:
00145         return tds_process_msg(tds, marker);
00146         break;
00147     case TDS_CAPABILITY_TOKEN:
00148         /* TODO split two part of capability and use it */
00149         tok_size = tds_get_smallint(tds);
00150         /* vicm */
00151         /*
00152          * Sybase 11.0 servers return the wrong length in the capability packet, causing use to read
00153          * past the done packet.
00154          */
00155         if (!TDS_IS_MSSQL(tds) && tds->product_version < TDS_SYB_VER(12, 0, 0)) {
00156             unsigned char type, size, *p, *pend;
00157 
00158             p = tds->capabilities;
00159             pend = tds->capabilities + TDS_MAX_CAPABILITY;
00160 
00161             do {
00162                 type = tds_get_byte(tds);
00163                 size = tds_get_byte(tds);
00164                 if ((p + 2) > pend)
00165                     break;
00166                 *p++ = type;
00167                 *p++ = size;
00168                 if ((p + size) > pend)
00169                     break;
00170                 if (tds_get_n(tds, p, size) == NULL)
00171                     return TDS_FAIL;
00172             } while (type != 2);
00173         } else {
00174             if (tds_get_n(tds, tds->capabilities, tok_size > TDS_MAX_CAPABILITY ? TDS_MAX_CAPABILITY : tok_size) ==
00175                 NULL)
00176                 return TDS_FAIL;
00177         }
00178         break;
00179         /* PARAM_TOKEN can be returned inserting text in db, to return new timestamp */
00180     case TDS_PARAM_TOKEN:
00181         tds_unget_byte(tds);
00182         return tds_process_param_result_tokens(tds);
00183         break;
00184     case TDS7_RESULT_TOKEN:
00185         return tds7_process_result(tds);
00186         break;
00187     case TDS_OPTIONCMD_TOKEN:
00188         return tds5_process_optioncmd(tds);
00189         break;
00190     case TDS_RESULT_TOKEN:
00191         return tds_process_result(tds);
00192         break;
00193     case TDS_ROWFMT2_TOKEN:
00194         return tds5_process_result(tds);
00195         break;
00196     case TDS_COLNAME_TOKEN:
00197         return tds_process_col_name(tds);
00198         break;
00199     case TDS_COLFMT_TOKEN:
00200         return tds_process_col_fmt(tds);
00201         break;
00202     case TDS_ROW_TOKEN:
00203         return tds_process_row(tds);
00204         break;
00205     case TDS5_PARAMFMT_TOKEN:
00206         /* store discarded parameters in param_info, not in old dynamic */
00207         tds->cur_dyn = NULL;
00208         return tds_process_dyn_result(tds);
00209         break;
00210     case TDS5_PARAMFMT2_TOKEN:
00211         tds->cur_dyn = NULL;
00212         return tds5_process_dyn_result2(tds);
00213         break;
00214     case TDS5_PARAMS_TOKEN:
00215         /* save params */
00216         return tds_process_params_result_token(tds);
00217         break;
00218     case TDS_CURINFO_TOKEN:
00219         return tds_process_cursor_tokens(tds);
00220         break;
00221     case TDS5_DYNAMIC_TOKEN:
00222     case TDS_LOGINACK_TOKEN:
00223     case TDS_ORDERBY_TOKEN:
00224     case TDS_CONTROL_TOKEN:
00225     case TDS_TABNAME_TOKEN: /* used for FOR BROWSE query */
00226         tdsdump_log(TDS_DBG_WARN, "Eating %s token\n", _tds_token_name(marker));
00227         tds_get_n(tds, NULL, tds_get_smallint(tds));
00228         break;
00229     case TDS_COLINFO_TOKEN:
00230         return tds_process_colinfo(tds);
00231         break;
00232     case TDS_ORDERBY2_TOKEN:
00233         tdsdump_log(TDS_DBG_WARN, "Eating %s token\n", _tds_token_name(marker));
00234         tds_get_n(tds, NULL, tds_get_int(tds));
00235         break;
00236     default: /* SYBEBTOK */
00237         tds_client_msg(tds->tds_ctx, tds, 20020, 9, 0, 0, "Bad token from the server: Datastream processing out of sync");
00238         if (IS_TDSDEAD(tds))
00239             tds_set_state(tds, TDS_DEAD);
00240         else
00241             tds_close_socket(tds);
00242         tdsdump_log(TDS_DBG_ERROR, "Unknown marker: %d(%x)!!\n", marker, (unsigned char) marker);
00243         return TDS_FAIL;
00244     }
00245     return TDS_SUCCEED;
00246 }
00247 
00248 static int
00249 tds_set_spid(TDSSOCKET * tds)
00250 {
00251     TDS_INT result_type;
00252     TDS_INT done_flags;
00253     TDS_INT rc;
00254     TDSCOLUMN *curcol;
00255 
00256     CHECK_TDS_EXTRA(tds);
00257 
00258     if (tds_submit_query(tds, "select @@spid") != TDS_SUCCEED) {
00259         return TDS_FAIL;
00260     }
00261 
00262     while ((rc = tds_process_tokens(tds, &result_type, &done_flags, TDS_RETURN_ROWFMT|TDS_RETURN_ROW|TDS_RETURN_DONE)) == TDS_SUCCEED) {
00263 
00264         switch (result_type) {
00265 
00266             case TDS_ROWFMT_RESULT:
00267                 if (tds->res_info->num_cols != 1)
00268                     return TDS_FAIL;
00269                 break;
00270 
00271             case TDS_ROW_RESULT:
00272                 curcol = tds->res_info->columns[0];
00273                 if (curcol->column_type == SYBINT2 || (curcol->column_type == SYBINTN && curcol->column_size == 2)) {
00274                     tds->spid = *((TDS_USMALLINT *) (tds->res_info->current_row + curcol->column_offset));
00275                 } else if (curcol->column_type == SYBINT4 || (curcol->column_type == SYBINTN && curcol->column_size == 4)) {
00276                     tds->spid = *((TDS_UINT *) (tds->res_info->current_row + curcol->column_offset));
00277                 } else
00278                     return TDS_FAIL;
00279                 break;
00280 
00281             case TDS_DONE_RESULT:
00282                 if ((done_flags & TDS_DONE_ERROR) != 0)
00283                     return TDS_FAIL;
00284                 break;
00285 
00286             default:
00287                 break;
00288         }
00289     }
00290     if (rc != TDS_NO_MORE_RESULTS)
00291         return TDS_FAIL;
00292 
00293     return TDS_SUCCEED;
00294 }
00295 
00296 /**
00297  * tds_process_login_tokens() is called after sending the login packet
00298  * to the server.  It returns the success or failure of the login
00299  * dependent on the protocol version. 4.2 sends an ACK token only when
00300  * successful, TDS 5.0 sends it always with a success byte within
00301  */
00302 int
00303 tds_process_login_tokens(TDSSOCKET * tds)
00304 {
00305     int succeed = TDS_FAIL;
00306     int marker;
00307     int len;
00308     int memrc = 0;
00309     unsigned char major_ver, minor_ver;
00310     unsigned char ack;
00311     TDS_UINT product_version;
00312 
00313     CHECK_TDS_EXTRA(tds);
00314 
00315     tdsdump_log(TDS_DBG_FUNC, "tds_process_login_tokens()\n");
00316     /* get_incoming(tds->s); */
00317     do {
00318         marker = tds_get_byte(tds);
00319         tdsdump_log(TDS_DBG_FUNC, "looking for login token, got  %x(%s)\n", marker, _tds_token_name(marker));
00320 
00321         switch (marker) {
00322         case TDS_AUTH_TOKEN:
00323             tds_process_auth(tds);
00324             break;
00325         case TDS_LOGINACK_TOKEN:
00326             /* TODO function */
00327             len = tds_get_smallint(tds);
00328             ack = tds_get_byte(tds);
00329             major_ver = tds_get_byte(tds);
00330             minor_ver = tds_get_byte(tds);
00331             tds_get_n(tds, NULL, 2);
00332             /* ignore product name length, see below */
00333             tds_get_byte(tds);
00334             product_version = 0;
00335             /* get server product name */
00336             /* compute length from packet, some version seem to fill this information wrongly */
00337             len -= 10;
00338             if (tds->product_name)
00339                 free(tds->product_name);
00340             if (major_ver >= 7u) {
00341                 product_version = 0x80000000u;
00342                 memrc += tds_alloc_get_string(tds, &tds->product_name, len / 2);
00343             } else if (major_ver >= 5) {
00344                 memrc += tds_alloc_get_string(tds, &tds->product_name, len);
00345             } else {
00346                 memrc += tds_alloc_get_string(tds, &tds->product_name, len);
00347                 if (tds->product_name != NULL && strstr(tds->product_name, "Microsoft") != NULL)
00348                     product_version = 0x80000000u;
00349             }
00350             product_version |= ((TDS_UINT) tds_get_byte(tds)) << 24;
00351             product_version |= ((TDS_UINT) tds_get_byte(tds)) << 16;
00352             product_version |= ((TDS_UINT) tds_get_byte(tds)) << 8;
00353             product_version |= tds_get_byte(tds);
00354             /*
00355              * MSSQL 6.5 and 7.0 seem to return strange values for this
00356              * using TDS 4.2, something like 5F 06 32 FF for 6.50
00357              */
00358             if (major_ver == 4 && minor_ver == 2 && (product_version & 0xff0000ffu) == 0x5f0000ffu)
00359                 product_version = ((product_version & 0xffff00u) | 0x800000u) << 8;
00360             tds->product_version = product_version;
00361 #ifdef WORDS_BIGENDIAN
00362             /* TODO do a best check */
00363 /*
00364 
00365                 if (major_ver==7) {
00366                     tds->broken_dates=1;
00367                 }
00368 */
00369 #endif
00370             /*
00371              * TDS 5.0 reports 5 on success 6 on failure
00372              * TDS 4.2 reports 1 on success and is not
00373              * present on failure
00374              */
00375             if (ack == 5 || ack == 1)
00376                 succeed = TDS_SUCCEED;
00377             break;
00378         default:
00379             if (tds_process_default_tokens(tds, marker) == TDS_FAIL)
00380                 return TDS_FAIL;
00381             break;
00382         }
00383     } while (marker != TDS_DONE_TOKEN);
00384     /* TODO why ?? */
00385     tds->spid = tds->rows_affected;
00386     if (tds->spid == 0) {
00387         if (tds_set_spid(tds) != TDS_SUCCEED) {
00388             tdsdump_log(TDS_DBG_ERROR, "tds_set_spid() failed\n");
00389             succeed = TDS_FAIL;
00390         }
00391     }
00392     tdsdump_log(TDS_DBG_FUNC, "leaving tds_process_login_tokens() returning %d\n", succeed);
00393     if (memrc != 0)
00394         succeed = TDS_FAIL;
00395     return succeed;
00396 }
00397 
00398 /**
00399 put a 8 byte filetime from a time_t
00400 This takes GMT as input
00401 **/
00402 static void unix_to_nt_time(TDS_UINT8 *nt, time_t t)
00403 {
00404 #define TIME_FIXUP_CONSTANT 11644473600LL
00405 
00406     TDS_UINT8 t2;
00407 
00408     if (t == (time_t)-1) {
00409         *nt = (TDS_UINT8)-1LL;
00410         return;
00411     }
00412     if (t == 0) {
00413         *nt = 0;
00414         return;
00415     }
00416 
00417     t2 = t;
00418     t2 += TIME_FIXUP_CONSTANT;
00419     t2 *= 1000*1000*10;
00420 
00421     *nt = t2;
00422 }
00423 
00424 
00425 static TDS_UINT
00426 swap_32(TDS_UINT val)
00427 {
00428     return
00429       (((((TDS_UINT)val) & 0xff000000) >> 24) | ((((TDS_UINT)val) & 0x00ff0000) >>  8) |
00430        ((((TDS_UINT)val) & 0x0000ff00) <<  8) | ((((TDS_UINT)val) & 0x000000ff) << 24));
00431 }
00432 
00433 
00434 static TDS_UINT8
00435 to_le(TDS_UINT8 val)
00436 {
00437 #ifndef WORDS_BIGENDIAN
00438   return val;
00439 #else
00440   union {
00441         TDS_UINT8 ll;
00442         TDS_UINT  l[2];
00443     } w, r;
00444     w.ll = val;
00445     r.l[0] = swap_32( w.l[1] );
00446     r.l[1] = swap_32( w.l[0] );
00447     return r.ll;
00448 #endif
00449 }
00450 
00451 
00452 static void
00453 fill_names_blob_prefix(names_blob_prefix_t* prefix)
00454 {
00455     TDS_UINT8 nttime = 0;
00456 
00457     unix_to_nt_time(&nttime, time(NULL));
00458 
00459     prefix->response_type = 0x01;
00460     prefix->max_response_type = 0x01;
00461     prefix->reserved1 = 0x0000;
00462     prefix->reserved2 = 0x00000000;
00463     prefix->timestamp.l = to_le(nttime);
00464     generate_random_buffer(prefix->challenge, sizeof(prefix->challenge));
00465 
00466     prefix->unknown = 0x00000000;
00467 }
00468 
00469 static int
00470 tds_process_auth(TDSSOCKET * tds)
00471 {
00472     int pdu_size;
00473     unsigned char nonce[8];
00474     TDS_UINT flags;
00475     int domain_len;
00476     int data_block_offset;
00477 
00478     int target_info_len;
00479     int target_info_offset;
00480 
00481     /* For debuging purposes ...
00482     unsigned char* target_info;
00483     int target_info_pos;
00484     short target_type;
00485     short target_len;
00486     unsigned char target_content[128];
00487     */
00488 
00489     int names_blob_len;
00490     unsigned char* names_blob;
00491 
00492     unsigned char domain[128];
00493     int where;
00494     int rc;
00495 
00496     CHECK_TDS_EXTRA(tds);
00497 
00498     memset(domain, 0, sizeof(domain));
00499 
00500 #if ENABLE_EXTRA_CHECKS
00501     if (!IS_TDS7_PLUS(tds))
00502         tdsdump_log(TDS_DBG_ERROR, "Called auth on TDS version < 7\n");
00503 #endif
00504 
00505     pdu_size = tds_get_smallint(tds);
00506     tdsdump_log(TDS_DBG_INFO1, "TDS_AUTH_TOKEN PDU size %d\n", pdu_size);
00507 
00508     /* at least 32 bytes (till context) */
00509     if (pdu_size < 32)
00510         return TDS_FAIL;
00511 
00512     /* Getting Message Type 2 */
00513     /* TODO check first 2 values */
00514     tds_get_n(tds, NULL, 8);    /* NTLMSSP Signature */
00515     tds_get_int(tds);   /* sequence -> 2 */
00516     domain_len = tds_get_smallint(tds); /* domain len */
00517     domain_len = tds_get_smallint(tds); /* domain len */
00518     data_block_offset = tds_get_int(tds);   /* domain offset */
00519     flags = tds_get_int(tds);   /* flags */
00520     tds_get_n(tds, nonce, 8);
00521     tdsdump_dump_buf(TDS_DBG_INFO1, "TDS_AUTH_TOKEN nonce", nonce, 8);
00522     where = 32;
00523 
00524     /*data_block_offset == 32*/
00525     /* Version 1 -- The Context, Target Information, and OS Version structure are all omitted */
00526 
00527     if (data_block_offset != 32) {
00528         /* Version 2 -- The Context and Target Information fields are present, but the OS Version structure is not. */
00529         tds_get_n(tds, NULL, 8); /* Context (two consecutive longs) */
00530 
00531         target_info_len = tds_get_smallint(tds); /* Target Information len */
00532         target_info_len = tds_get_smallint(tds); /* Target Information len */
00533         target_info_offset = tds_get_int(tds);  /* Target Information offset */
00534 
00535         where += 16;
00536 
00537         if (data_block_offset == 56) {
00538             /* Version 3 -- The Context, Target Information, and OS Version structure are all present. */
00539             tds_get_n(tds, NULL, 8); /* OS Version Structure */
00540             where += 8;
00541         }
00542     }
00543 
00544     /* Read domain name in ucs2 encoding ... */
00545     tds_get_n(tds, domain, domain_len);
00546     tdsdump_log(TDS_DBG_INFO1, "TDS_AUTH_TOKEN domain %s\n", domain);
00547     where += domain_len;
00548 
00549     names_blob_len = sizeof(names_blob_prefix_t) + target_info_len + 4;
00550 
00551     /* Read Terget Info */
00552     names_blob = (unsigned char*)malloc(names_blob_len);
00553     memset(names_blob, 0, names_blob_len);
00554 
00555     fill_names_blob_prefix(names_blob);
00556     tds_get_n(tds, names_blob + sizeof(names_blob_prefix_t), target_info_len);
00557 
00558     /* Decode Target Info for debuging purposes ...*/
00559     /*
00560     target_info = names_blob + sizeof(names_blob_prefix_t);
00561     target_info_pos = 0;
00562     do {
00563         target_type = *(target_info + target_info_pos);
00564         target_info_pos += sizeof(target_type);
00565         target_len = *(target_info + target_info_pos);
00566         target_info_pos += sizeof(target_len);
00567         memcpy(target_content, target_info + target_info_pos, target_len);
00568         target_info_pos += target_len;
00569     } while(target_type != 0);
00570     */
00571 
00572     rc = tds7_send_auth(tds, nonce, flags, names_blob, names_blob_len);
00573 
00574     free(names_blob);
00575 
00576     return rc;
00577 }
00578 
00579 /**
00580  * process all streams.
00581  * tds_process_tokens() is called after submitting a query with
00582  * tds_submit_query() and is responsible for calling the routines to
00583  * populate tds->res_info if appropriate (some query have no result sets)
00584  * @param tds A pointer to the TDSSOCKET structure managing a client/server operation.
00585  * @param result_type A pointer to an integer variable which
00586  *        tds_process_tokens sets to indicate the current type of result.
00587  *  @par
00588  *  <b>Values that indicate command status</b>
00589  *  <table>
00590  *   <tr><td>TDS_DONE_RESULT</td><td>The results of a command have been completely processed. This command return no rows.</td></tr>
00591  *   <tr><td>TDS_DONEPROC_RESULT</td><td>The results of a  command have been completely processed. This command return rows.</td></tr>
00592  *   <tr><td>TDS_DONEINPROC_RESULT</td><td>The results of a  command have been completely processed. This command return rows.</td></tr>
00593  *  </table>
00594  *  <b>Values that indicate results information is available</b>
00595  *  <table><tr>
00596  *    <td>TDS_ROWFMT_RESULT</td><td>Regular Data format information</td>
00597  *    <td>tds->res_info now contains the result details ; tds->current_results now points to that data</td>
00598  *   </tr><tr>
00599  *    <td>TDS_COMPUTEFMT_ RESULT</td><td>Compute data format information</td>
00600  *    <td>tds->comp_info now contains the result data; tds->current_results now points to that data</td>
00601  *   </tr><tr>
00602  *    <td>TDS_DESCRIBE_RESULT</td><td></td>
00603  *    <td></td>
00604  *  </tr></table>
00605  *  <b>Values that indicate data is available</b>
00606  *  <table><tr>
00607  *   <td><b>Value</b></td><td><b>Meaning</b></td><td><b>Information returned</b></td>
00608  *   </tr><tr>
00609  *    <td>TDS_ROW_RESULT</td><td>Regular row results</td>
00610  *    <td>1 or more rows of regular data can now be retrieved</td>
00611  *   </tr><tr>
00612  *    <td>TDS_COMPUTE_RESULT</td><td>Compute row results</td>
00613  *    <td>A single row of compute data can now be retrieved</td>
00614  *   </tr><tr>
00615  *    <td>TDS_PARAM_RESULT</td><td>Return parameter results</td>
00616  *    <td>param_info or cur_dyn->params contain returned parameters</td>
00617  *   </tr><tr>
00618  *    <td>TDS_STATUS_RESULT</td><td>Stored procedure status results</td>
00619  *    <td>tds->ret_status contain the returned code</td>
00620  *  </tr></table>
00621  * @param flag Flags to select token type to stop/return
00622  * @todo Complete TDS_DESCRIBE_RESULT description
00623  * @retval TDS_SUCCEED if a result set is available for processing.
00624  * @retval TDS_NO_MORE_RESULTS if all results have been completely processed.
00625  */
00626 int
00627 tds_process_tokens(TDSSOCKET *tds, TDS_INT *result_type, int *done_flags, unsigned flag)
00628 {
00629     int marker;
00630     TDSPARAMINFO *pinfo = NULL;
00631     TDSCOLUMN   *curcol;
00632     int rc;
00633     int saved_rows_affected = tds->rows_affected;
00634     TDS_INT ret_status;
00635     int cancel_seen = 0;
00636     unsigned return_flag = 0;
00637 
00638 #define SET_RETURN(ret, f) \
00639     *result_type = ret; \
00640     return_flag = TDS_RETURN_##f | TDS_STOPAT_##f; \
00641     if (flag & TDS_STOPAT_##f) {\
00642         tds_unget_byte(tds); \
00643         tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens::SET_RETURN stopping on current token\n"); \
00644         break; \
00645     }
00646 
00647     CHECK_TDS_EXTRA(tds);
00648 
00649     tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens(%p, %p, %p, 0x%x)\n", tds, result_type, done_flags, flag);
00650 
00651     if (tds->state == TDS_IDLE) {
00652         tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens() state is COMPLETED\n");
00653         *result_type = TDS_DONE_RESULT;
00654         return TDS_NO_MORE_RESULTS;
00655     }
00656 
00657     if (tds_set_state(tds, TDS_READING) != TDS_READING)
00658         return TDS_FAIL;
00659 
00660     rc = TDS_SUCCEED;
00661     for (;;) {
00662 
00663         marker = tds_get_byte(tds);
00664         tdsdump_log(TDS_DBG_INFO1, "processing result tokens.  marker is  %x(%s)\n", marker, _tds_token_name(marker));
00665 
00666         switch (marker) {
00667         case TDS7_RESULT_TOKEN:
00668 
00669             /*
00670              * If we're processing the results of a cursor fetch
00671              * from sql server we don't want to pass back the
00672              * TDS_ROWFMT_RESULT to the calling API
00673              */
00674 
00675             if (tds->internal_sp_called == TDS_SP_CURSORFETCH) {
00676                 rc = tds7_process_result(tds);
00677                 marker = tds_get_byte(tds);
00678                 if (marker != TDS_TABNAME_TOKEN) {
00679                     tds_unget_byte(tds);
00680                 } else {
00681                     if ((rc = tds_process_default_tokens(tds, marker)) == TDS_FAIL)
00682                         break;
00683                     marker = tds_get_byte(tds);
00684                     if (marker != TDS_COLINFO_TOKEN) {
00685                         tds_unget_byte(tds);
00686                     } else {
00687                         tds_process_colinfo(tds);
00688                     }
00689                 }
00690             } else {
00691                 SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
00692 
00693                 rc = tds7_process_result(tds);
00694                 /*
00695                  * handle browse information (if presents)
00696                  * TODO copied from below, function or put in results process
00697                  */
00698                 marker = tds_get_byte(tds);
00699                 if (marker != TDS_TABNAME_TOKEN) {
00700                     tds_unget_byte(tds);
00701                     rc = TDS_SUCCEED;
00702                     break;
00703                 }
00704                 if ((rc = tds_process_default_tokens(tds, marker)) == TDS_FAIL)
00705                     break;
00706                 marker = tds_get_byte(tds);
00707                 if (marker != TDS_COLINFO_TOKEN) {
00708                     tds_unget_byte(tds);
00709                     rc = TDS_SUCCEED;
00710                     break;
00711                 }
00712                 if (rc == TDS_FAIL)
00713                     break;
00714                 else {
00715                     tds_process_colinfo(tds);
00716                     rc = TDS_SUCCEED;
00717                     break;
00718                 }
00719             }
00720             break;
00721         case TDS_RESULT_TOKEN:
00722             SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
00723             rc = tds_process_result(tds);
00724             break;
00725         case TDS_ROWFMT2_TOKEN:
00726             SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
00727             rc = tds5_process_result(tds);
00728             break;
00729         case TDS_COLNAME_TOKEN:
00730             rc = tds_process_col_name(tds);
00731             break;
00732         case TDS_COLFMT_TOKEN:
00733             SET_RETURN(TDS_ROWFMT_RESULT, ROWFMT);
00734             rc = tds_process_col_fmt(tds);
00735             /* handle browse information (if presents) */
00736             marker = tds_get_byte(tds);
00737             if (marker != TDS_TABNAME_TOKEN) {
00738                 tds_unget_byte(tds);
00739                 break;
00740             }
00741             if ((rc = tds_process_default_tokens(tds, marker)) == TDS_FAIL)
00742                 break;
00743             marker = tds_get_byte(tds);
00744             if (marker != TDS_COLINFO_TOKEN) {
00745                 tds_unget_byte(tds);
00746                 break;
00747             }
00748             if (rc == TDS_FAIL)
00749                 break;
00750             tds_process_colinfo(tds);
00751             rc = TDS_SUCCEED;
00752             break;
00753         case TDS_PARAM_TOKEN:
00754             tds_unget_byte(tds);
00755             if (tds->internal_sp_called) {
00756                 tdsdump_log(TDS_DBG_FUNC, "processing parameters for sp %d\n", tds->internal_sp_called);
00757                 while ((marker = tds_get_byte(tds)) == TDS_PARAM_TOKEN) {
00758                     tdsdump_log(TDS_DBG_INFO1, "calling tds_process_param_result\n");
00759                     tds_process_param_result(tds, &pinfo);
00760                 }
00761                 tds_unget_byte(tds);
00762                 tdsdump_log(TDS_DBG_FUNC, "%d hidden return parameters\n", pinfo ? pinfo->num_cols : -1);
00763                 if (pinfo && pinfo->num_cols > 0) {
00764                     curcol = pinfo->columns[0];
00765                     if (tds->internal_sp_called == TDS_SP_CURSOROPEN && tds->cur_cursor) {
00766                         TDSCURSOR  *cursor = tds->cur_cursor;
00767 
00768                         cursor->cursor_id = *(TDS_INT *) &(pinfo->current_row[curcol->column_offset]);
00769                         tdsdump_log(TDS_DBG_FUNC, "stored internal cursor id %d\n",
00770                                 cursor->cursor_id);
00771                         cursor->srv_status &= ~TDS_CUR_ISTAT_CLOSED;
00772                         cursor->srv_status |= TDS_CUR_ISTAT_OPEN;
00773                         /* 0.95
00774                         cursor->srv_status &= ~(TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_OPEN|TDS_CUR_ISTAT_DEALLOC);
00775                         cursor->srv_status |= cursor->cursor_id ? TDS_CUR_ISTAT_OPEN : TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_DEALLOC;
00776                         */
00777                     }
00778                     if (tds->internal_sp_called == TDS_SP_PREPARE
00779                         && tds->cur_dyn && tds->cur_dyn->num_id == 0 && curcol->column_cur_size > 0) {
00780                         tds->cur_dyn->num_id = *(TDS_INT *) &(pinfo->current_row[curcol->column_offset]);
00781                     }
00782                 }
00783                 tds_free_param_results(pinfo);
00784             } else {
00785                 SET_RETURN(TDS_PARAM_RESULT, PROC);
00786                 rc = tds_process_param_result_tokens(tds);
00787             }
00788             break;
00789         case TDS_COMPUTE_NAMES_TOKEN:
00790             rc = tds_process_compute_names(tds);
00791             break;
00792         case TDS_COMPUTE_RESULT_TOKEN:
00793             SET_RETURN(TDS_COMPUTEFMT_RESULT, COMPUTEFMT);
00794             rc = tds_process_compute_result(tds);
00795             break;
00796         case TDS7_COMPUTE_RESULT_TOKEN:
00797             SET_RETURN(TDS_COMPUTEFMT_RESULT, COMPUTEFMT);
00798             rc = tds7_process_compute_result(tds);
00799             break;
00800         case TDS_ROW_TOKEN:
00801             /* overstepped the mark... */
00802             if (tds->cur_cursor) {
00803                 TDSCURSOR  *cursor = tds->cur_cursor;
00804 
00805                 tds->current_results = cursor->res_info;
00806                 tdsdump_log(TDS_DBG_INFO1, "tds_process_tokens(). set current_results to cursor->res_info\n");
00807             } else {
00808                 /* assure that we point to row, not to compute */
00809                 if (tds->res_info)
00810                     tds->current_results = tds->res_info;
00811             }
00812             /* I don't know when this it's false but it happened, also server can send garbage... */
00813             if (tds->current_results)
00814                 tds->current_results->rows_exist = 1;
00815             SET_RETURN(TDS_ROW_RESULT, ROW);
00816 
00817             rc = tds_process_row(tds);
00818             break;
00819         case TDS_CMP_ROW_TOKEN:
00820             /* I don't know when this it's false but it happened, also server can send garbage... */
00821             if (tds->res_info)
00822                 tds->res_info->rows_exist = 1;
00823             SET_RETURN(TDS_COMPUTE_RESULT, COMPUTE);
00824             rc = tds_process_compute(tds, NULL);
00825             break;
00826         case TDS_RETURNSTATUS_TOKEN:
00827             ret_status = tds_get_int(tds);
00828             marker = tds_peek(tds);
00829             if (marker != TDS_PARAM_TOKEN && marker != TDS_DONEPROC_TOKEN && marker != TDS_DONE_TOKEN)
00830                 break;
00831             if (tds->internal_sp_called) {
00832                 /* TODO perhaps we should use ret_status ?? */
00833             } else {
00834                 /* TODO optimize */
00835                 flag &= ~TDS_STOPAT_PROC;
00836                 SET_RETURN(TDS_STATUS_RESULT, PROC);
00837                 tds->has_status = 1;
00838                 tds->ret_status = ret_status;
00839                 tdsdump_log(TDS_DBG_FUNC, "tds_process_tokens: return status is %d\n", tds->ret_status);
00840                 rc = TDS_SUCCEED;
00841             }
00842             break;
00843         case TDS5_DYNAMIC_TOKEN:
00844             /* process acknowledge dynamic */
00845             tds->cur_dyn = tds_process_dynamic(tds);
00846             /* special case, prepared statement cannot be prepared */
00847             if (!tds->cur_dyn || tds->cur_dyn->emulated)
00848                 break;
00849             marker = tds_get_byte(tds);
00850             if (marker != TDS_EED_TOKEN) {
00851                 tds_unget_byte(tds);
00852                 break;
00853             }
00854             tds_process_msg(tds, marker);
00855             if (!tds->cur_dyn || !tds->cur_dyn->emulated)
00856                 break;
00857             marker = tds_get_byte(tds);
00858             if (marker != TDS_DONE_TOKEN) {
00859                 tds_unget_byte(tds);
00860                 break;
00861             }
00862             rc = tds_process_end(tds, marker, done_flags);
00863             if (done_flags)
00864                 *done_flags &= ~TDS_DONE_ERROR;
00865             /* FIXME warning to macro expansion */
00866             SET_RETURN(TDS_DONE_RESULT, DONE);
00867             break;
00868         case TDS5_PARAMFMT_TOKEN:
00869             SET_RETURN(TDS_DESCRIBE_RESULT, PARAMFMT);
00870             rc = tds_process_dyn_result(tds);
00871             break;
00872         case TDS5_PARAMFMT2_TOKEN:
00873             SET_RETURN(TDS_DESCRIBE_RESULT, PARAMFMT);
00874             rc = tds5_process_dyn_result2(tds);
00875             break;
00876         case TDS5_PARAMS_TOKEN:
00877             SET_RETURN(TDS_PARAM_RESULT, PROC);
00878             rc = tds_process_params_result_token(tds);
00879             break;
00880         case TDS_CURINFO_TOKEN:
00881             rc = tds_process_cursor_tokens(tds);
00882             break;
00883         case TDS_DONE_TOKEN:
00884             SET_RETURN(TDS_DONE_RESULT, DONE);
00885             rc = tds_process_end(tds, marker, done_flags);
00886             break;
00887         case TDS_DONEPROC_TOKEN:
00888             SET_RETURN(TDS_DONEPROC_RESULT, DONE);
00889             rc = tds_process_end(tds, marker, done_flags);
00890             tds->rows_affected = saved_rows_affected; /* ssikorsk */
00891             switch (tds->internal_sp_called) {
00892             case 0:
00893             case TDS_SP_PREPARE:
00894             case TDS_SP_EXECUTE:
00895             case TDS_SP_UNPREPARE:
00896             case TDS_SP_EXECUTESQL:
00897                 break;
00898             case TDS_SP_CURSOROPEN:
00899                 *result_type       = TDS_DONE_RESULT;
00900                 tds->rows_affected = saved_rows_affected;
00901                 break;
00902             case TDS_SP_CURSORCLOSE:
00903                 tdsdump_log(TDS_DBG_FUNC, "TDS_SP_CURSORCLOSE\n");
00904                 if (tds->cur_cursor) {
00905 
00906                     TDSCURSOR  *cursor = tds->cur_cursor;
00907 
00908                     cursor->srv_status &= ~TDS_CUR_ISTAT_OPEN;
00909                     cursor->srv_status |= TDS_CUR_ISTAT_CLOSED;
00910                     cursor->srv_status |= TDS_CUR_ISTAT_DECLARED;
00911                     if (cursor->status.dealloc == 2) {
00912                         tds_free_cursor(tds, cursor);
00913                     }
00914                     /* 0.95
00915                     cursor->srv_status |= TDS_CUR_ISTAT_CLOSED|TDS_CUR_ISTAT_DECLARED;
00916                     if (cursor->status.dealloc == TDS_CURSOR_STATE_SENT) {
00917                         tds_cursor_deallocated(tds, cursor);
00918                     }
00919                     */
00920                 }
00921                 *result_type = TDS_NO_MORE_RESULTS;
00922                 rc = TDS_NO_MORE_RESULTS;
00923                 break;
00924             default:
00925                 *result_type = TDS_NO_MORE_RESULTS;
00926                 rc = TDS_NO_MORE_RESULTS;
00927                 break;
00928             }
00929             break;
00930         case TDS_DONEINPROC_TOKEN:
00931             switch(tds->internal_sp_called) {
00932             case TDS_SP_CURSOROPEN:
00933             case TDS_SP_CURSORFETCH:
00934             case TDS_SP_PREPARE:
00935             case TDS_SP_CURSORCLOSE:
00936                 rc = tds_process_end(tds, marker, done_flags);
00937                 if (tds->rows_affected != TDS_NO_COUNT) {
00938                     saved_rows_affected = tds->rows_affected;
00939                 }
00940                 break;
00941             default:
00942                 SET_RETURN(TDS_DONEINPROC_RESULT, DONE);
00943                 rc = tds_process_end(tds, marker, done_flags);
00944                 break;
00945             }
00946             break;
00947         case TDS_ERROR_TOKEN:
00948         case TDS_INFO_TOKEN:
00949         case TDS_EED_TOKEN:
00950             SET_RETURN(TDS_MSG_RESULT, MSG);
00951             rc = tds_process_default_tokens(tds, marker);
00952             break;
00953         default:
00954             SET_RETURN(TDS_OTHERS_RESULT, OTHERS);
00955             rc = tds_process_default_tokens(tds, marker);
00956             break;
00957         }
00958 
00959         if (rc == TDS_FAIL) {
00960             tds_set_state(tds, TDS_PENDING);
00961             return rc;
00962         }
00963 
00964         cancel_seen |= tds->in_cancel;
00965         if (cancel_seen) {
00966             /* during cancel handle all tokens */
00967             flag = TDS_HANDLE_ALL;
00968         }
00969 
00970         if ((return_flag & flag) != 0) {
00971             tds_set_state(tds, TDS_PENDING);
00972             return rc;
00973         }
00974 
00975         if (tds->state == TDS_IDLE)
00976             return cancel_seen ? TDS_CANCELLED : TDS_NO_MORE_RESULTS;
00977 
00978         if (tds->state == TDS_DEAD) {
00979             /* TODO free all results ?? */
00980             return TDS_FAIL;
00981         }
00982     }
00983 }
00984 
00985 /**
00986  * Process results for simple query as "SET TEXTSIZE" or "USE dbname"
00987  * If the statement returns results, beware they are discarded.
00988  *
00989  * This function was written to avoid direct calls to tds_process_default_tokens
00990  * (which caused problems such as ignoring query errors).
00991  * Results are read until idle state or severe failure (do not stop for
00992  * statement failure).
00993  * @return see tds_process_tokens for results (TDS_NO_MORE_RESULTS is never returned)
00994  */
00995 int
00996 tds_process_simple_query(TDSSOCKET * tds)
00997 {
00998     TDS_INT res_type;
00999     TDS_INT done_flags;
01000     int     rc;
01001     int     ret = TDS_SUCCEED;
01002 
01003     CHECK_TDS_EXTRA(tds);
01004 
01005     while ((rc = tds_process_tokens(tds, &res_type, &done_flags, TDS_RETURN_DONE)) == TDS_SUCCEED) {
01006         switch (res_type) {
01007 
01008             case TDS_DONE_RESULT:
01009             case TDS_DONEPROC_RESULT:
01010             case TDS_DONEINPROC_RESULT:
01011                 if ((done_flags & TDS_DONE_ERROR) != 0)
01012                     ret = TDS_FAIL;
01013                 break;
01014 
01015             default:
01016                 break;
01017         }
01018     }
01019     if (rc != TDS_NO_MORE_RESULTS) {
01020         ret = TDS_FAIL;
01021     }
01022 
01023     return ret;
01024 }
01025 
01026 /**
01027  * tds_process_col_name() is one half of the result set under TDS 4.2
01028  * it contains all the column names, a TDS_COLFMT_TOKEN should
01029  * immediately follow this token with the datatype/size information
01030  * This is a 4.2 only function
01031  */
01032 static int
01033 tds_process_col_name(TDSSOCKET * tds)
01034 {
01035     int hdrsize, len = 0;
01036     int memrc = 0;
01037     int col, num_cols = 0;
01038     struct tmp_col_struct
01039     {
01040         char *column_name;
01041         int column_namelen;
01042         struct tmp_col_struct *next;
01043     };
01044     struct tmp_col_struct *head = NULL, *cur = NULL, *prev;
01045     TDSCOLUMN *curcol;
01046     TDSRESULTINFO *info;
01047 
01048     CHECK_TDS_EXTRA(tds);
01049 
01050     hdrsize = tds_get_smallint(tds);
01051 
01052     /*
01053      * this is a little messy...TDS 5.0 gives the number of columns
01054      * upfront, while in TDS 4.2, you're expected to figure it out
01055      * by the size of the message. So, I use a link list to get the
01056      * colum names and then allocate the result structure, copy
01057      * and delete the linked list
01058      */
01059     /*
01060      * TODO reallocate columns
01061      * TODO code similar below, function to reuse
01062      */
01063     while (len < hdrsize) {
01064         prev = cur;
01065         cur = (struct tmp_col_struct *)
01066             malloc(sizeof(struct tmp_col_struct));
01067 
01068         if (!cur) {
01069             memrc = -1;
01070             break;
01071         }
01072 
01073         if (prev)
01074             prev->next = cur;
01075         if (!head)
01076             head = cur;
01077 
01078         cur->column_namelen = tds_get_byte(tds);
01079         memrc += tds_alloc_get_string(tds, &cur->column_name, cur->column_namelen);
01080         cur->next = NULL;
01081 
01082         len += cur->column_namelen + 1;
01083         num_cols++;
01084     }
01085 
01086     /* free results/computes/params etc... */
01087     tds_free_all_results(tds);
01088     tds->rows_affected = TDS_NO_COUNT;
01089 
01090     if ((info = tds_alloc_results(num_cols)) == NULL)
01091         memrc = -1;
01092     tds->current_results = tds->res_info = info;
01093 
01094     cur = head;
01095 
01096     if (memrc != 0) {
01097         while (cur != NULL) {
01098             prev = cur;
01099             cur = cur->next;
01100             free(prev->column_name);
01101             free(prev);
01102         }
01103         return TDS_FAIL;
01104     } else {
01105         for (col = 0; col < info->num_cols; col++) {
01106             curcol = info->columns[col];
01107             tds_strlcpy(curcol->column_name, cur->column_name, sizeof(curcol->column_name));
01108             curcol->column_namelen = strlen(curcol->column_name);
01109             prev = cur;
01110             cur = cur->next;
01111             free(prev->column_name);
01112             free(prev);
01113         }
01114         return TDS_SUCCEED;
01115     }
01116 }
01117 
01118 /**
01119  * Add a column size to result info row size and calc offset into row
01120  * @param info   result where to add column
01121  * @param curcol column to add
01122  */
01123 void
01124 tds_add_row_column_size(TDSRESULTINFO * info, TDSCOLUMN * curcol)
01125 {
01126     CHECK_RESULTINFO_EXTRA(info);
01127     CHECK_COLUMN_EXTRA(curcol);
01128 
01129     /*
01130      * the column_offset is the offset into the row buffer
01131      * where this column begins, text types are no longer
01132      * stored in the row buffer because the max size can
01133      * be too large (2gig) to allocate
01134      */
01135     curcol->column_offset = info->row_size;
01136     if (is_numeric_type(curcol->column_type)) {
01137         info->row_size += sizeof(TDS_NUMERIC);
01138     } else if (is_blob_type(curcol->column_type)) {
01139         info->row_size += sizeof(TDSBLOB);
01140     } else {
01141         info->row_size += curcol->column_size;
01142     }
01143 
01144     /* Added by ssikorsk */
01145     info->row_size += curcol->column_varint_size;
01146 
01147     info->row_size += (TDS_ALIGN_SIZE - 1);
01148     info->row_size -= info->row_size % TDS_ALIGN_SIZE;
01149 }
01150 
01151 /**
01152  * tds_process_col_fmt() is the other half of result set processing
01153  * under TDS 4.2. It follows tds_process_col_name(). It contains all the
01154  * column type and size information.
01155  * This is a 4.2 only function
01156  */
01157 static int
01158 tds_process_col_fmt(TDSSOCKET * tds)
01159 {
01160     int col, hdrsize;
01161     TDSCOLUMN *curcol;
01162     TDSRESULTINFO *info;
01163     /* TDS_SMALLINT tabnamesize; */
01164     int bytes_read = 0;
01165     int rest;
01166     TDS_SMALLINT flags;
01167 
01168     CHECK_TDS_EXTRA(tds);
01169 
01170     hdrsize = tds_get_smallint(tds);
01171 
01172     /* TODO use current_results instead of res_info ?? */
01173     info = tds->res_info;
01174     for (col = 0; col < info->num_cols; col++) {
01175         curcol = info->columns[col];
01176         /* In Sybase all 4 byte are used for usertype, while mssql place 2 byte as usertype and 2 byte as flags */
01177         if (TDS_IS_MSSQL(tds)) {
01178             curcol->column_usertype = tds_get_smallint(tds);
01179             flags = tds_get_smallint(tds);
01180             curcol->column_nullable = flags & 0x01;
01181             curcol->column_writeable = (flags & 0x08) > 0;
01182             curcol->column_identity = (flags & 0x10) > 0;
01183         } else {
01184             curcol->column_usertype = tds_get_int(tds);
01185         }
01186         /* on with our regularly scheduled code (mlilback, 11/7/01) */
01187         tds_set_column_type(tds, curcol, tds_get_byte(tds));
01188 
01189         tdsdump_log(TDS_DBG_INFO1, "processing result. type = %d(%s), varint_size %d\n",
01190                 curcol->column_type, tds_prtype(curcol->column_type), curcol->column_varint_size);
01191 
01192         switch (curcol->column_varint_size) {
01193         case 4:
01194             curcol->column_size = tds_get_int(tds);
01195             /* junk the table name -- for now
01196             tabnamesize = tds_get_smallint(tds);
01197             tds_get_n(tds, NULL, tabnamesize);
01198             bytes_read += 5 + 4 + 2 + tabnamesize;
01199             */
01200             /* ssikorsk */
01201             curcol->table_namelen = tds_get_smallint(tds);
01202             tds_get_n(tds, curcol->table_name, curcol->table_namelen);
01203             bytes_read += 5 + 4 + 2 + curcol->table_namelen;
01204             break;
01205         case 1:
01206             curcol->column_size = tds_get_byte(tds);
01207             bytes_read += 5 + 1;
01208             break;
01209         case 0:
01210             bytes_read += 5 + 0;
01211             break;
01212         }
01213 
01214         /* Adjust column size according to client's encoding */
01215         curcol->on_server.column_size = curcol->column_size;
01216         adjust_character_column_size(tds, curcol);
01217 
01218         tds_add_row_column_size(info, curcol);
01219     }
01220 
01221     /* get the rest of the bytes */
01222     rest = hdrsize - bytes_read;
01223     if (rest > 0) {
01224         tdsdump_log(TDS_DBG_INFO1, "NOTE:tds_process_col_fmt: draining %d bytes\n", rest);
01225         tds_get_n(tds, NULL, rest);
01226     }
01227 
01228     if ((info->current_row = tds_alloc_row(info)) != NULL)
01229         return TDS_SUCCEED;
01230     else
01231         return TDS_FAIL;
01232 }
01233 
01234 static int
01235 tds_process_colinfo(TDSSOCKET * tds)
01236 {
01237     int hdrsize;
01238     TDSCOLUMN *curcol;
01239     TDSRESULTINFO *info;
01240     int bytes_read = 0;
01241     unsigned char col_info[3], l;
01242 
01243     CHECK_TDS_EXTRA(tds);
01244 
01245     hdrsize = tds_get_smallint(tds);
01246 
01247     info = tds->current_results;
01248 
01249     while (bytes_read < hdrsize) {
01250 
01251         tds_get_n(tds, col_info, 3);
01252         bytes_read += 3;
01253         if (info && col_info[0] > 0 && col_info[0] <= info->num_cols) {
01254             curcol = info->columns[col_info[0] - 1];
01255             curcol->column_writeable = (col_info[2] & 0x4) == 0;
01256             curcol->column_key = (col_info[2] & 0x8) > 0;
01257             curcol->column_hidden = (col_info[2] & 0x10) > 0;
01258         }
01259         /* skip real name */
01260         /* TODO keep it */
01261         if (col_info[2] & 0x20) {
01262             l = tds_get_byte(tds);
01263             if (IS_TDS7_PLUS(tds))
01264                 l *= 2;
01265             tds_get_n(tds, NULL, l);
01266             bytes_read += l + 1;
01267         }
01268     }
01269 
01270     return TDS_SUCCEED;
01271 }
01272 
01273 /**
01274  * tds_process_param_result() processes output parameters of a stored
01275  * procedure. This differs from regular row/compute results in that there
01276  * is no total number of parameters given, they just show up singly.
01277  */
01278 static int
01279 tds_process_param_result(TDSSOCKET * tds, TDSPARAMINFO ** pinfo)
01280 {
01281     int hdrsize;
01282     TDSCOLUMN *curparam;
01283     TDSPARAMINFO *info;
01284     int i;
01285 
01286     CHECK_TDS_EXTRA(tds);
01287     if (*pinfo)
01288         CHECK_PARAMINFO_EXTRA(*pinfo);
01289 
01290     /* TODO check if current_results is a param result */
01291 
01292     /* limited to 64K but possible types are always smaller (not TEXT/IMAGE) */
01293     hdrsize = tds_get_smallint(tds);
01294     if ((info = tds_alloc_param_result(*pinfo)) == NULL)
01295         return TDS_FAIL;
01296 
01297     *pinfo = info;
01298     curparam = info->columns[info->num_cols - 1];
01299 
01300     /*
01301      * FIXME check support for tds7+ (seem to use same format of tds5 for data...)
01302      * perhaps varint_size can be 2 or collation can be specified ??
01303      */
01304     tds_get_data_info(tds, curparam, 1);
01305 
01306     curparam->column_cur_size = curparam->column_size;  /* needed ?? */
01307 
01308     if (tds_alloc_param_row(info, curparam) == NULL)
01309         return TDS_FAIL;
01310 
01311     i = tds_get_data(tds, curparam, info->current_row, info->num_cols - 1);
01312 
01313     /*
01314      * Real output parameters will either be unnamed or will have a valid
01315      * parameter name beginning with '@'. Ignore any other Spurious parameters
01316      * such as those returned from calls to writetext in the proc.
01317      */
01318     if (curparam->column_namelen > 0 && curparam->column_name[0] != '@')
01319         tds_free_param_result(*pinfo);
01320 
01321     return i;
01322 }
01323 
01324 static int
01325 tds_process_param_result_tokens(TDSSOCKET * tds)
01326 {
01327     int marker;
01328     TDSPARAMINFO **pinfo;
01329 
01330     CHECK_TDS_EXTRA(tds);
01331 
01332     if (tds->cur_dyn)
01333         pinfo = &(tds->cur_dyn->res_info);
01334     else
01335         pinfo = &(tds->param_info);
01336 
01337     while ((marker = tds_get_byte(tds)) == TDS_PARAM_TOKEN) {
01338         tds_process_param_result(tds, pinfo);
01339     }
01340     if (!marker) {
01341         tdsdump_log(TDS_DBG_FUNC, "error: tds_process_param_result() returned TDS_FAIL\n");
01342         return TDS_FAIL;
01343     }
01344 
01345     tds->current_results = *pinfo;
01346     tds_unget_byte(tds);
01347     return TDS_SUCCEED;
01348 }
01349 
01350 /**
01351  * tds_process_params_result_token() processes params on TDS5.
01352  */
01353 static int
01354 tds_process_params_result_token(TDSSOCKET * tds)
01355 {
01356     int i;
01357     TDSCOLUMN *curcol;
01358     TDSPARAMINFO *info;
01359 
01360     CHECK_TDS_EXTRA(tds);
01361 
01362     /* TODO check if current_results is a param result */
01363     info = tds->current_results;
01364     if (!info)
01365         return TDS_FAIL;
01366 
01367     for (i = 0; i < info->num_cols; i++) {
01368         curcol = info->columns[i];
01369         if (tds_get_data(tds, curcol, info->current_row, i) != TDS_SUCCEED)
01370             return TDS_FAIL;
01371     }
01372     return TDS_SUCCEED;
01373 }
01374 
01375 /**
01376  * tds_process_compute_result() processes compute result sets.  These functions
01377  * need work but since they get little use, nobody has complained!
01378  * It is very similar to normal result sets.
01379  */
01380 static int
01381 tds_process_compute_result(TDSSOCKET * tds)
01382 {
01383     int hdrsize;
01384     int col, num_cols;
01385     TDS_TINYINT by_cols = 0;
01386     TDS_SMALLINT *cur_by_col;
01387     TDS_SMALLINT compute_id = 0;
01388     TDSCOLUMN *curcol;
01389     TDSCOMPUTEINFO *info;
01390     int i;
01391 
01392     CHECK_TDS_EXTRA(tds);
01393 
01394     hdrsize = tds_get_smallint(tds);
01395 
01396     /*
01397      * compute statement id which this relates
01398      * to. You can have more than one compute
01399      * statement in a SQL statement
01400      */
01401 
01402     compute_id = tds_get_smallint(tds);
01403 
01404     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. compute_id = %d\n", compute_id);
01405 
01406     /*
01407      * number of compute columns returned - so
01408      * COMPUTE SUM(x), AVG(x)... would return
01409      * num_cols = 2
01410      */
01411 
01412     num_cols = tds_get_byte(tds);
01413 
01414     for (i = 0;; ++i) {
01415         if (i >= tds->num_comp_info)
01416             return TDS_FAIL;
01417         info = tds->comp_info[i];
01418         tdsdump_log(TDS_DBG_FUNC, "in dbaltcolid() found computeid = %d\n", info->computeid);
01419         if (info->computeid == compute_id)
01420             break;
01421     }
01422 
01423     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. num_cols = %d\n", num_cols);
01424 
01425     for (col = 0; col < num_cols; col++) {
01426         tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 2\n");
01427         curcol = info->columns[col];
01428 
01429         curcol->column_operator = tds_get_byte(tds);
01430         curcol->column_operand = tds_get_byte(tds);
01431 
01432         /*
01433          * if no name has been defined for the compute column,
01434          * put in "max", "avg" etc.
01435          */
01436 
01437         if (curcol->column_namelen == 0) {
01438             strcpy(curcol->column_name, tds_pr_op(curcol->column_operator));
01439             curcol->column_namelen = strlen(curcol->column_name);
01440         }
01441 
01442         /*  User defined data type of the column */
01443         curcol->column_usertype = tds_get_int(tds);
01444 
01445         tds_set_column_type(tds, curcol, tds_get_byte(tds));
01446 
01447         switch (curcol->column_varint_size) {
01448         case 4:
01449             curcol->column_size = tds_get_int(tds);
01450             break;
01451         case 2:
01452             curcol->column_size = tds_get_smallint(tds);
01453             break;
01454         case 1:
01455             curcol->column_size = tds_get_byte(tds);
01456             break;
01457         case 0:
01458             break;
01459         }
01460         tdsdump_log(TDS_DBG_INFO1, "processing result. column_size %d\n", curcol->column_size);
01461 
01462         /* Adjust column size according to client's encoding */
01463         curcol->on_server.column_size = curcol->column_size;
01464         /* TODO check if this column can have collation information associated */
01465         adjust_character_column_size(tds, curcol);
01466 
01467         /* skip locale */
01468         if (!IS_TDS42(tds))
01469             tds_get_n(tds, NULL, tds_get_byte(tds));
01470 
01471         tds_add_row_column_size(info, curcol);
01472     }
01473 
01474     by_cols = tds_get_byte(tds);
01475 
01476     tdsdump_log(TDS_DBG_INFO1, "processing tds compute result. by_cols = %d\n", by_cols);
01477 
01478     if (by_cols) {
01479         if ((info->bycolumns = (TDS_SMALLINT *) malloc(by_cols * sizeof(TDS_SMALLINT))) == NULL)
01480             return TDS_FAIL;
01481 
01482         memset(info->bycolumns, '\0', by_cols * sizeof(TDS_SMALLINT));
01483     }
01484     info->by_cols = by_cols;
01485 
01486     cur_by_col = info->bycolumns;
01487     for (col = 0; col < by_cols; col++) {
01488         *cur_by_col = tds_get_byte(tds);
01489         cur_by_col++;
01490     }
01491 
01492     if ((info->current_row = tds_alloc_compute_row(info)) != NULL)
01493         return TDS_SUCCEED;
01494     else
01495         return TDS_FAIL;
01496 }
01497 
01498 /**
01499  * Read data information from wire
01500  * \param tds state information for the socket and the TDS protocol
01501  * \param curcol column where to store information
01502  */
01503 static int
01504 tds7_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol)
01505 {
01506     int colnamelen;
01507 
01508     CHECK_TDS_EXTRA(tds);
01509     CHECK_COLUMN_EXTRA(curcol);
01510 
01511     /*  User defined data type of the column */
01512     curcol->column_usertype = tds_get_smallint(tds);
01513 
01514     curcol->column_flags = tds_get_smallint(tds);   /*  Flags */
01515 
01516     curcol->column_nullable = curcol->column_flags & 0x01;
01517     curcol->column_writeable = (curcol->column_flags & 0x08) > 0;
01518     curcol->column_identity = (curcol->column_flags & 0x10) > 0;
01519 
01520     tds_set_column_type(tds, curcol, tds_get_byte(tds)); /* sets "cardinal" type */
01521 
01522     curcol->column_timestamp = (curcol->column_type == SYBBINARY && curcol->column_usertype == TDS_UT_TIMESTAMP);
01523 
01524     switch (curcol->column_varint_size) {
01525     case 4:
01526         curcol->column_size = tds_get_int(tds);
01527         break;
01528     case 2:
01529         curcol->column_size = tds_get_smallint(tds);
01530         break;
01531     case 1:
01532         curcol->column_size = tds_get_byte(tds);
01533         break;
01534     case 0:
01535         break;
01536     }
01537 
01538     /* Adjust column size according to client's encoding */
01539     curcol->on_server.column_size = curcol->column_size;
01540 
01541     /* numeric and decimal have extra info */
01542     if (is_numeric_type(curcol->column_type)) {
01543         curcol->column_prec = tds_get_byte(tds);    /* precision */
01544         curcol->column_scale = tds_get_byte(tds);   /* scale */
01545         /* FIXME check prec/scale, don't let server crash us */
01546     }
01547 
01548     if (IS_TDS80(tds) && is_collate_type(curcol->on_server.column_type)) {
01549         /* based on true type as sent by server */
01550         /*
01551          * first 2 bytes are windows code (such as 0x409 for english)
01552          * other 2 bytes ???
01553          * last bytes is id in syscharsets
01554          */
01555         tds_get_n(tds, curcol->column_collation, 5);
01556         curcol->char_conv =
01557             tds_iconv_from_collate(tds, curcol->column_collation[4],
01558                            curcol->column_collation[1] * 256 + curcol->column_collation[0]);
01559     }
01560 
01561     /* NOTE adjustements must be done after curcol->char_conv initialization */
01562     adjust_character_column_size(tds, curcol);
01563 
01564     if (is_blob_type(curcol->column_type)) {
01565         curcol->table_namelen =
01566             tds_get_string(tds, tds_get_smallint(tds), curcol->table_name, sizeof(curcol->table_name) - 1);
01567     }
01568 
01569     /*
01570      * under 7.0 lengths are number of characters not
01571      * number of bytes...tds_get_string handles this
01572      */
01573     colnamelen = tds_get_string(tds, tds_get_byte(tds), curcol->column_name, sizeof(curcol->column_name) - 1);
01574     curcol->column_name[colnamelen] = 0;
01575     curcol->column_namelen = colnamelen;
01576 
01577     tdsdump_log(TDS_DBG_INFO1, "tds7_get_data_info: \n"
01578             "\tcolname = %s (%d bytes)\n"
01579             "\ttype = %d (%s)\n"
01580             "\tserver's type = %d (%s)\n"
01581             "\tcolumn_varint_size = %d\n"
01582             "\tcolumn_size = %d (%d on server)\n",
01583             curcol->column_name, curcol->column_namelen,
01584             curcol->column_type, tds_prtype(curcol->column_type),
01585             curcol->on_server.column_type, tds_prtype(curcol->on_server.column_type),
01586             curcol->column_varint_size,
01587             curcol->column_size, curcol->on_server.column_size);
01588 
01589     CHECK_COLUMN_EXTRA(curcol);
01590 
01591     return TDS_SUCCEED;
01592 }
01593 
01594 /**
01595  * tds7_process_result() is the TDS 7.0 result set processing routine.  It
01596  * is responsible for populating the tds->res_info structure.
01597  * This is a TDS 7.0 only function
01598  */
01599 static int
01600 tds7_process_result(TDSSOCKET * tds)
01601 {
01602     int col, num_cols;
01603     TDSCOLUMN *curcol;
01604     TDSRESULTINFO *info;
01605 
01606     CHECK_TDS_EXTRA(tds);
01607     tdsdump_log(TDS_DBG_INFO1, "processing TDS7 result metadata.\n");
01608 
01609     /* read number of columns and allocate the columns structure */
01610 
01611     num_cols = tds_get_smallint(tds);
01612 
01613     /* This can be a DUMMY results token from a cursor fetch */
01614 
01615     if (num_cols == -1) {
01616         tdsdump_log(TDS_DBG_INFO1, "no meta data\n");
01617         return TDS_SUCCEED;
01618     }
01619 
01620     tds_free_all_results(tds);
01621     tds->rows_affected = TDS_NO_COUNT;
01622 
01623     if ((info = tds_alloc_results(num_cols)) == NULL)
01624         return TDS_FAIL;
01625     tds->current_results = info;
01626     if (tds->cur_cursor) {
01627         tds->cur_cursor->res_info = info;
01628         tdsdump_log(TDS_DBG_INFO1, "set current_results to cursor->res_info\n");
01629     } else {
01630         tds->res_info = info;
01631         tdsdump_log(TDS_DBG_INFO1, "set current_results (%d column%s) to tds->res_info\n", num_cols, (num_cols==1? "":"s"));
01632     }
01633 
01634 
01635     /*
01636      * loop through the columns populating COLINFO struct from
01637      * server response
01638      */
01639     for (col = 0; col < num_cols; col++) {
01640 
01641         curcol = info->columns[col];
01642 
01643         tdsdump_log(TDS_DBG_INFO1, "setting up column %d\n", col);
01644         tds7_get_data_info(tds, curcol);
01645 
01646         tds_add_row_column_size(info, curcol);
01647     }
01648 
01649     /* all done now allocate a row for tds_process_row to use */
01650     if ((info->current_row = tds_alloc_row(info)) != NULL) {
01651         CHECK_TDS_EXTRA(tds);
01652         return TDS_SUCCEED;
01653     } else {
01654         CHECK_TDS_EXTRA(tds);
01655         return TDS_FAIL;
01656     }
01657 }
01658 
01659 /**
01660  * Read data information from wire
01661  * \param tds state information for the socket and the TDS protocol
01662  * \param curcol column where to store information
01663  */
01664 static int
01665 tds_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int is_param)
01666 {
01667     CHECK_TDS_EXTRA(tds);
01668     CHECK_COLUMN_EXTRA(curcol);
01669 
01670     curcol->column_namelen = tds_get_string(tds, tds_get_byte(tds), curcol->column_name, sizeof(curcol->column_name) - 1);
01671     curcol->column_name[curcol->column_namelen] = '\0';
01672 
01673     curcol->column_flags = tds_get_byte(tds);   /*  Flags */
01674     if (!is_param) {
01675         /* TODO check if all flags are the same for all TDS versions */
01676         if (IS_TDS50(tds))
01677             curcol->column_hidden = curcol->column_flags & 0x1;
01678         curcol->column_key = (curcol->column_flags & 0x2) > 1;
01679         curcol->column_writeable = (curcol->column_flags & 0x10) > 1;
01680         curcol->column_nullable = (curcol->column_flags & 0x20) > 1;
01681         curcol->column_identity = (curcol->column_flags & 0x40) > 1;
01682     }
01683 
01684     curcol->column_usertype = tds_get_int(tds);
01685     tds_set_column_type(tds, curcol, tds_get_byte(tds));
01686 
01687     tdsdump_log(TDS_DBG_INFO1, "processing result. type = %d(%s), varint_size %d\n",
01688             curcol->column_type, tds_prtype(curcol->column_type), curcol->column_varint_size);
01689     switch (curcol->column_varint_size) {
01690     case 4:
01691         curcol->column_size = tds_get_int(tds);
01692         /* Only read table_name for blob columns (eg. not for SYBLONGBINARY) */
01693         if (is_blob_type (curcol->column_type)) {
01694             curcol->table_namelen =
01695                 tds_get_string(tds, tds_get_smallint(tds), curcol->table_name, sizeof(curcol->table_name) - 1);
01696         }
01697         break;
01698     case 2:
01699         /* assure > 0 */
01700         curcol->column_size = tds_get_smallint(tds);
01701         break;
01702     case 1:
01703         curcol->column_size = tds_get_byte(tds);
01704         break;
01705     case 0:
01706         break;
01707     }
01708     tdsdump_log(TDS_DBG_INFO1, "processing result. column_size %d\n", curcol->column_size);
01709 
01710     /* numeric and decimal have extra info */
01711     if (is_numeric_type(curcol->column_type)) {
01712         curcol->column_prec = tds_get_byte(tds);    /* precision */
01713         curcol->column_scale = tds_get_byte(tds);   /* scale */
01714         /* FIXME check prec/scale, don't let server crash us */
01715     }
01716 
01717     /* read sql collation info */
01718     /* TODO: we should use it ! */
01719     if (IS_TDS80(tds) && is_collate_type(curcol->on_server.column_type)) {
01720         tds_get_n(tds, curcol->column_collation, 5);
01721         curcol->char_conv =
01722             tds_iconv_from_collate(tds, curcol->column_collation[4],
01723                            curcol->column_collation[1] * 256 + curcol->column_collation[0]);
01724     }
01725 
01726     /* Adjust column size according to client's encoding */
01727     curcol->on_server.column_size = curcol->column_size;
01728     adjust_character_column_size(tds, curcol);
01729 
01730     return TDS_SUCCEED;
01731 }
01732 
01733 /**
01734  * tds_process_result() is the TDS 5.0 result set processing routine.  It
01735  * is responsible for populating the tds->res_info structure.
01736  * This is a TDS 5.0 only function
01737  */
01738 static int
01739 tds_process_result(TDSSOCKET * tds)
01740 {
01741     int hdrsize;
01742     int col, num_cols;
01743     TDSCOLUMN *curcol;
01744     TDSRESULTINFO *info;
01745 
01746     CHECK_TDS_EXTRA(tds);
01747 
01748     tds_free_all_results(tds);
01749     tds->rows_affected = TDS_NO_COUNT;
01750 
01751     hdrsize = tds_get_smallint(tds);
01752 
01753     /* read number of columns and allocate the columns structure */
01754     num_cols = tds_get_smallint(tds);
01755 
01756     if ((info = tds_alloc_results(num_cols)) == NULL)
01757         return TDS_FAIL;
01758     tds->current_results = info;
01759     if (tds->cur_cursor) {
01760         tds->cur_cursor->res_info = info;
01761     } else {
01762         tds->res_info = info;
01763     }
01764 
01765     /*
01766      * loop through the columns populating COLINFO struct from
01767      * server response
01768      */
01769     for (col = 0; col < info->num_cols; col++) {
01770         curcol = info->columns[col];
01771 
01772         tds_get_data_info(tds, curcol, 0);
01773 
01774         /* skip locale information */
01775         /* NOTE do not put into tds_get_data_info, param do not have locale information */
01776         tds_get_n(tds, NULL, tds_get_byte(tds));
01777 
01778         tds_add_row_column_size(info, curcol);
01779     }
01780     if ((info->current_row = tds_alloc_row(info)) != NULL)
01781         return TDS_SUCCEED;
01782     else
01783         return TDS_FAIL;
01784 }
01785 
01786 /**
01787  * tds5_process_result() is the new TDS 5.0 result set processing routine.
01788  * It is responsible for populating the tds->res_info structure.
01789  * This is a TDS 5.0 only function
01790  */
01791 static int
01792 tds5_process_result(TDSSOCKET * tds)
01793 {
01794     int hdrsize;
01795 
01796     int colnamelen;
01797     int col, num_cols;
01798     TDSCOLUMN *curcol;
01799     TDSRESULTINFO *info;
01800 
01801     CHECK_TDS_EXTRA(tds);
01802 
01803     tdsdump_log(TDS_DBG_INFO1, "tds5_process_result\n");
01804 
01805     /*
01806      * free previous resultset
01807      */
01808     tds_free_all_results(tds);
01809     tds->rows_affected = TDS_NO_COUNT;
01810 
01811     /*
01812      * read length of packet (4 bytes)
01813      */
01814     hdrsize = tds_get_int(tds);
01815 
01816     /* read number of columns and allocate the columns structure */
01817     num_cols = tds_get_smallint(tds);
01818 
01819     if ((info = tds_alloc_results(num_cols)) == NULL)
01820         return TDS_FAIL;
01821     tds->current_results = info;
01822     if (tds->cur_cursor) {
01823         tds->cur_cursor->res_info = info;
01824     }
01825     else
01826         tds->res_info = info;
01827 
01828     tdsdump_log(TDS_DBG_INFO1, "num_cols=%d\n", num_cols);
01829 
01830     /* TODO reuse some code... */
01831     /*
01832      * loop through the columns populating COLINFO struct from
01833      * server response
01834      */
01835     for (col = 0; col < info->num_cols; col++) {
01836         curcol = info->columns[col];
01837 
01838         /* label */
01839         curcol->column_namelen =
01840             tds_get_string(tds, tds_get_byte(tds), curcol->column_name, sizeof(curcol->column_name) - 1);
01841         curcol->column_name[curcol->column_namelen] = '\0';
01842 
01843         /* TODO save informations somewhere */
01844         /* database */
01845         colnamelen = tds_get_byte(tds);
01846         tds_get_n(tds, NULL, colnamelen);
01847         /*
01848          * tds_get_n(tds, curcol->catalog_name, colnamelen);
01849          * curcol->catalog_name[colnamelen] = '\0';
01850          */
01851 
01852         /* owner */
01853         colnamelen = tds_get_byte(tds);
01854         tds_get_n(tds, NULL, colnamelen);
01855         /*
01856          * tds_get_n(tds, curcol->schema_name, colnamelen);
01857          * curcol->schema_name[colnamelen] = '\0';
01858          */
01859 
01860         /* table */
01861         curcol->table_namelen =
01862             tds_get_string(tds, tds_get_byte(tds), curcol->table_name, sizeof(curcol->table_name) - 1);
01863         curcol->table_name[curcol->table_namelen] = '\0';
01864 
01865         /* column name */
01866         colnamelen = tds_get_byte(tds);
01867         if (colnamelen != 0) {
01868             if (curcol->column_namelen == 0) {
01869                 tds_get_string(tds, colnamelen, curcol->column_name, sizeof(curcol->column_name) - 1);
01870                 curcol->column_name[colnamelen] = '\0';
01871                 curcol->column_namelen = colnamelen;
01872             }
01873             else {
01874                 tds_get_n(tds, NULL, colnamelen);
01875             }
01876         }
01877         /*
01878         if (curcol->table_column_name)
01879             TDS_ZERO_FREE(curcol->table_column_name);
01880         tds_alloc_get_string(tds, &curcol->table_column_name, tds_get_byte(tds));
01881         */
01882 
01883         /* if label is empty, use the table column name */
01884         /*
01885         if (!curcol->column_namelen && curcol->table_column_name) {
01886             tds_strlcpy(curcol->column_name, curcol->table_column_name, sizeof(curcol->column_name));
01887             curcol->column_namelen = strlen(curcol->column_name);
01888         }
01889         */
01890 
01891         /* flags (4 bytes) */
01892         curcol->column_flags = tds_get_int(tds);
01893         curcol->column_hidden = curcol->column_flags & 0x1;
01894         curcol->column_key = (curcol->column_flags & 0x2) > 1;
01895         curcol->column_writeable = (curcol->column_flags & 0x10) > 1;
01896         curcol->column_nullable = (curcol->column_flags & 0x20) > 1;
01897         curcol->column_identity = (curcol->column_flags & 0x40) > 1;
01898 
01899         curcol->column_usertype = tds_get_int(tds);
01900 
01901         tds_set_column_type(tds, curcol, tds_get_byte(tds));
01902 
01903         switch (curcol->column_varint_size) {
01904         case 4:
01905             if (curcol->column_type == SYBTEXT || curcol->column_type == SYBIMAGE) {
01906                 int namelen;
01907 
01908                 curcol->column_size = tds_get_int(tds);
01909 
01910                 /* skip name */
01911                 namelen = tds_get_smallint(tds);
01912                 if (namelen)
01913                     tds_get_n(tds, NULL, namelen);
01914 
01915             } else {
01916                 curcol->column_size = tds_get_int(tds);
01917             }
01918             break;
01919         case 2:
01920             curcol->column_size = tds_get_smallint(tds);
01921             break;
01922         case 1:
01923             curcol->column_size = tds_get_byte(tds);
01924             break;
01925         case 0:
01926             curcol->column_size = tds_get_size_by_type(curcol->column_type);
01927             break;
01928         }
01929 
01930         /* numeric and decimal have extra info */
01931         if (is_numeric_type(curcol->column_type)) {
01932             curcol->column_prec = tds_get_byte(tds);    /* precision */
01933             curcol->column_scale = tds_get_byte(tds);   /* scale */
01934             /* FIXME check prec/scale, don't let server crash us */
01935         }
01936 
01937         /* Adjust column size according to client's encoding */
01938         curcol->on_server.column_size = curcol->column_size;
01939         adjust_character_column_size(tds, curcol);
01940 
01941         /* discard Locale */
01942         tds_get_n(tds, NULL, tds_get_byte(tds));
01943 
01944         tds_add_row_column_size(info, curcol);
01945 
01946         /*
01947          *  Dump all information on this column
01948          */
01949         tdsdump_log(TDS_DBG_INFO1, "col %d:\n", col);
01950         tdsdump_log(TDS_DBG_INFO1, "tcolumn_label=[%s]\n", curcol->column_name);
01951 /*
01952         tdsdump_log(TDS_DBG_INFO1, "\tcolumn_name=[%s]\n", curcol->column_colname);
01953         tdsdump_log(TDS_DBG_INFO1, "\tcatalog=[%s] schema=[%s] table=[%s]\n",
01954                 curcol->catalog_name, curcol->schema_name, curcol->table_name, curcol->column_colname);
01955 */
01956         tdsdump_log(TDS_DBG_INFO1, "\tflags=%x utype=%d type=%d varint=%d\n",
01957                 curcol->column_flags, curcol->column_usertype, curcol->column_type, curcol->column_varint_size);
01958 
01959         tdsdump_log(TDS_DBG_INFO1, "\tcolsize=%d prec=%d scale=%d\n",
01960                 curcol->column_size, curcol->column_prec, curcol->column_scale);
01961     }
01962     if ((info->current_row = tds_alloc_row(info)) != NULL)
01963         return TDS_SUCCEED;
01964     else
01965         return TDS_FAIL;
01966 }
01967 
01968 /**
01969  * tds_process_compute() processes compute rows and places them in the row
01970  * buffer.
01971  */
01972 static int
01973 tds_process_compute(TDSSOCKET * tds, TDS_INT * computeid)
01974 {
01975     int i;
01976     TDSCOLUMN *curcol;
01977     TDSCOMPUTEINFO *info;
01978     TDS_INT compute_id;
01979 
01980     CHECK_TDS_EXTRA(tds);
01981 
01982     compute_id = tds_get_smallint(tds);
01983 
01984     for (i = 0;; ++i) {
01985         if (i >= tds->num_comp_info)
01986             return TDS_FAIL;
01987         info = tds->comp_info[i];
01988         if (info->computeid == compute_id)
01989             break;
01990     }
01991     tds->current_results = info;
01992 
01993     for (i = 0; i < info->num_cols; i++) {
01994         curcol = info->columns[i];
01995         if (tds_get_data(tds, curcol, info->current_row, i) != TDS_SUCCEED)
01996             return TDS_FAIL;
01997     }
01998     if (computeid)
01999         *computeid = compute_id;
02000     return TDS_SUCCEED;
02001 }
02002 
02003 
02004 static int
02005 tds_process_compute_095(TDSSOCKET * tds, TDS_INT * pcomputeid)
02006 {
02007     int i;
02008     TDSCOLUMN *curcol;
02009     TDSCOMPUTEINFO *info;
02010     TDS_INT id;
02011 
02012     CHECK_TDS_EXTRA(tds);
02013 
02014     id = tds_get_smallint(tds);
02015 
02016     tdsdump_log(TDS_DBG_INFO1, "tds_process_compute() found compute id %d\n", id);
02017 
02018     for (i = 0;; ++i) {
02019         if (i >= tds->num_comp_info) {
02020             tdsdump_log(TDS_DBG_INFO1, "tds_process_compute() FAIL: id exceeds bound (%d)\n", tds->num_comp_info);
02021             return TDS_FAIL;
02022         }
02023         info = tds->comp_info[i];
02024         if (info->computeid == id)
02025             break;
02026     }
02027     tds->current_results = info;
02028 
02029     for (i = 0; i < info->num_cols; i++) {
02030         curcol = info->columns[i];
02031         if (tds_get_data(tds, curcol, info->current_row, i) != TDS_SUCCEED) {
02032             tdsdump_log(TDS_DBG_INFO1, "tds_process_compute() FAIL: tds_get_data() failed\n");
02033             return TDS_FAIL;
02034         }
02035     }
02036     if (pcomputeid)
02037         *pcomputeid = id;
02038     return TDS_SUCCEED;
02039 }
02040 
02041 
02042 /**
02043  * Read a data from wire
02044  * \param tds state information for the socket and the TDS protocol
02045  * \param curcol column where store column information
02046  * \param current_row pointer to row data to store information
02047  * \param i column position in current_row
02048  * \return TDS_FAIL on error or TDS_SUCCEED
02049  */
02050 static int
02051 tds_get_data(TDSSOCKET * tds, TDSCOLUMN * curcol, unsigned char *current_row, int i)
02052 {
02053     unsigned char *dest;
02054     int len, colsize;
02055     int fillchar;
02056     TDSBLOB *blob = NULL;
02057 
02058     CHECK_TDS_EXTRA(tds);
02059     CHECK_COLUMN_EXTRA(curcol);
02060 
02061     tdsdump_log(TDS_DBG_INFO1, "tds_get_data: type %d, varint size %d\n", curcol->column_type, curcol->column_varint_size);
02062     switch (curcol->column_varint_size) {
02063     case 4:
02064         /*
02065          * TODO finish
02066          * This strange type has following structure
02067          * 0 len (int32) -- NULL
02068          * len (int32), type (int8), data -- ints, date, etc
02069          * len (int32), type (int8), 7 (int8), collation, column size (int16) -- [n]char, [n]varchar, binary, varbinary
02070          * BLOBS (text/image) not supported
02071          */
02072         if (curcol->column_type == SYBVARIANT) {
02073             colsize = tds_get_int(tds);
02074             tds_get_n(tds, NULL, colsize);
02075             curcol->column_cur_size = -1;
02076             return TDS_SUCCEED;
02077         }
02078 
02079         /* It's a BLOB... */
02080         if (is_blob_type(curcol->column_type)) {
02081             len = tds_get_byte(tds);
02082             blob = (TDSBLOB *) & (current_row[curcol->column_offset]);
02083             if (len == 16) {    /*  Jeff's hack */
02084                 tds_get_n(tds, blob->textptr, 16);
02085                 tds_get_n(tds, blob->timestamp, 8);
02086                 colsize = tds_get_int(tds);
02087             } else {
02088                 colsize = -1;
02089             }
02090             break;
02091         }
02092 
02093         /* Any other type (XSYBCHAR and alike) */
02094         colsize = tds_get_int(tds);
02095         if (colsize == 0)
02096             colsize = -1;
02097         break;
02098     case 2:
02099         colsize = tds_get_smallint(tds);
02100         break;
02101     case 1:
02102         colsize = tds_get_byte(tds);
02103         if (colsize == 0)
02104             colsize = -1;
02105         break;
02106     case 0:
02107         /* TODO this should be column_size */
02108         colsize = tds_get_size_by_type(curcol->column_type);
02109         break;
02110     default:
02111         colsize = -1;
02112         break;
02113     }
02114     if (IS_TDSDEAD(tds))
02115         return TDS_FAIL;
02116 
02117     tdsdump_log(TDS_DBG_INFO1, "processing row.  column size is %d \n", colsize);
02118     /* set NULL flag in the row buffer */
02119     if (colsize < 0) {
02120         curcol->column_cur_size = -1;
02121         return TDS_SUCCEED;
02122     }
02123 
02124     /*
02125      * We're now set to read the data from the wire.  For varying types (e.g. char/varchar)
02126      * make sure that curcol->column_cur_size reflects the size of the read data,
02127      * after any charset conversion.  tds_get_char_data() does that for you,
02128      * but of course tds_get_n() doesn't.
02129      *
02130      * colsize == wire_size, bytes to read
02131      * curcol->column_cur_size == sizeof destination buffer, room to write
02132      */
02133     dest = &(current_row[curcol->column_offset]);
02134     if (is_numeric_type(curcol->column_type)) {
02135         /*
02136          * Handling NUMERIC datatypes:
02137          * Since these can be passed around independent
02138          * of the original column they came from, we embed the TDS_NUMERIC datatype in the row buffer
02139          * instead of using the wire representation, even though it uses a few more bytes.
02140          */
02141         TDS_NUMERIC *num = (TDS_NUMERIC *) dest;
02142         memset(num, '\0', sizeof(TDS_NUMERIC));
02143         /* TODO perhaps it would be fine to change format ?? */
02144         num->precision = curcol->column_prec;
02145         num->scale = curcol->column_scale;
02146 
02147         /* server is going to crash freetds ?? */
02148         /* TODO close connection it server try to do so ?? */
02149         if (colsize > sizeof(num->array))
02150             return TDS_FAIL;
02151         tds_get_n(tds, num->array, colsize);
02152 
02153         /* corrected colsize for column_cur_size */
02154         colsize = sizeof(TDS_NUMERIC);
02155         if (IS_TDS7_PLUS(tds)) {
02156             tdsdump_log(TDS_DBG_INFO1, "swapping numeric data...\n");
02157             tds_swap_numeric(num);
02158         }
02159         curcol->column_cur_size = colsize;
02160     } else if (is_blob_type(curcol->column_type)) {
02161         TDS_CHAR *p;
02162         int new_blob_size;
02163         assert(blob == (TDSBLOB *) dest);   /* cf. column_varint_size case 4, above */
02164 
02165         /*
02166          * Blobs don't use a column's fixed buffer because the official maximum size is 2 GB.
02167          * Instead, they're reallocated as necessary, based on the data's size.
02168          * Here we allocate memory, if need be.
02169          */
02170         /* TODO this can lead to a big waste of memory */
02171         new_blob_size = determine_adjusted_size(curcol->char_conv, colsize);
02172         if (new_blob_size == 0) {
02173             curcol->column_cur_size = 0;
02174             if (blob->textvalue)
02175                 TDS_ZERO_FREE(blob->textvalue);
02176             return TDS_SUCCEED;
02177         }
02178 
02179         /* NOTE we use an extra pointer (p) to avoid lose of memory in the case realloc fails */
02180         p = blob->textvalue;
02181         if (!p) {
02182             p = (TDS_CHAR *) malloc(new_blob_size);
02183         } else {
02184             /* TODO perhaps we should store allocated bytes too ? */
02185             if (new_blob_size > curcol->column_cur_size ||  (curcol->column_cur_size - new_blob_size) > 10240) {
02186                 p = (TDS_CHAR *) realloc(p, new_blob_size);
02187             }
02188         }
02189 
02190         if (!p)
02191             return TDS_FAIL;
02192         blob->textvalue = p;
02193         curcol->column_cur_size = new_blob_size;
02194 
02195         /* read the data */
02196         if (is_char_type(curcol->column_type)) {
02197             if (tds_get_char_data(tds, (char *) blob, colsize, curcol) == TDS_FAIL)
02198                 return TDS_FAIL;
02199         } else {
02200             assert(colsize == new_blob_size);
02201             tds_get_n(tds, blob->textvalue, colsize);
02202         }
02203     } else {        /* non-numeric and non-blob */
02204         curcol->column_cur_size = colsize;
02205 
02206         if (curcol->char_conv) {
02207             if (tds_get_char_data(tds, (char *) dest, colsize, curcol) == TDS_FAIL)
02208                 return TDS_FAIL;
02209         } else {
02210             /*
02211              * special case, some servers seem to return more data in some conditions
02212              * (ASA 7 returning 4 byte nullable integer)
02213              */
02214             int discard_len = 0;
02215             if (colsize > curcol->column_size) {
02216                 discard_len = colsize - curcol->column_size;
02217                 colsize = curcol->column_size;
02218             }
02219             if (tds_get_n(tds, dest, colsize) == NULL)
02220                 return TDS_FAIL;
02221             if (discard_len > 0)
02222                 tds_get_n(tds, NULL, discard_len);
02223             curcol->column_cur_size = colsize;
02224         }
02225 
02226         /* pad (UNI)CHAR and BINARY types */
02227         fillchar = 0;
02228         switch (curcol->column_type) {
02229         /* extra handling for SYBLONGBINARY */
02230         case SYBLONGBINARY:
02231             if (curcol->column_usertype != USER_UNICHAR_TYPE)
02232                 break;
02233         case SYBCHAR:
02234         case XSYBCHAR:
02235             if (curcol->column_size != curcol->on_server.column_size)
02236                 break;
02237             /* FIXME use client charset */
02238             fillchar = ' ';
02239         case SYBBINARY:
02240         case XSYBBINARY:
02241             if (colsize < curcol->column_size)
02242                 memset(dest + colsize, fillchar, curcol->column_size - colsize);
02243             colsize = curcol->column_size;
02244             break;
02245         }
02246 
02247         if (curcol->column_type == SYBDATETIME4) {
02248             tdsdump_log(TDS_DBG_INFO1, "datetime4 %d %d %d %d\n", dest[0], dest[1], dest[2], dest[3]);
02249         }
02250     }
02251 
02252 #ifdef WORDS_BIGENDIAN
02253     /*
02254      * MS SQL Server 7.0 has broken date types from big endian
02255      * machines, this swaps the low and high halves of the
02256      * affected datatypes
02257      *
02258      * Thought - this might be because we don't have the
02259      * right flags set on login.  -mjs
02260      *
02261      * Nope its an actual MS SQL bug -bsb
02262      */
02263     /* TODO test on login, remove configuration -- freddy77 */
02264     if (tds->broken_dates &&
02265         (curcol->column_type == SYBDATETIME ||
02266          curcol->column_type == SYBDATETIME4 ||
02267          curcol->column_type == SYBDATETIMN ||
02268          curcol->column_type == SYBMONEY ||
02269          curcol->column_type == SYBMONEY4 || (curcol->column_type == SYBMONEYN && curcol->column_size > 4)))
02270         /*
02271          * above line changed -- don't want this for 4 byte SYBMONEYN
02272          * values (mlilback, 11/7/01)
02273          */
02274     {
02275         unsigned char temp_buf[8];
02276 
02277         memcpy(temp_buf, dest, colsize / 2);
02278         memcpy(dest, &dest[colsize / 2], colsize / 2);
02279         memcpy(&dest[colsize / 2], temp_buf, colsize / 2);
02280     }
02281     if (tds->emul_little_endian) {
02282         tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n", tds_get_conversion_type(curcol->column_type, colsize));
02283         tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), dest);
02284     }
02285 #endif
02286     return TDS_SUCCEED;
02287 }
02288 
02289 /**
02290  * tds_process_row() processes rows and places them in the row buffer.
02291  */
02292 static int
02293 tds_process_row(TDSSOCKET * tds)
02294 {
02295     int i;
02296     TDSCOLUMN *curcol;
02297     TDSRESULTINFO *info;
02298 
02299     CHECK_TDS_EXTRA(tds);
02300 
02301     info = tds->current_results;
02302     if (!info)
02303         return TDS_FAIL;
02304 
02305     assert(info->num_cols > 0);
02306 
02307     info->row_count++;
02308     for (i = 0; i < info->num_cols; i++) {
02309         tdsdump_log(TDS_DBG_INFO1, "tds_process_row(): reading column %d \n", i);
02310         curcol = info->columns[i];
02311         if (tds_get_data(tds, curcol, info->current_row, i) != TDS_SUCCEED)
02312             return TDS_FAIL;
02313     }
02314     return TDS_SUCCEED;
02315 }
02316 
02317 /**
02318  * tds_process_end() processes any of the DONE, DONEPROC, or DONEINPROC
02319  * tokens.
02320  * \param tds        state information for the socket and the TDS protocol
02321  * \param marker     TDS token number
02322  * \param flags_parm filled with bit flags (see TDS_DONE_ constants).
02323  *        Is NULL nothing is returned
02324  */
02325 static int
02326 tds_process_end(TDSSOCKET * tds, int marker, int *flags_parm)
02327 {
02328     int more_results, was_cancelled, error, done_count_valid;
02329     int tmp, state;
02330 
02331     CHECK_TDS_EXTRA(tds);
02332 
02333     tmp = tds_get_smallint(tds);
02334 
02335     state = tds_get_smallint(tds);
02336 
02337     more_results = (tmp & TDS_DONE_MORE_RESULTS) != 0;
02338     was_cancelled = (tmp & TDS_DONE_CANCELLED) != 0;
02339     error = (tmp & TDS_DONE_ERROR) != 0;
02340     done_count_valid = (tmp & TDS_DONE_COUNT) != 0;
02341 
02342 
02343     tdsdump_log(TDS_DBG_FUNC, "tds_process_end: more_results = %d\n"
02344             "\t\twas_cancelled = %d\n"
02345             "\t\terror = %d\n"
02346             "\t\tdone_count_valid = %d\n", more_results, was_cancelled, error, done_count_valid);
02347 
02348     if (tds->res_info) {
02349         tds->res_info->more_results = more_results;
02350         if (tds->current_results == NULL)
02351             tds->current_results = tds->res_info;
02352 
02353     }
02354 
02355     if (flags_parm)
02356         *flags_parm = tmp;
02357 
02358     if (was_cancelled || (!more_results && !tds->in_cancel)) {
02359         tdsdump_log(TDS_DBG_FUNC, "tds_process_end() state set to TDS_IDLE\n");
02360         /* reset of in_cancel should must done before setting IDLE */
02361         tds->in_cancel = 0;
02362         tds_set_state(tds, TDS_IDLE);
02363         if (!tds->query_timeout  &&  tds->save_query_timeout)
02364             tds->query_timeout = tds->save_query_timeout;
02365     }
02366 
02367     if (IS_TDSDEAD(tds))
02368         return TDS_FAIL;
02369 
02370     /*
02371      * rows affected is in the tds struct because a query may affect rows but
02372      * have no result set.
02373      */
02374 
02375     if (done_count_valid) {
02376         tds->rows_affected = tds_get_int(tds);
02377         tdsdump_log(TDS_DBG_FUNC, "                rows_affected = %d\n", tds->rows_affected);
02378     } else {
02379         tmp = tds_get_int(tds); /* throw it away */
02380         tds->rows_affected = TDS_NO_COUNT;
02381     }
02382 
02383     if (IS_TDSDEAD(tds))
02384         return TDS_FAIL;
02385 
02386     return TDS_SUCCEED;
02387 }
02388 
02389 
02390 
02391 /**
02392  * tds_client_msg() sends a message to the client application from the CLI or
02393  * TDS layer. A client message is one that is generated from with the library
02394  * and not from the server.  The message is sent to the CLI (the
02395  * err_handler) so that it may forward it to the client application or
02396  * discard it if no msg handler has been by the application. tds->parent
02397  * contains a void pointer to the parent of the tds socket. This can be cast
02398  * back into DBPROCESS or CS_CONNECTION by the CLI and used to determine the
02399  * proper recipient function for this message.
02400  * \todo This procedure is deprecated, because the client libraries use differing messages and message numbers.
02401  *  The general approach is to emit ct-lib error information and let db-lib and ODBC map that to their number and text.
02402  */
02403 int
02404 tds_client_msg(const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, int msgno, int severity, int state, int line, const char *msg_text)
02405 {
02406     int ret;
02407     TDSMESSAGE msg;
02408 
02409     CHECK_CONTEXT_EXTRA(tds_ctx);
02410     if (tds)
02411         CHECK_TDS_EXTRA(tds);
02412 
02413     if (tds_ctx->err_handler) {
02414         memset(&msg, 0, sizeof(TDSMESSAGE));
02415         msg.msgno = msgno;
02416         msg.severity = severity;
02417         msg.state = state;
02418         /* TODO is possible to avoid copy of strings ? */
02419         msg.server = strdup("OpenClient");
02420         msg.line_number = line;
02421         msg.message = strdup(msg_text);
02422         if (msg.sql_state == NULL)
02423             msg.sql_state = tds_alloc_client_sqlstate(msg.msgno);
02424         ret = tds_ctx->err_handler(tds_ctx, tds, &msg);
02425         tds_free_msg(&msg);
02426 #if 1
02427         /*
02428          * error handler may return:
02429          * INT_EXIT -- Print an error message, and exit application, . returning an error to the OS.
02430          * INT_CANCEL -- Return FAIL to the db-lib function that caused the error.
02431          * For SQLETIME errors only, call dbcancel() to try to cancel the current command batch
02432          *  and flush any pending results. Break the connection if dbcancel() times out,
02433          * INT_CONTINUE -- For SQLETIME, wait for one additional time-out period, then call the error handler again.
02434          *      Else treat as INT_CANCEL.
02435          */
02436 #else
02437         /*
02438          * This was bogus afaict.
02439          * Definitely, it's a mistake to set the state to TDS_DEAD for information messages when the handler
02440          * returns INT_CANCEL, at least according to Microsoft's documentation.
02441          * --jkl
02442          */
02443         /*
02444          * message handler returned FAIL/CS_FAIL
02445          * mark socket as dead
02446          */
02447         if (ret && tds) {
02448             /* TODO close socket too ?? */
02449             tds_set_state(tds, TDS_DEAD);
02450         }
02451 #endif
02452     }
02453 
02454     tdsdump_log(TDS_DBG_FUNC, "tds_client_msg: #%d: \"%s\".  Connection state is now %d.  \n", msgno, msg_text, tds ? (int)tds->state : -1);
02455 
02456     return 0;
02457 }
02458 
02459 /**
02460  * tds_process_env_chg()
02461  * when ever certain things change on the server, such as database, character
02462  * set, language, or block size.  A environment change message is generated
02463  * There is no action taken currently, but certain functions at the CLI level
02464  * that return the name of the current database will need to use this.
02465  */
02466 static int
02467 tds_process_env_chg(TDSSOCKET * tds)
02468 {
02469     int size, type;
02470     char *oldval = NULL;
02471     char *newval = NULL;
02472     char **dest;
02473     int new_block_size;
02474     int lcid;
02475     int memrc = 0;
02476 
02477     CHECK_TDS_EXTRA(tds);
02478 
02479     size = tds_get_smallint(tds);
02480     /*
02481      * this came in a patch, apparently someone saw an env message
02482      * that was different from what we are handling? -- brian
02483      * changed back because it won't handle multibyte chars -- 7.0
02484      */
02485     /* tds_get_n(tds,NULL,size); */
02486 
02487     type = tds_get_byte(tds);
02488 
02489     /*
02490      * handle collate default change (if you change db or during login)
02491      * this environment is not a string so need different handles
02492      */
02493     if (type == TDS_ENV_SQLCOLLATION) {
02494         /* save new collation */
02495         size = tds_get_byte(tds);
02496         memset(tds->collation, 0, 5);
02497         if (size < 5) {
02498             tds_get_n(tds, tds->collation, size);
02499         } else {
02500             tds_get_n(tds, tds->collation, 5);
02501             tds_get_n(tds, NULL, size - 5);
02502             lcid = (tds->collation[0] + ((int) tds->collation[1] << 8) + ((int) tds->collation[2] << 16)) & 0xffffflu;
02503             tds7_srv_charset_changed(tds, tds->collation[4], lcid);
02504         }
02505         /* discard old one */
02506         tds_get_n(tds, NULL, tds_get_byte(tds));
02507         return TDS_SUCCEED;
02508     }
02509 
02510     /* fetch the new value */
02511     memrc += tds_alloc_get_string(tds, &newval, tds_get_byte(tds));
02512 
02513     /* fetch the old value */
02514     memrc += tds_alloc_get_string(tds, &oldval, tds_get_byte(tds));
02515 
02516     if (memrc != 0) {
02517         if (newval != NULL)
02518             free(newval);
02519         if (oldval != NULL)
02520             free(oldval);
02521         return TDS_FAIL;
02522     }
02523 
02524     dest = NULL;
02525     switch (type) {
02526     case TDS_ENV_PACKSIZE:
02527         new_block_size = atoi(newval);
02528         if (new_block_size > tds->env.block_size) {
02529             tdsdump_log(TDS_DBG_INFO1, "increasing block size from %s to %d\n", oldval, new_block_size);
02530             /*
02531              * I'm not aware of any way to shrink the
02532              * block size but if it is possible, we don't
02533              * handle it.
02534              */
02535             /* Reallocate buffer if impossible (strange values from server or out of memory) use older buffer */
02536             tds_realloc_socket(tds, new_block_size);
02537         }
02538         break;
02539     case TDS_ENV_DATABASE:
02540         dest = &tds->env.database;
02541         break;
02542     case TDS_ENV_LANG:
02543         dest = &tds->env.language;
02544         break;
02545     case TDS_ENV_CHARSET:
02546         dest = &tds->env.charset;
02547         tds_srv_charset_changed(tds, newval);
02548         break;
02549     }
02550     if (tds->env_chg_func) {
02551         (*(tds->env_chg_func)) (tds, type, oldval, newval);
02552     }
02553 
02554     if (oldval)
02555         free(oldval);
02556     if (newval) {
02557         if (dest) {
02558             if (*dest)
02559                 free(*dest);
02560             *dest = newval;
02561         } else
02562             free(newval);
02563     }
02564 
02565     return TDS_SUCCEED;
02566 }
02567 
02568 /**
02569  * tds_process_msg() is called for MSG, ERR, or EED tokens and is responsible
02570  * for calling the CLI's message handling routine
02571  * returns TDS_SUCCEED if informational, TDS_ERROR if error.
02572  */
02573 static int
02574 tds_process_msg(TDSSOCKET * tds, int marker)
02575 {
02576     int rc;
02577     int len;
02578     int len_sqlstate;
02579     int has_eed = 0;
02580     TDSMESSAGE msg;
02581 
02582     CHECK_TDS_EXTRA(tds);
02583 
02584     /* make sure message has been freed */
02585     memset(&msg, 0, sizeof(TDSMESSAGE));
02586 
02587     /* packet length */
02588     len = tds_get_smallint(tds);
02589 
02590     /* message number */
02591     rc = tds_get_int(tds);
02592     msg.msgno = rc;
02593 
02594     /* msg state */
02595     msg.state = tds_get_byte(tds);
02596 
02597     /* msg level */
02598     msg.severity = tds_get_byte(tds);
02599 
02600     /* determine if msg or error */
02601     switch (marker) {
02602     case TDS_EED_TOKEN:
02603         if (msg.severity <= 10)
02604             msg.priv_msg_type = 0;
02605         else
02606             msg.priv_msg_type = 1;
02607 
02608         /* read SQL state */
02609         len_sqlstate = tds_get_byte(tds);
02610         msg.sql_state = (char *) malloc(len_sqlstate + 1);
02611         if (!msg.sql_state) {
02612             tds_free_msg(&msg);
02613             return TDS_FAIL;
02614         }
02615 
02616         tds_get_n(tds, msg.sql_state, len_sqlstate);
02617         msg.sql_state[len_sqlstate] = '\0';
02618 
02619         /* do a better mapping using native errors */
02620         if (strcmp(msg.sql_state, "ZZZZZ") == 0)
02621             TDS_ZERO_FREE(msg.sql_state);
02622 
02623         /* if has_eed = 1, extended error data follows */
02624         has_eed = tds_get_byte(tds);
02625 
02626         /* junk status and transaction state */
02627         tds_get_smallint(tds);
02628         break;
02629     case TDS_INFO_TOKEN:
02630         msg.priv_msg_type = 0;
02631         break;
02632     case TDS_ERROR_TOKEN:
02633         msg.priv_msg_type = 1;
02634         break;
02635     default:
02636         tdsdump_log(TDS_DBG_ERROR, "tds_process_msg() called with unknown marker '%d'!\n", (int) marker);
02637         tds_free_msg(&msg);
02638         return TDS_FAIL;
02639     }
02640 
02641     tdsdump_log(TDS_DBG_ERROR, "tds_process_msg() reading message from server\n");
02642 
02643     rc = 0;
02644     /* the message */
02645     rc += tds_alloc_get_string(tds, &msg.message, tds_get_smallint(tds));
02646 
02647     /* server name */
02648     rc += tds_alloc_get_string(tds, &msg.server, tds_get_byte(tds));
02649 
02650     /* stored proc name if available */
02651     rc += tds_alloc_get_string(tds, &msg.proc_name, tds_get_byte(tds));
02652 
02653     /* line number in the sql statement where the problem occured */
02654     msg.line_number = tds_get_smallint(tds);
02655 
02656     /*
02657      * If the server doesen't provide an sqlstate, map one via server native errors
02658      * I'm assuming there is not a protocol I'm missing to fetch these from the server?
02659      * I know sybase has an sqlstate column in it's sysmessages table, mssql doesn't and
02660      * TDS_EED_TOKEN is not being called for me.
02661      */
02662     if (msg.sql_state == NULL)
02663         msg.sql_state = tds_alloc_lookup_sqlstate(tds, msg.msgno);
02664 
02665 
02666     /* In case extended error data is sent, we just try to discard it */
02667     if (has_eed == 1) {
02668         int next_marker;
02669         for (;;) {
02670             switch (next_marker = tds_get_byte(tds)) {
02671             case TDS5_PARAMFMT_TOKEN:
02672             case TDS5_PARAMFMT2_TOKEN:
02673             case TDS5_PARAMS_TOKEN:
02674                 if (tds_process_default_tokens(tds, next_marker) != TDS_SUCCEED)
02675                     ++rc;
02676                 continue;
02677             }
02678             break;
02679         }
02680         tds_unget_byte(tds);
02681     }
02682 
02683     /*
02684      * call the msg_handler that was set by an upper layer
02685      * (dblib, ctlib or some other one).  Call it with the pointer to
02686      * the "parent" structure.
02687      */
02688 
02689     if (rc != 0) {
02690         tds_free_msg(&msg);
02691         return TDS_ERROR;
02692     }
02693 
02694     /* special case, */
02695     if (marker == TDS_EED_TOKEN && tds->cur_dyn && !TDS_IS_MSSQL(tds) && msg.msgno == 2782) {
02696         /* we must emulate prepare */
02697         tds->cur_dyn->emulated = 1;
02698     /* 0.95
02699     } else if (marker == TDS_INFO_TOKEN && msg.msgno == 16954 && TDS_IS_MSSQL(tds)
02700            && tds->internal_sp_called == TDS_SP_CURSOROPEN && tds->cur_cursor) {
02701     */
02702         /* here mssql say "Executing SQL directly; no cursor." opening cursor */
02703     } else {
02704         /* EED can be followed to PARAMFMT/PARAMS, do not store it in dynamic */
02705         tds->cur_dyn = NULL;
02706 
02707         if (tds->tds_ctx->msg_handler) {
02708             tdsdump_log(TDS_DBG_ERROR, "tds_process_msg() calling client msg handler\n");
02709             tds->tds_ctx->msg_handler(tds->tds_ctx, tds, &msg);
02710         } else if (msg.msgno) {
02711             tdsdump_log(TDS_DBG_WARN,
02712                     "Msg %d, Severity %d, State %d, Server %s, Line %d\n%s\n",
02713                     msg.msgno,
02714                     msg.severity ,
02715                     msg.state, msg.server, msg.line_number, msg.message);
02716         }
02717     }
02718 
02719     tds_free_msg(&msg);
02720 
02721     tdsdump_log(TDS_DBG_ERROR, "tds_process_msg() returning TDS_SUCCEED\n");
02722 
02723     return TDS_SUCCEED;
02724 }
02725 
02726 /**
02727  * Read a string from wire in a new allocated buffer
02728  * \param tds state information for the socket and the TDS protocol
02729  * \param len length of string to read
02730  */
02731 static int
02732 tds_alloc_get_string(TDSSOCKET * tds, char **string, int len)
02733 {
02734     char *s;
02735     int out_len;
02736 
02737     CHECK_TDS_EXTRA(tds);
02738 
02739     if (len < 0) {
02740         *string = NULL;
02741         return 0;
02742     }
02743 
02744     /* assure sufficient space for every conversion */
02745     s = (char *) malloc(len * 4 + 1);
02746     out_len = tds_get_string(tds, len, s, len * 4);
02747     if (!s) {
02748         *string = NULL;
02749         return -1;
02750     }
02751     s = realloc(s, out_len + 1);
02752     s[out_len] = '\0';
02753     *string = s;
02754     return 0;
02755 }
02756 
02757 /**
02758  * tds_process_cancel() processes the incoming token stream until it finds
02759  * an end token (DONE, DONEPROC, DONEINPROC) with the cancel flag set.
02760  * a that point the connetion should be ready to handle a new query.
02761  */
02762 int
02763 tds_process_cancel(TDSSOCKET * tds)
02764 {
02765     CHECK_TDS_EXTRA(tds);
02766 
02767     /* silly cases, nothing to do */
02768     if (!tds->in_cancel)
02769         return TDS_SUCCEED;
02770     /* TODO handle cancellation sending data */
02771     if (tds->state != TDS_PENDING)
02772         return TDS_SUCCEED;
02773 
02774     tds->query_start_time = 0;
02775     tds->query_timeout = 0;
02776 
02777     /* TODO support TDS5 cancel, wait for cancel packet first, then wait for done */
02778     for (;;) {
02779         TDS_INT result_type;
02780 
02781         switch (tds_process_tokens(tds, &result_type, NULL, 0)) {
02782         case TDS_FAIL:
02783             return TDS_FAIL;
02784         case TDS_CANCELLED:
02785         case TDS_SUCCEED:
02786         case TDS_NO_MORE_RESULTS:
02787             return TDS_SUCCEED;
02788         }
02789     }
02790 }
02791 
02792 /**
02793  * Find a dynamic given string id
02794  * \return dynamic or NULL is not found
02795  * \param tds  state information for the socket and the TDS protocol
02796  * \param id   dynamic id to search
02797  */
02798 TDSDYNAMIC *
02799 tds_lookup_dynamic(TDSSOCKET * tds, char *id)
02800 {
02801     TDSDYNAMIC *curr;
02802 
02803     CHECK_TDS_EXTRA(tds);
02804 
02805     for (curr = tds->dyns; curr != NULL; curr = curr->next) {
02806         if (!strcmp(curr->id, id))
02807             return curr;
02808     }
02809     return NULL;
02810 }
02811 
02812 /**
02813  * tds_process_dynamic()
02814  * finds the element of the dyns array for the id
02815  */
02816 static TDSDYNAMIC *
02817 tds_process_dynamic(TDSSOCKET * tds)
02818 {
02819     int token_sz;
02820     unsigned char type, status;
02821     int id_len;
02822     char id[TDS_MAX_DYNID_LEN + 1];
02823     int drain = 0;
02824 
02825     CHECK_TDS_EXTRA(tds);
02826 
02827     token_sz = tds_get_smallint(tds);
02828     type = tds_get_byte(tds);
02829     status = tds_get_byte(tds);
02830     /* handle only acknowledge */
02831     if (type != 0x20) {
02832         tdsdump_log(TDS_DBG_ERROR, "Unrecognized TDS5_DYN type %x\n", type);
02833         tds_get_n(tds, NULL, token_sz - 2);
02834         return NULL;
02835     }
02836     id_len = tds_get_byte(tds);
02837     if (id_len > TDS_MAX_DYNID_LEN) {
02838         drain = id_len - TDS_MAX_DYNID_LEN;
02839         id_len = TDS_MAX_DYNID_LEN;
02840     }
02841     id_len = tds_get_string(tds, id_len, id, TDS_MAX_DYNID_LEN);
02842     id[id_len] = '\0';
02843     if (drain) {
02844         tds_get_string(tds, drain, NULL, drain);
02845     }
02846     return tds_lookup_dynamic(tds, id);
02847 }
02848 
02849 static int
02850 tds_process_dyn_result(TDSSOCKET * tds)
02851 {
02852     int hdrsize;
02853     int col, num_cols;
02854     TDSCOLUMN *curcol;
02855     TDSPARAMINFO *info;
02856     TDSDYNAMIC *dyn;
02857 
02858     CHECK_TDS_EXTRA(tds);
02859 
02860     hdrsize = tds_get_smallint(tds);
02861     num_cols = tds_get_smallint(tds);
02862 
02863     if (tds->cur_dyn) {
02864         dyn = tds->cur_dyn;
02865         tds_free_param_results(dyn->res_info);
02866         /* read number of columns and allocate the columns structure */
02867         if ((dyn->res_info = tds_alloc_results(num_cols)) == NULL)
02868             return TDS_FAIL;
02869         info = dyn->res_info;
02870     } else {
02871         tds_free_param_results(tds->param_info);
02872         if ((tds->param_info = tds_alloc_results(num_cols)) == NULL)
02873             return TDS_FAIL;
02874         info = tds->param_info;
02875     }
02876     tds->current_results = info;
02877 
02878     for (col = 0; col < info->num_cols; col++) {
02879         curcol = info->columns[col];
02880 
02881         tds_get_data_info(tds, curcol, 1);
02882 
02883         /* skip locale information */
02884         tds_get_n(tds, NULL, tds_get_byte(tds));
02885 
02886         tds_add_row_column_size(info, curcol);
02887     }
02888 
02889     if ((info->current_row = tds_alloc_row(info)) != NULL)
02890         return TDS_SUCCEED;
02891     else
02892         return TDS_FAIL;
02893 }
02894 
02895 /**
02896  *  New TDS 5.0 token for describing output parameters
02897  */
02898 static int
02899 tds5_process_dyn_result2(TDSSOCKET * tds)
02900 {
02901     int hdrsize;
02902     int col, num_cols;
02903     TDSCOLUMN *curcol;
02904     TDSPARAMINFO *info;
02905     TDSDYNAMIC *dyn;
02906 
02907     CHECK_TDS_EXTRA(tds);
02908 
02909     hdrsize = tds_get_int(tds);
02910     num_cols = tds_get_smallint(tds);
02911 
02912     if (tds->cur_dyn) {
02913         dyn = tds->cur_dyn;
02914         tds_free_param_results(dyn->res_info);
02915         /* read number of columns and allocate the columns structure */
02916         if ((dyn->res_info = tds_alloc_results(num_cols)) == NULL)
02917             return TDS_FAIL;
02918         info = dyn->res_info;
02919     } else {
02920         tds_free_param_results(tds->param_info);
02921         if ((tds->param_info = tds_alloc_results(num_cols)) == NULL)
02922             return TDS_FAIL;
02923         info = tds->param_info;
02924     }
02925     tds->current_results = info;
02926 
02927     for (col = 0; col < info->num_cols; col++) {
02928         curcol = info->columns[col];
02929 
02930         /* TODO reuse tds_get_data_info code, sligthly different */
02931 
02932         /* column name */
02933         curcol->column_namelen =
02934             tds_get_string(tds, tds_get_byte(tds), curcol->column_name, sizeof(curcol->column_name) - 1);
02935         curcol->column_name[curcol->column_namelen] = '\0';
02936 
02937         /* column status */
02938         curcol->column_flags = tds_get_int(tds);
02939         curcol->column_nullable = (curcol->column_flags & 0x20) > 0;
02940 
02941         /* user type */
02942         curcol->column_usertype = tds_get_int(tds);
02943 
02944         /* column type */
02945         tds_set_column_type(tds, curcol, tds_get_byte(tds));
02946 
02947         /* FIXME this should be done by tds_set_column_type */
02948         curcol->column_varint_size = tds5_get_varint_size(curcol->column_type);
02949         /* column size */
02950         switch (curcol->column_varint_size) {
02951         case 4:
02952             if (curcol->column_type == SYBTEXT || curcol->column_type == SYBIMAGE) {
02953                 curcol->column_size = tds_get_int(tds);
02954                 /* read table name */
02955                 curcol->table_namelen =
02956                     tds_get_string(tds, tds_get_smallint(tds), curcol->table_name,
02957                                sizeof(curcol->table_name) - 1);
02958             } else {
02959                 curcol->column_size = tds_get_int(tds);
02960                 /*tdsdump_log(TDS_DBG_INFO1, "UNHANDLED TYPE %x\n", curcol->column_type);*/
02961             }
02962             break;
02963         case 2:
02964             curcol->column_size = tds_get_smallint(tds);
02965             break;
02966         case 1:
02967             curcol->column_size = tds_get_byte(tds);
02968             break;
02969         case 0:
02970             break;
02971         }
02972 
02973         /* numeric and decimal have extra info */
02974         if (is_numeric_type(curcol->column_type)) {
02975             curcol->column_prec = tds_get_byte(tds);    /* precision */
02976             curcol->column_scale = tds_get_byte(tds);   /* scale */
02977             /* FIXME check prec/scale, don't let server crash us */
02978         }
02979 
02980         /* Adjust column size according to client's encoding */
02981         curcol->on_server.column_size = curcol->column_size;
02982         adjust_character_column_size(tds, curcol);
02983 
02984         /* discard Locale */
02985         tds_get_n(tds, NULL, tds_get_byte(tds));
02986 
02987         tds_add_row_column_size(info, curcol);
02988 
02989         tdsdump_log(TDS_DBG_INFO1, "elem %d:\n", col);
02990         tdsdump_log(TDS_DBG_INFO1, "\tcolumn_name=[%s]\n", curcol->column_name);
02991         tdsdump_log(TDS_DBG_INFO1, "\tflags=%x utype=%d type=%d varint=%d\n",
02992                 curcol->column_flags, curcol->column_usertype, curcol->column_type, curcol->column_varint_size);
02993         tdsdump_log(TDS_DBG_INFO1, "\tcolsize=%d prec=%d scale=%d\n",
02994                 curcol->column_size, curcol->column_prec, curcol->column_scale);
02995     }
02996 
02997     if ((info->current_row = tds_alloc_row(info)) != NULL)
02998         return TDS_SUCCEED;
02999     else
03000         return TDS_FAIL;
03001 }
03002 
03003 /**
03004  * tds_get_token_size() returns the size of a fixed length token
03005  * used by tds_process_cancel() to determine how to read past a token
03006  */
03007 int
03008 tds_get_token_size(int marker)
03009 {
03010     /* TODO finish */
03011     switch (marker) {
03012     case TDS_DONE_TOKEN:
03013     case TDS_DONEPROC_TOKEN:
03014     case TDS_DONEINPROC_TOKEN:
03015         return 8;
03016     case TDS_RETURNSTATUS_TOKEN:
03017         return 4;
03018     case TDS_PROCID_TOKEN:
03019         return 8;
03020     default:
03021         return 0;
03022     }
03023 }
03024 
03025 void
03026 tds_swap_datatype(int coltype, unsigned char *buf)
03027 {
03028     switch (coltype) {
03029     case SYBINT2:
03030         tds_swap_bytes(buf, 2);
03031         break;
03032     case SYBINT4:
03033     case SYBMONEY4:
03034     case SYBREAL:
03035         tds_swap_bytes(buf, 4);
03036         break;
03037     case SYBINT8:
03038     case SYBFLT8:
03039         tds_swap_bytes(buf, 8);
03040         break;
03041     case SYBMONEY:
03042     case SYBDATETIME:
03043         tds_swap_bytes(buf, 4);
03044         tds_swap_bytes(&buf[4], 4);
03045         break;
03046     case SYBDATETIME4:
03047         tds_swap_bytes(buf, 2);
03048         tds_swap_bytes(&buf[2], 2);
03049         break;
03050     case SYBUNIQUE:
03051         tds_swap_bytes(buf, 4);
03052         tds_swap_bytes(&buf[4], 2);
03053         tds_swap_bytes(&buf[6], 2);
03054         break;
03055     }
03056 }
03057 
03058 void
03059 tds_swap_numeric(TDS_NUMERIC *num)
03060 {
03061     /* swap the sign */
03062     num->array[0] = (num->array[0] == 0) ? 1 : 0;
03063     /* swap the data */
03064     tds_swap_bytes(&(num->array[1]), tds_numeric_bytes_per_prec[num->precision] - 1);
03065 }
03066 
03067 
03068 
03069 /**
03070  * tds5_get_varint_size5() returns the size of a variable length integer
03071  * returned in a TDS 5.1 result string
03072  */
03073 /* TODO can we use tds_get_varint_size ?? */
03074 static int
03075 tds5_get_varint_size(int datatype)
03076 {
03077     switch (datatype) {
03078     case SYBLONGBINARY:
03079     case XSYBCHAR:
03080     case SYBTEXT:
03081     case SYBNTEXT:
03082     case SYBIMAGE:
03083     case SYBVARIANT:
03084         return 4;
03085 
03086     case SYBVOID:
03087     case SYBINT1:
03088     case SYBBIT:
03089     case SYBINT2:
03090     case SYBINT4:
03091     case SYBINT8:
03092     case SYBDATETIME4:
03093     case SYBREAL:
03094     case SYBMONEY:
03095     case SYBDATETIME:
03096     case SYBFLT8:
03097     case SYBMONEY4:
03098     case SYBSINT1:
03099     case SYBUINT2:
03100     case SYBUINT4:
03101     case SYBUINT8:
03102         return 0;
03103 
03104     case XSYBNVARCHAR:
03105     case XSYBVARCHAR:
03106     case XSYBBINARY:
03107     case XSYBVARBINARY:
03108         return 2;
03109 
03110     default:
03111         return 1;
03112     }
03113 }
03114 
03115 /**
03116  * tds_process_compute_names() processes compute result sets.
03117  */
03118 static int
03119 tds_process_compute_names(TDSSOCKET * tds)
03120 {
03121     int hdrsize;
03122     int remainder;
03123     int num_cols = 0;
03124     int col;
03125     int memrc = 0;
03126     TDS_SMALLINT compute_id = 0;
03127     TDS_TINYINT namelen;
03128     TDSCOMPUTEINFO *info;
03129     TDSCOLUMN *curcol;
03130 
03131     struct namelist
03132     {
03133         char name[256];
03134         int namelen;
03135         struct namelist *nextptr;
03136     };
03137 
03138     struct namelist *topptr = NULL;
03139     struct namelist *curptr = NULL;
03140     struct namelist *freeptr = NULL;
03141 
03142     CHECK_TDS_EXTRA(tds);
03143 
03144     hdrsize = tds_get_smallint(tds);
03145     remainder = hdrsize;
03146     tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. remainder = %d\n", remainder);
03147 
03148     /*
03149      * compute statement id which this relates
03150      * to. You can have more than one compute
03151      * statement in a SQL statement
03152      */
03153 
03154     compute_id = tds_get_smallint(tds);
03155     remainder -= 2;
03156 
03157     while (remainder) {
03158         namelen = tds_get_byte(tds);
03159         remainder--;
03160         if (topptr == NULL) {
03161             if ((topptr = (struct namelist *) malloc(sizeof(struct namelist))) == NULL) {
03162                 memrc = -1;
03163                 break;
03164             }
03165             curptr = topptr;
03166             curptr->nextptr = NULL;
03167         } else {
03168             if ((curptr->nextptr = (struct namelist *) malloc(sizeof(struct namelist))) == NULL) {
03169                 memrc = -1;
03170                 break;
03171             }
03172             curptr = curptr->nextptr;
03173             curptr->nextptr = NULL;
03174         }
03175         if (namelen == 0)
03176             strcpy(curptr->name, "");
03177         else {
03178             namelen = tds_get_string(tds, namelen, curptr->name, sizeof(curptr->name) - 1);
03179             curptr->name[namelen] = 0;
03180             remainder -= namelen;
03181         }
03182         curptr->namelen = namelen;
03183         num_cols++;
03184         tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. remainder = %d\n", remainder);
03185     }
03186 
03187     tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. num_cols = %d\n", num_cols);
03188 
03189     if ((tds->comp_info = tds_alloc_compute_results(tds, num_cols, 0)) == NULL)
03190         memrc = -1;
03191 
03192     tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. num_comp_info = %d\n", tds->num_comp_info);
03193 
03194     info = tds->comp_info[tds->num_comp_info - 1];
03195     tds->current_results = info;
03196 
03197     info->computeid = compute_id;
03198 
03199     curptr = topptr;
03200 
03201     if (memrc == 0) {
03202         for (col = 0; col < num_cols; col++) {
03203             curcol = info->columns[col];
03204 
03205             assert(strlen(curcol->column_name) == curcol->column_namelen);
03206             memcpy(curcol->column_name, curptr->name, curptr->namelen + 1);
03207             curcol->column_namelen = curptr->namelen;
03208 
03209             freeptr = curptr;
03210             curptr = curptr->nextptr;
03211             free(freeptr);
03212         }
03213         return TDS_SUCCEED;
03214     } else {
03215         while (curptr != NULL) {
03216             freeptr = curptr;
03217             curptr = curptr->nextptr;
03218             free(freeptr);
03219         }
03220         return TDS_FAIL;
03221     }
03222 }
03223 
03224 
03225 static int
03226 tds_process_compute_names_095(TDSSOCKET * tds)
03227 {
03228     int hdrsize;
03229     int remainder;
03230     int num_cols = 0;
03231     int col;
03232     int memrc = 0;
03233     TDS_SMALLINT compute_id = 0;
03234     TDS_TINYINT namelen;
03235     TDSCOMPUTEINFO *info;
03236     TDSCOLUMN *curcol;
03237 
03238     struct namelist
03239     {
03240         char name[256];
03241         int namelen;
03242         struct namelist *nextptr;
03243     };
03244 
03245     struct namelist *topptr = NULL;
03246     struct namelist *curptr = NULL;
03247     struct namelist *nextptr = NULL;
03248 
03249     CHECK_TDS_EXTRA(tds);
03250 
03251     hdrsize = tds_get_smallint(tds);
03252     remainder = hdrsize;
03253     tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. remainder = %d\n", remainder);
03254 
03255     /*
03256      * compute statement id which this relates
03257      * to. You can have more than one compute
03258      * statement in a SQL statement
03259      */
03260 
03261     compute_id = tds_get_smallint(tds);
03262     remainder -= 2;
03263 
03264     while (remainder > 0) {
03265         namelen = tds_get_byte(tds);
03266         remainder--;
03267         if ((nextptr = (struct namelist *) malloc(sizeof(struct namelist))) == NULL) {
03268             memrc = -1;
03269             break;
03270         }
03271         if (topptr == NULL)
03272             topptr = nextptr;
03273         else
03274             curptr->nextptr = nextptr;
03275         curptr = nextptr;
03276         curptr->nextptr = NULL;
03277         remainder -= namelen;
03278         namelen = tds_get_string(tds, namelen, curptr->name, sizeof(curptr->name) - 1);
03279         curptr->name[namelen] = 0;
03280         curptr->namelen = namelen;
03281         num_cols++;
03282         tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. remainder = %d\n", remainder);
03283     }
03284 
03285     tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. num_cols = %d\n", num_cols);
03286 
03287     if ((tds->comp_info = tds_alloc_compute_results(tds, num_cols, 0)) == NULL)
03288         memrc = -1;
03289 
03290     tdsdump_log(TDS_DBG_INFO1, "processing tds5 compute names. num_comp_info = %d\n", tds->num_comp_info);
03291 
03292     info = tds->comp_info[tds->num_comp_info - 1];
03293     tds->current_results = info;
03294 
03295     info->computeid = compute_id;
03296 
03297     curptr = topptr;
03298 
03299     if (memrc == 0) {
03300         for (col = 0; col < num_cols; col++) {
03301             curcol = info->columns[col];
03302 
03303             assert(strlen(curcol->column_name) == curcol->column_namelen);
03304             memcpy(curcol->column_name, curptr->name, curptr->namelen + 1);
03305             curcol->column_namelen = curptr->namelen;
03306 
03307             nextptr = curptr->nextptr;
03308             free(curptr);
03309             curptr = nextptr;
03310         }
03311         return TDS_SUCCEED;
03312     } else {
03313         while (curptr != NULL) {
03314             nextptr = curptr->nextptr;
03315             free(curptr);
03316             curptr = nextptr;
03317         }
03318         return TDS_FAIL;
03319     }
03320 }
03321 
03322 /**
03323  * tds7_process_compute_result() processes compute result sets for TDS 7/8.
03324  * They is are very  similar to normal result sets.
03325  */
03326 static int
03327 tds7_process_compute_result(TDSSOCKET * tds)
03328 {
03329     int col, num_cols;
03330     TDS_TINYINT by_cols;
03331     TDS_SMALLINT *cur_by_col;
03332     TDS_SMALLINT compute_id;
03333     TDSCOLUMN *curcol;
03334     TDSCOMPUTEINFO *info;
03335 
03336     CHECK_TDS_EXTRA(tds);
03337 
03338     /*
03339      * number of compute columns returned - so
03340      * COMPUTE SUM(x), AVG(x)... would return
03341      * num_cols = 2
03342      */
03343 
03344     num_cols = tds_get_smallint(tds);
03345 
03346     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. num_cols = %d\n", num_cols);
03347 
03348     /*
03349      * compute statement id which this relates
03350      * to. You can have more than one compute
03351      * statement in a SQL statement
03352      */
03353 
03354     compute_id = tds_get_smallint(tds);
03355 
03356     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. compute_id = %d\n", compute_id);
03357     /*
03358      * number of "by" columns in compute - so
03359      * COMPUTE SUM(x) BY a, b, c would return
03360      * by_cols = 3
03361      */
03362 
03363     by_cols = tds_get_byte(tds);
03364     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. by_cols = %d\n", by_cols);
03365 
03366     if ((tds->comp_info = tds_alloc_compute_results(tds, num_cols, by_cols)) == NULL)
03367         return TDS_FAIL;
03368 
03369     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. num_comp_info = %d\n", tds->num_comp_info);
03370 
03371     info = tds->comp_info[tds->num_comp_info - 1];
03372     tds->current_results = info;
03373 
03374     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 0\n");
03375 
03376     info->computeid = compute_id;
03377 
03378     /*
03379      * the by columns are a list of the column
03380      * numbers in the select statement
03381      */
03382 
03383     cur_by_col = info->bycolumns;
03384     for (col = 0; col < by_cols; col++) {
03385         *cur_by_col = tds_get_smallint(tds);
03386         cur_by_col++;
03387     }
03388     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 1\n");
03389 
03390     for (col = 0; col < num_cols; col++) {
03391         tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 2\n");
03392         curcol = info->columns[col];
03393 
03394         curcol->column_operator = tds_get_byte(tds);
03395         curcol->column_operand = tds_get_smallint(tds);
03396 
03397         tds7_get_data_info(tds, curcol);
03398 
03399         if (!curcol->column_namelen) {
03400             strcpy(curcol->column_name, tds_pr_op(curcol->column_operator));
03401             curcol->column_namelen = strlen(curcol->column_name);
03402         }
03403 
03404         tds_add_row_column_size(info, curcol);
03405     }
03406 
03407     /* all done now allocate a row for tds_process_row to use */
03408     tdsdump_log(TDS_DBG_INFO1, "processing tds7 compute result. point 5 \n");
03409     if ((info->current_row = tds_alloc_compute_row(info)) != NULL)
03410         return TDS_SUCCEED;
03411     else
03412         return TDS_FAIL;
03413 }
03414 
03415 static int
03416 tds_process_cursor_tokens(TDSSOCKET * tds)
03417 {
03418     TDS_SMALLINT hdrsize;
03419     TDS_INT rowcount;
03420     TDS_INT cursor_id;
03421     TDS_TINYINT namelen;
03422     unsigned char cursor_cmd;
03423     TDS_SMALLINT cursor_status;
03424     TDSCURSOR *cursor;
03425 
03426     CHECK_TDS_EXTRA(tds);
03427 
03428     hdrsize  = tds_get_smallint(tds);
03429     cursor_id = tds_get_int(tds);
03430     hdrsize  -= sizeof(TDS_INT);
03431     if (cursor_id == 0){
03432         namelen = tds_get_byte(tds);
03433         hdrsize -= 1;
03434         /* discard name */
03435         tds_get_n(tds, NULL, namelen);
03436         hdrsize -= namelen;
03437     }
03438     cursor_cmd    = tds_get_byte(tds);
03439     cursor_status = tds_get_smallint(tds);
03440     hdrsize -= 3;
03441 
03442     if (hdrsize == sizeof(TDS_INT))
03443         rowcount = tds_get_int(tds);
03444 
03445     if (tds->cur_cursor) {
03446         cursor = tds->cur_cursor;
03447         cursor->cursor_id = cursor_id;
03448         cursor->srv_status = cursor_status;
03449         if ((cursor_status & TDS_CUR_ISTAT_DEALLOC) != 0)
03450             tds_free_cursor(tds, cursor);
03451             /* 0.95 tds_cursor_deallocated(tds, cursor); */
03452     }
03453     return TDS_SUCCEED;
03454 }
03455 
03456 static int
03457 tds5_process_optioncmd(TDSSOCKET * tds)
03458 {
03459     TDS_SMALLINT length;
03460     TDS_INT command;
03461     TDS_INT option;
03462     TDS_INT argsize;
03463     TDS_INT arg;
03464 
03465     CHECK_TDS_EXTRA(tds);
03466 
03467     tdsdump_log(TDS_DBG_INFO1, "tds5_process_optioncmd()\n");
03468 
03469     assert(IS_TDS50(tds));
03470 
03471     length = tds_get_smallint(tds);
03472     command = tds_get_byte(tds);
03473     option = tds_get_byte(tds);
03474     argsize = tds_get_byte(tds);
03475 
03476     switch (argsize) {
03477     case 0:
03478         arg = 0;
03479         break;
03480     case 1:
03481         arg = tds_get_byte(tds);
03482         break;
03483     case 4:
03484         arg = tds_get_int(tds);
03485         break;
03486     default:
03487         tdsdump_log(TDS_DBG_INFO1, "oops: cannot process option of size %d\n", argsize);
03488         assert(argsize <= 4);
03489         exit(1);     /* FIXME: stream would become desynchronized */
03490         break;
03491     }
03492     tdsdump_log(TDS_DBG_INFO1, "received option %d value %d\n", option, arg);
03493 
03494     if (command != TDS_OPT_INFO)
03495         return TDS_FAIL;
03496 
03497     tds->option_value = arg;
03498 
03499     return TDS_SUCCEED;
03500 }
03501 
03502 static const char *
03503 tds_pr_op(int op)
03504 {
03505 #define TYPE(con, s) case con: return s; break
03506     switch (op) {
03507         TYPE(SYBAOPAVG, "avg");
03508         TYPE(SYBAOPAVGU, "avg");
03509         TYPE(SYBAOPCNT, "count");
03510         TYPE(SYBAOPCNTU, "count");
03511         TYPE(SYBAOPMAX, "max");
03512         TYPE(SYBAOPMIN, "min");
03513         TYPE(SYBAOPSUM, "sum");
03514         TYPE(SYBAOPSUMU, "sum");
03515         TYPE(SYBAOPCHECKSUM_AGG, "checksum_agg");
03516         TYPE(SYBAOPCNT_BIG, "count");
03517         TYPE(SYBAOPSTDEV, "stdevp");
03518         TYPE(SYBAOPSTDEVP, "stdevp");
03519         TYPE(SYBAOPVAR, "var");
03520         TYPE(SYBAOPVARP, "varp");
03521     default:
03522         break;
03523     }
03524     return "";
03525 #undef TYPE
03526 }
03527 
03528 const char *
03529 tds_prtype(int token)
03530 {
03531 #define TYPE(con, s) case con: return s; break
03532     switch (token) {
03533         TYPE(SYBAOPAVG, "avg");
03534         TYPE(SYBAOPCNT, "count");
03535         TYPE(SYBAOPMAX, "max");
03536         TYPE(SYBAOPMIN, "min");
03537         TYPE(SYBAOPSUM, "sum");
03538 
03539         TYPE(SYBBINARY, "binary");
03540         TYPE(SYBLONGBINARY, "longbinary");
03541         TYPE(SYBBIT, "bit");
03542         TYPE(SYBBITN, "bit-null");
03543         TYPE(SYBCHAR, "char");
03544         TYPE(SYBDATETIME4, "smalldatetime");
03545         TYPE(SYBDATETIME, "datetime");
03546         TYPE(SYBDATETIMN, "datetime-null");
03547         TYPE(SYBDECIMAL, "decimal");
03548         TYPE(SYBFLT8, "float");
03549         TYPE(SYBFLTN, "float-null");
03550         TYPE(SYBIMAGE, "image");
03551         TYPE(SYBINT1, "tinyint");
03552         TYPE(SYBINT2, "smallint");
03553         TYPE(SYBINT4, "int");
03554         TYPE(SYBINT8, "bigint");
03555         TYPE(SYBINTN, "integer-null");
03556         TYPE(SYBMONEY4, "smallmoney");
03557         TYPE(SYBMONEY, "money");
03558         TYPE(SYBMONEYN, "money-null");
03559         TYPE(SYBNTEXT, "UCS-2 text");
03560         TYPE(SYBNVARCHAR, "UCS-2 varchar");
03561         TYPE(SYBNUMERIC, "numeric");
03562         TYPE(SYBREAL, "real");
03563         TYPE(SYBTEXT, "text");
03564         TYPE(SYBUNIQUE, "uniqueidentifier");
03565         TYPE(SYBVARBINARY, "varbinary");
03566         TYPE(SYBVARCHAR, "varchar");
03567         TYPE(SYBVARIANT, "variant");
03568         TYPE(SYBVOID, "void");
03569         TYPE(XSYBBINARY, "xbinary");
03570         TYPE(XSYBCHAR, "xchar");
03571         TYPE(XSYBNCHAR, "x UCS-2 char");
03572         TYPE(XSYBNVARCHAR, "x UCS-2 varchar");
03573         TYPE(XSYBVARBINARY, "xvarbinary");
03574         TYPE(XSYBVARCHAR, "xvarchar");
03575     default:
03576         break;
03577     }
03578     return "";
03579 #undef TYPE
03580 }
03581 
03582 /** \@} */
03583 
03584 static const char *
03585 _tds_token_name(unsigned char marker)
03586 {
03587     switch (marker) {
03588 
03589     case 0x20:
03590         return "TDS5_PARAMFMT2";
03591     case 0x22:
03592         return "ORDERBY2";
03593     case 0x61:
03594         return "ROWFMT2";
03595     case 0x71:
03596         return "LOGOUT";
03597     case 0x79:
03598         return "RETURNSTATUS";
03599     case 0x7C:
03600         return "PROCID";
03601     case 0x81:
03602         return "TDS7_RESULT";
03603     case 0x88:
03604         return "TDS7_COMPUTE_RESULT";
03605     case 0xA0:
03606         return "COLNAME";
03607     case 0xA1:
03608         return "COLFMT";
03609     case 0xA3:
03610         return "DYNAMIC2";
03611     case 0xA4:
03612         return "TABNAME";
03613     case 0xA5:
03614         return "COLINFO";
03615     case 0xA7:
03616         return "COMPUTE_NAMES";
03617     case 0xA8:
03618         return "COMPUTE_RESULT";
03619     case 0xA9:
03620         return "ORDERBY";
03621     case 0xAA:
03622         return "ERROR";
03623     case 0xAB:
03624         return "INFO";
03625     case 0xAC:
03626         return "PARAM";
03627     case 0xAD:
03628         return "LOGINACK";
03629     case 0xAE:
03630         return "CONTROL";
03631     case 0xD1:
03632         return "ROW";
03633     case 0xD3:
03634         return "CMP_ROW";
03635     case 0xD7:
03636         return "TDS5_PARAMS";
03637     case 0xE2:
03638         return "CAPABILITY";
03639     case 0xE3:
03640         return "ENVCHANGE";
03641     case 0xE5:
03642         return "EED";
03643     case 0xE6:
03644         return "DBRPC";
03645     case 0xE7:
03646         return "TDS5_DYNAMIC";
03647     case 0xEC:
03648         return "TDS5_PARAMFMT";
03649     case 0xED:
03650         return "AUTH";
03651     case 0xEE:
03652         return "RESULT";
03653     case 0xFD:
03654         return "DONE";
03655     case 0xFE:
03656         return "DONEPROC";
03657     case 0xFF:
03658         return "DONEINPROC";
03659 
03660     default:
03661         break;
03662     }
03663 
03664     return "";
03665 }
03666 
03667 /**
03668  * Adjust column size according to client's encoding
03669  */
03670 static void
03671 adjust_character_column_size(const TDSSOCKET * tds, TDSCOLUMN * curcol)
03672 {
03673     CHECK_TDS_EXTRA(tds);
03674     CHECK_COLUMN_EXTRA(curcol);
03675 
03676     if (is_unicode_type(curcol->on_server.column_type))
03677         curcol->char_conv = tds->char_convs[client2ucs2];
03678 
03679     /* Sybase UNI(VAR)CHAR fields are transmitted via SYBLONGBINARY and in UTF-16*/
03680     if (curcol->on_server.column_type == SYBLONGBINARY && (
03681         curcol->column_usertype == USER_UNICHAR_TYPE ||
03682         curcol->column_usertype == USER_UNIVARCHAR_TYPE)) {
03683         /* FIXME ucs2 is not UTF-16... */
03684         /* FIXME what happen if client is big endian ?? */
03685         curcol->char_conv = tds->char_convs[client2ucs2];
03686     }
03687 
03688     /* FIXME: and sybase ?? */
03689     if (!curcol->char_conv && IS_TDS7_PLUS(tds) && is_ascii_type(curcol->on_server.column_type))
03690         curcol->char_conv = tds->char_convs[client2server_chardata];
03691 
03692     if (!curcol->char_conv)
03693         return;
03694 
03695     curcol->on_server.column_size = curcol->column_size;
03696     curcol->column_size = determine_adjusted_size(curcol->char_conv, curcol->column_size);
03697 
03698     tdsdump_log(TDS_DBG_INFO1, "adjust_character_column_size:\n"
03699                    "\tServer charset: %s\n"
03700                    "\tServer column_size: %d\n"
03701                    "\tClient charset: %s\n"
03702                    "\tClient column_size: %d\n",
03703                    curcol->char_conv->server_charset.name,
03704                    curcol->on_server.column_size,
03705                    curcol->char_conv->client_charset.name,
03706                    curcol->column_size);
03707 }
03708 
03709 /**
03710  * Allow for maximum possible size of converted data,
03711  * while being careful about integer division truncation.
03712  * All character data pass through iconv.  It doesn't matter if the server side
03713  * is Unicode or not; even Latin1 text need conversion if,
03714  * for example, the client is UTF-8.
03715  */
03716 static int
03717 determine_adjusted_size(const TDSICONV * char_conv, int size)
03718 {
03719     if (!char_conv)
03720         return size;
03721 
03722     /* ssikorsk */
03723     if (char_conv->client_charset.max_bytes_per_char > 1 &&
03724         INT_MAX / char_conv->client_charset.max_bytes_per_char < size
03725         ) {
03726         size = INT_MAX;
03727     } else {
03728         size *= char_conv->client_charset.max_bytes_per_char;
03729     }
03730 
03731     if (size % char_conv->server_charset.min_bytes_per_char
03732         &&  INT_MAX - char_conv->server_charset.min_bytes_per_char >= size)
03733     {
03734         size += char_conv->server_charset.min_bytes_per_char;
03735     }
03736     size /= char_conv->server_charset.min_bytes_per_char;
03737 
03738     return size;
03739 }
03740 
03741 

Generated on Sun Dec 6 22:23:20 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Mon Dec 07 16:20:59 2009 by modify_doxy.py rev. 173732