📄 token.c
字号:
/* FreeTDS - Library of routines accessing Sybase and Microsoft databases * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns * Copyright (C) 2005, 2006, 2007 Frediano Ziglio * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */#if HAVE_CONFIG_H#include <config.h>#endif /* HAVE_CONFIG_H */#if HAVE_STRING_H#include <string.h>#endif /* HAVE_STRING_H */#if HAVE_STDLIB_H#include <stdlib.h>#endif /* HAVE_STDLIB_H */#include <assert.h>#include "tds.h"#include "tdsstring.h"#include "tdsconvert.h"#include "tdsiconv.h"#include "tds_checks.h"#include "replacements.h"#ifdef DMALLOC#include <dmalloc.h>#endifTDS_RCSID(var, "$Id: token.c,v 1.343.2.1 2008/01/27 10:41:23 freddy77 Exp $");static int tds_process_msg(TDSSOCKET * tds, int marker);static int tds_process_compute_result(TDSSOCKET * tds);static int tds_process_compute_names(TDSSOCKET * tds);static int tds7_process_compute_result(TDSSOCKET * tds);static int tds_process_result(TDSSOCKET * tds);static int tds_process_col_name(TDSSOCKET * tds);static int tds_process_col_fmt(TDSSOCKET * tds);static int tds_process_tabname(TDSSOCKET *tds);static int tds_process_colinfo(TDSSOCKET * tds, char **names, int num_names);static int tds_process_compute(TDSSOCKET * tds, TDS_INT * computeid);static int tds_process_cursor_tokens(TDSSOCKET * tds);static int tds_process_row(TDSSOCKET * tds);static int tds_process_param_result(TDSSOCKET * tds, TDSPARAMINFO ** info);static int tds7_process_result(TDSSOCKET * tds);static TDSDYNAMIC *tds_process_dynamic(TDSSOCKET * tds);static int tds_process_auth(TDSSOCKET * tds);static int tds_process_env_chg(TDSSOCKET * tds);static int tds_process_param_result_tokens(TDSSOCKET * tds);static int tds_process_params_result_token(TDSSOCKET * tds);static int tds_process_dyn_result(TDSSOCKET * tds);static int tds5_process_result(TDSSOCKET * tds);static int tds5_process_dyn_result2(TDSSOCKET * tds);static int tds_process_default_tokens(TDSSOCKET * tds, int marker);static int tds5_process_optioncmd(TDSSOCKET * tds);static int tds_process_end(TDSSOCKET * tds, int marker, /*@out@*/ int *flags_parm);static int tds_get_data(TDSSOCKET * tds, TDSCOLUMN * curcol);static int tds_get_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int is_param);static /*@observer@*/ const char *_tds_token_name(unsigned char marker);static void adjust_character_column_size(const TDSSOCKET * tds, TDSCOLUMN * curcol);static int determine_adjusted_size(const TDSICONV * char_conv, int size);static /*@observer@*/ const char *tds_pr_op(int op);static int tds_alloc_get_string(TDSSOCKET * tds, /*@special@*/ char **string, int len) /*allocates *string*/;/** * \ingroup libtds * \defgroup token Results processing * Handle tokens in packets. Many PDU (packets data unit) contain tokens. * (like result description, rows, data, errors and many other). *//** * \addtogroup token * @{ *//** * tds_process_default_tokens() is a catch all function that is called to * process tokens not known to other tds_process_* routines */static inttds_process_default_tokens(TDSSOCKET * tds, int marker){ int tok_size; int done_flags; TDS_INT ret_status; CHECK_TDS_EXTRA(tds); tdsdump_log(TDS_DBG_FUNC, "tds_process_default_tokens() marker is %x(%s)\n", marker, _tds_token_name(marker)); if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_FUNC, "leaving tds_process_default_tokens() connection dead\n"); tds_close_socket(tds); return TDS_FAIL; } switch (marker) { case TDS_AUTH_TOKEN: return tds_process_auth(tds); break; case TDS_ENVCHANGE_TOKEN: return tds_process_env_chg(tds); break; case TDS_DONE_TOKEN: case TDS_DONEPROC_TOKEN: case TDS_DONEINPROC_TOKEN: return tds_process_end(tds, marker, &done_flags); break; case TDS_PROCID_TOKEN: tds_get_n(tds, NULL, 8); break; case TDS_RETURNSTATUS_TOKEN: ret_status = tds_get_int(tds); marker = tds_peek(tds); if (marker != TDS_PARAM_TOKEN && marker != TDS_DONEPROC_TOKEN && marker != TDS_DONE_TOKEN) break; tds->has_status = 1; tds->ret_status = ret_status; tdsdump_log(TDS_DBG_FUNC, "tds_process_default_tokens: return status is %d\n", tds->ret_status); break; case TDS_ERROR_TOKEN: case TDS_INFO_TOKEN: case TDS_EED_TOKEN: return tds_process_msg(tds, marker); break; case TDS_CAPABILITY_TOKEN: /* TODO split two part of capability and use it */ tok_size = tds_get_smallint(tds); /* vicm */ /* * Sybase 11.0 servers return the wrong length in the capability packet, causing use to read * past the done packet. */ if (!TDS_IS_MSSQL(tds) && tds->product_version < TDS_SYB_VER(12, 0, 0)) { unsigned char type, size, *p, *pend; p = tds->capabilities; pend = tds->capabilities + TDS_MAX_CAPABILITY; do { type = tds_get_byte(tds); size = tds_get_byte(tds); if ((p + 2) > pend) break; *p++ = type; *p++ = size; if ((p + size) > pend) break; if (tds_get_n(tds, p, size) == NULL) return TDS_FAIL; } while (type != 2); } else { const int len = tok_size < TDS_MAX_CAPABILITY? tok_size : TDS_MAX_CAPABILITY; if (tds_get_n(tds, tds->capabilities, len) == NULL) return TDS_FAIL; } break; /* PARAM_TOKEN can be returned inserting text in db, to return new timestamp */ case TDS_PARAM_TOKEN: tds_unget_byte(tds); return tds_process_param_result_tokens(tds); break; case TDS7_RESULT_TOKEN: return tds7_process_result(tds); break; case TDS_OPTIONCMD_TOKEN: return tds5_process_optioncmd(tds); break; case TDS_RESULT_TOKEN: return tds_process_result(tds); break; case TDS_ROWFMT2_TOKEN: return tds5_process_result(tds); break; case TDS_COLNAME_TOKEN: return tds_process_col_name(tds); break; case TDS_COLFMT_TOKEN: return tds_process_col_fmt(tds); break; case TDS_ROW_TOKEN: return tds_process_row(tds); break; case TDS5_PARAMFMT_TOKEN: /* store discarded parameters in param_info, not in old dynamic */ tds->cur_dyn = NULL; return tds_process_dyn_result(tds); break; case TDS5_PARAMFMT2_TOKEN: tds->cur_dyn = NULL; return tds5_process_dyn_result2(tds); break; case TDS5_PARAMS_TOKEN: /* save params */ return tds_process_params_result_token(tds); break; case TDS_CURINFO_TOKEN: return tds_process_cursor_tokens(tds); break; case TDS5_DYNAMIC_TOKEN: case TDS_LOGINACK_TOKEN: case TDS_ORDERBY_TOKEN: case TDS_CONTROL_TOKEN: tdsdump_log(TDS_DBG_WARN, "Eating %s token\n", _tds_token_name(marker)); tds_get_n(tds, NULL, tds_get_smallint(tds)); break; case TDS_TABNAME_TOKEN: /* used for FOR BROWSE query */ return tds_process_tabname(tds); break; case TDS_COLINFO_TOKEN: return tds_process_colinfo(tds, NULL, 0); break; case TDS_ORDERBY2_TOKEN: tdsdump_log(TDS_DBG_WARN, "Eating %s token\n", _tds_token_name(marker)); tds_get_n(tds, NULL, tds_get_int(tds)); break; default: /* SYBEBTOK */ tds_close_socket(tds); tdserror(tds->tds_ctx, tds, TDSEBTOK, 0); tdsdump_log(TDS_DBG_ERROR, "Unknown marker: %d(%x)!!\n", marker, (unsigned char) marker); return TDS_FAIL; } return TDS_SUCCEED;}static inttds_set_spid(TDSSOCKET * tds){ TDS_INT result_type; TDS_INT done_flags; TDS_INT rc; TDSCOLUMN *curcol; CHECK_TDS_EXTRA(tds); if (tds_submit_query(tds, "select @@spid") != TDS_SUCCEED) { return TDS_FAIL; } while ((rc = tds_process_tokens(tds, &result_type, &done_flags, TDS_RETURN_ROWFMT|TDS_RETURN_ROW|TDS_RETURN_DONE)) == TDS_SUCCEED) { switch (result_type) { case TDS_ROWFMT_RESULT: if (tds->res_info->num_cols != 1) return TDS_FAIL; break; case TDS_ROW_RESULT: curcol = tds->res_info->columns[0]; if (curcol->column_type == SYBINT2 || (curcol->column_type == SYBINTN && curcol->column_size == 2)) { tds->spid = *((TDS_USMALLINT *) curcol->column_data); } else if (curcol->column_type == SYBINT4 || (curcol->column_type == SYBINTN && curcol->column_size == 4)) { tds->spid = *((TDS_UINT *) curcol->column_data); } else return TDS_FAIL; break; case TDS_DONE_RESULT: if ((done_flags & TDS_DONE_ERROR) != 0) return TDS_FAIL; break; default: break; } } if (rc != TDS_NO_MORE_RESULTS) return TDS_FAIL; return TDS_SUCCEED;}/** * tds_process_login_tokens() is called after sending the login packet * to the server. It returns the success or failure of the login * dependent on the protocol version. 4.2 sends an ACK token only when * successful, TDS 5.0 sends it always with a success byte within */inttds_process_login_tokens(TDSSOCKET * tds){ int succeed = TDS_FAIL; int marker; int len; int memrc = 0; unsigned char major_ver, minor_ver; unsigned char ack; TDS_UINT product_version; CHECK_TDS_EXTRA(tds); tdsdump_log(TDS_DBG_FUNC, "tds_process_login_tokens()\n"); /* get_incoming(tds->s); */ do { marker = tds_get_byte(tds); tdsdump_log(TDS_DBG_FUNC, "looking for login token, got %x(%s)\n", marker, _tds_token_name(marker)); switch (marker) { case TDS_AUTH_TOKEN: tds_process_auth(tds); break; case TDS_LOGINACK_TOKEN: /* TODO function */ len = tds_get_smallint(tds); ack = tds_get_byte(tds); major_ver = tds_get_byte(tds); minor_ver = tds_get_byte(tds); tds_get_n(tds, NULL, 2); /* ignore product name length, see below */ tds_get_byte(tds); product_version = 0; /* get server product name */ /* compute length from packet, some version seem to fill this information wrongly */ len -= 10; free(tds->product_name); if (major_ver >= 7u) { product_version = 0x80000000u; memrc += tds_alloc_get_string(tds, &tds->product_name, len / 2); } else if (major_ver >= 5) { memrc += tds_alloc_get_string(tds, &tds->product_name, len); } else { memrc += tds_alloc_get_string(tds, &tds->product_name, len); if (tds->product_name != NULL && strstr(tds->product_name, "Microsoft") != NULL) product_version = 0x80000000u; } product_version |= ((TDS_UINT) tds_get_byte(tds)) << 24; product_version |= ((TDS_UINT) tds_get_byte(tds)) << 16; product_version |= ((TDS_UINT) tds_get_byte(tds)) << 8; product_version |= tds_get_byte(tds); /* * MSSQL 6.5 and 7.0 seem to return strange values for this * using TDS 4.2, something like 5F 06 32 FF for 6.50 */ if (major_ver == 4 && minor_ver == 2 && (product_version & 0xff0000ffu) == 0x5f0000ffu) product_version = ((product_version & 0xffff00u) | 0x800000u) << 8; tds->product_version = product_version;#ifdef WORDS_BIGENDIAN /* TODO do a best check *//* if (major_ver==7) { tds->broken_dates=1; }*/#endif /* * TDS 5.0 reports 5 on success 6 on failure * TDS 4.2 reports 1 on success and is not * present on failure */ if (ack == 5 || ack == 1) succeed = TDS_SUCCEED; /* authentication is now useless */ if (tds->authentication) { tds->authentication->free(tds, tds->authentication); tds->authentication = NULL; } break; default: if (tds_process_default_tokens(tds, marker) == TDS_FAIL) return TDS_FAIL; break; } } while (marker != TDS_DONE_TOKEN); /* TODO why ?? */ tds->spid = tds->rows_affected; if (tds->spid == 0) { if (tds_set_spid(tds) != TDS_SUCCEED) { tdsdump_log(TDS_DBG_ERROR, "tds_set_spid() failed\n"); succeed = TDS_FAIL; } } tdsdump_log(TDS_DBG_FUNC, "leaving tds_process_login_tokens() returning %d\n", succeed); if (memrc != 0) succeed = TDS_FAIL; return succeed;}static inttds_process_auth(TDSSOCKET * tds){ int pdu_size; CHECK_TDS_EXTRA(tds);#if ENABLE_EXTRA_CHECKS if (!IS_TDS7_PLUS(tds)) tdsdump_log(TDS_DBG_ERROR, "Called auth on TDS version < 7\n");#endif pdu_size = tds_get_smallint(tds); tdsdump_log(TDS_DBG_INFO1, "TDS_AUTH_TOKEN PDU size %d\n", pdu_size); if (!tds->authentication) return TDS_FAIL; return tds->authentication->handle_next(tds, tds->authentication, pdu_size);}/** * process all streams. * tds_process_tokens() is called after submitting a query with * tds_submit_query() and is responsible for calling the routines to * populate tds->res_info if appropriate (some query have no result sets) * @param tds A pointer to the TDSSOCKET structure managing a client/server operation. * @param result_type A pointer to an integer variable which * tds_process_tokens sets to indicate the current type of result. * @par * <b>Values that indicate command status</b> * <table> * <tr><td>TDS_DONE_RESULT</td><td>The results of a command have been completely processed. * This command returned no rows.</td></tr> * <tr><td>TDS_DONEPROC_RESULT</td><td>The results of a command have been completely processed. * This command returned rows.</td></tr> * <tr><td>TDS_DONEINPROC_RESULT</td><td>The results of a command have been completely processed. * This command returned rows.</td></tr> * </table> * <b>Values that indicate results information is available</b> * <table><tr> * <td>TDS_ROWFMT_RESULT</td><td>Regular Data format information</td> * <td>tds->res_info now contains the result details ; tds->current_results now points to that data</td> * </tr><tr> * <td>TDS_COMPUTEFMT_ RESULT</td><td>Compute data format information</td> * <td>tds->comp_info now contains the result data; tds->current_results now points to that data</td> * </tr><tr> * <td>TDS_DESCRIBE_RESULT</td><td></td> * <td></td> * </tr></table> * <b>Values that indicate data is available</b> * <table><tr> * <td><b>Value</b></td><td><b>Meaning</b></td><td><b>Information returned</b></td> * </tr><tr> * <td>TDS_ROW_RESULT</td><td>Regular row results</td> * <td>1 or more rows of regular data can now be retrieved</td> * </tr><tr> * <td>TDS_COMPUTE_RESULT</td><td>Compute row results</td> * <td>A single row of compute data can now be retrieved</td>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -