📄 query.c
字号:
/* FreeTDS - Library of routines accessing Sybase and Microsoft databases * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns * Copyright (C) 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#include <stdarg.h>#include <stdio.h>#if HAVE_STDLIB_H#include <stdlib.h>#endif /* HAVE_STDLIB_H */#if HAVE_STRING_H#include <string.h>#endif /* HAVE_STRING_H */#include <ctype.h>#include "tds.h"#include "tdsiconv.h"#include "tdsconvert.h"#include "tds_checks.h"#include "replacements.h"#ifdef DMALLOC#include <dmalloc.h>#endif#include <assert.h>TDS_RCSID(var, "$Id: query.c,v 1.217.2.1 2008/02/12 15:41:51 freddy77 Exp $");static void tds_put_params(TDSSOCKET * tds, TDSPARAMINFO * info, int flags);static void tds7_put_query_params(TDSSOCKET * tds, const char *query, int query_len);static void tds7_put_params_definition(TDSSOCKET * tds, const char *param_definition, size_t param_length);static int tds_put_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int flags);static int tds_put_data(TDSSOCKET * tds, TDSCOLUMN * curcol);static char *tds7_build_param_def_from_query(TDSSOCKET * tds, const char* converted_query, int converted_query_len, TDSPARAMINFO * params, size_t *out_len);static char *tds7_build_param_def_from_params(TDSSOCKET * tds, const char* query, size_t query_len, TDSPARAMINFO * params, size_t *out_len);static int tds_send_emulated_execute(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params);static const char *tds_skip_comment(const char *s);static int tds_count_placeholders_ucs2le(const char *query, const char *query_end);#define TDS_PUT_DATA_USE_NAME 1#define TDS_PUT_DATA_PREFIX_NAME 2#undef MIN#define MIN(a,b) (((a) < (b)) ? (a) : (b))#undef MAX#define MAX(a,b) (((a) > (b)) ? (a) : (b))/* All manner of client to server submittal functions *//** * \ingroup libtds * \defgroup query Query * Function to handle query. *//** * \addtogroup query * @{ *//** * Accept an ASCII string, convert it to UCS2-LE * The input is null-terminated, but the output excludes the null. * \param buffer buffer where to store output * \param buf string to write * \return bytes written */static inttds_ascii_to_ucs2(char *buffer, const char *buf){ char *s; assert(buffer && buf && *buf); /* This is an internal function. Call it correctly. */ for (s = buffer; *buf != '\0'; ++buf) { *s++ = *buf; *s++ = '\0'; } return s - buffer;}#define TDS_PUT_N_AS_UCS2(tds, s) do { \ char buffer[sizeof(s)*2-2]; \ tds_put_n(tds, buffer, tds_ascii_to_ucs2(buffer, s)); \} while(0)/** * Convert a string in an allocated buffer * \param tds state information for the socket and the TDS protocol * \param char_conv information about the encodings involved * \param s input string * \param len input string length (in bytes), -1 for null terminated * \param out_len returned output length (in bytes) * \return string allocated (or input pointer if no conversion required) or NULL if error */static const char *tds_convert_string(TDSSOCKET * tds, const TDSICONV * char_conv, const char *s, int len, int *out_len){ char *buf; const char *ib; char *ob; size_t il, ol; /* char_conv is only mostly const */ TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress; CHECK_TDS_EXTRA(tds); if (len < 0) len = strlen(s); if (char_conv->flags == TDS_ENCODING_MEMCPY) { *out_len = len; return s; } /* allocate needed buffer (+1 is to exclude 0 case) */ ol = len * char_conv->server_charset.max_bytes_per_char / char_conv->client_charset.min_bytes_per_char + 1; buf = (char *) malloc(ol); if (!buf) return NULL; ib = s; il = len; ob = buf; memset(suppress, 0, sizeof(char_conv->suppress)); if (tds_iconv(tds, char_conv, to_server, &ib, &il, &ob, &ol) == (size_t)-1) { free(buf); return NULL; } *out_len = ob - buf; return buf;}#if ENABLE_EXTRA_CHECKSstatic voidtds_convert_string_free(const char *original, const char *converted){ if (original != converted) free((char *) converted);}#else#define tds_convert_string_free(original, converted) \ do { if (original != converted) free((char*) converted); } while(0)#endifstatic inttds_query_flush_packet(TDSSOCKET *tds){ /* TODO depend on result ?? */ tds_set_state(tds, TDS_PENDING); return tds_flush_packet(tds);}/** * tds_submit_query() sends a language string to the database server for * processing. TDS 4.2 is a plain text message with a packet type of 0x01, * TDS 7.0 is a unicode string with packet type 0x01, and TDS 5.0 uses a * TDS_LANGUAGE_TOKEN to encapsulate the query and a packet type of 0x0f. * \param tds state information for the socket and the TDS protocol * \param query language query to submit * \return TDS_FAIL or TDS_SUCCEED */inttds_submit_query(TDSSOCKET * tds, const char *query){ return tds_submit_query_params(tds, query, NULL);}static char *tds5_fix_dot_query(const char *query, int *query_len, TDSPARAMINFO * params){ int i, pos, l; const char *e, *s; int size = *query_len + 30; char *out = (char *) malloc(size); if (!out) return NULL; pos = 0; s = query; for (i = 0;; ++i) { e = tds_next_placeholder(s); l = e ? e - s : strlen(s); if (pos + l + 12 >= size) { char *p; size = pos + l + 30; p = realloc(out, size); if (!p) { free(out); return NULL; } out = p; } memcpy(out + pos, s, l); pos += l; if (!e) break; pos += sprintf(out + pos, "@P%d", i + 1); if (i >= params->num_cols) { free(out); return NULL; } sprintf(params->columns[i]->column_name, "@P%d", i + 1); params->columns[i]->column_namelen = strlen(params->columns[i]->column_name); s = e + 1; } out[pos] = 0; *query_len = pos; return out;}#ifdef ENABLE_DEVELOPINGstatic const TDS_UCHAR tds9_query_start[] = { /* total length */ 0x16, 0, 0, 0, /* length */ 0x12, 0, 0, 0, /* type */ 0x02, 0, /* transaction */ 0, 0, 0, 0, 0, 0, 0, 0, /* request count */ 1, 0, 0, 0};#define START_QUERY \do { \ if (IS_TDS90(tds)) \ tds_start_query(tds); \} while(0)static voidtds_start_query(TDSSOCKET *tds){ tds_put_n(tds, tds9_query_start, 10); tds_put_n(tds, tds->tds9_transaction, 8); tds_put_n(tds, tds9_query_start + 10 + 8, 4);}#else#define START_QUERY do { ; } while(0)#endif/** * tds_submit_query_params() sends a language string to the database server for * processing. TDS 4.2 is a plain text message with a packet type of 0x01, * TDS 7.0 is a unicode string with packet type 0x01, and TDS 5.0 uses a * TDS_LANGUAGE_TOKEN to encapsulate the query and a packet type of 0x0f. * \param tds state information for the socket and the TDS protocol * \param query language query to submit * \param params parameters of query * \return TDS_FAIL or TDS_SUCCEED */inttds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params){ int query_len; int num_params = params ? params->num_cols : 0; CHECK_TDS_EXTRA(tds); if (params) CHECK_PARAMINFO_EXTRA(params); if (!query) return TDS_FAIL; if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING) return TDS_FAIL; query_len = strlen(query); if (IS_TDS50(tds)) { char *new_query = NULL; /* are there '?' style parameters ? */ if (tds_next_placeholder(query)) { if ((new_query = tds5_fix_dot_query(query, &query_len, params)) == NULL) { tds_set_state(tds, TDS_IDLE); return TDS_FAIL; } query = new_query; } tds->out_flag = TDS_NORMAL; tds_put_byte(tds, TDS_LANGUAGE_TOKEN); /* TODO ICONV use converted size, not input size and convert string */ tds_put_int(tds, query_len + 1); tds_put_byte(tds, params ? 1 : 0); /* 1 if there are params, 0 otherwise */ tds_put_n(tds, query, query_len); if (params) { /* add on parameters */ tds_put_params(tds, params, params->columns[0]->column_name[0] ? TDS_PUT_DATA_USE_NAME : 0); } free(new_query); } else if (!IS_TDS7_PLUS(tds) || !params || !params->num_cols) { tds->out_flag = TDS_QUERY; START_QUERY; tds_put_string(tds, query, query_len); } else { TDSCOLUMN *param; size_t definition_len; int count, i; char *param_definition; int converted_query_len; const char *converted_query; converted_query = tds_convert_string(tds, tds->char_convs[client2ucs2], query, query_len, &converted_query_len); if (!converted_query) { tds_set_state(tds, TDS_IDLE); return TDS_FAIL; } count = tds_count_placeholders_ucs2le(converted_query, converted_query + converted_query_len); if (!count) { param_definition = tds7_build_param_def_from_params(tds, converted_query, converted_query_len, params, &definition_len); if (!param_definition) { tds_convert_string_free(query, converted_query); tds_set_state(tds, TDS_IDLE); return TDS_FAIL; } } else { /* * TODO perhaps functions that calls tds7_build_param_def_from_query * should call also tds7_build_param_def_from_params ?? */ param_definition = tds7_build_param_def_from_query(tds, converted_query, converted_query_len, params, &definition_len); if (!param_definition) { tds_set_state(tds, TDS_IDLE); return TDS_FAIL; } } tds->out_flag = TDS_RPC; START_QUERY; /* procedure name */ if (IS_TDS8_PLUS(tds)) { tds_put_smallint(tds, -1); tds_put_smallint(tds, TDS_SP_EXECUTESQL); } else { tds_put_smallint(tds, 13); TDS_PUT_N_AS_UCS2(tds, "sp_executesql"); } tds_put_smallint(tds, 0); /* string with sql statement */ if (!count) { tds_put_byte(tds, 0); tds_put_byte(tds, 0); tds_put_byte(tds, SYBNTEXT); /* must be Ntype */ tds_put_int(tds, converted_query_len); if (IS_TDS8_PLUS(tds)) tds_put_n(tds, tds->collation, 5); tds_put_int(tds, converted_query_len); tds_put_n(tds, converted_query, converted_query_len); } else { tds7_put_query_params(tds, converted_query, converted_query_len); } tds_convert_string_free(query, converted_query); tds7_put_params_definition(tds, param_definition, definition_len); free(param_definition); for (i = 0; i < num_params; i++) { param = params->columns[i]; /* TODO check error */ tds_put_data_info(tds, param, 0); tds_put_data(tds, param); } tds->internal_sp_called = TDS_SP_EXECUTESQL; } return tds_query_flush_packet(tds);}inttds_submit_queryf(TDSSOCKET * tds, const char *queryf, ...){ va_list ap; char *query = NULL; int rc = TDS_FAIL; CHECK_TDS_EXTRA(tds); va_start(ap, queryf); if (vasprintf(&query, queryf, ap) >= 0) { rc = tds_submit_query(tds, query); free(query); } va_end(ap); return rc;}static const char *tds_skip_comment(const char *s){ const char *p = s; if (*p == '-' && p[1] == '-') { for (;*++p != '\0';) if (*p == '\n') return p; } else if (*p == '/' && p[1] == '*') { ++p; for(;*++p != '\0';) if (*p == '*' && p[1] == '/') return p + 2; } else ++p; return p;}/** * Skip quoting string (like 'sfsf', "dflkdj" or [dfkjd]) * @param s pointer to first quoting character (should be '," or [) * @return character after quoting */const char *tds_skip_quoted(const char *s){ const char *p = s; char quote = (*s == '[') ? ']' : *s; for (; *++p;) { if (*p == quote) { if (*++p != quote) return p; } } return p;}/** * Get position of next placeholder * @param start pointer to part of query to search * @return next placeholder or NULL if not found */const char *tds_next_placeholder(const char *start){ const char *p = start; if (!p) return NULL; for (;;) { switch (*p) { case '\0': return NULL; case '\'': case '\"': case '[': p = tds_skip_quoted(p); break; case '-': case '/': p = tds_skip_comment(p); break; case '?': return p; default: ++p; break; } }}/** * Count the number of placeholders in query */inttds_count_placeholders(const char *query){ const char *p = query - 1; int count = 0; for (;; ++count) { if (!(p = tds_next_placeholder(p + 1))) return count; }}static const char *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -