/src/tds/query.c
C | 3745 lines | 2565 code | 536 blank | 644 comment | 575 complexity | 6938f1b6db45218cd94b4bb8439074f7 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0
Large files files are truncated, but you can click here to view the full file
- /* 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, 2008, 2009, 2010, 2011 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.
- */
- #include <config.h>
- #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 <freetds/tds.h>
- #include <freetds/enum_cap.h>
- #include <freetds/iconv.h>
- #include <freetds/convert.h>
- #include <freetds/string.h>
- #include "tds_checks.h"
- #include "replacements.h"
- #include <assert.h>
- TDS_RCSID(var, "$Id: query.c,v 1.269 2012-03-06 20:33:14 freddy77 Exp $");
- static void tds_put_params(TDSSOCKET * tds, TDSPARAMINFO * info, int flags);
- static void tds7_put_query_params(TDSSOCKET * tds, const char *query, size_t query_len);
- static void tds7_put_params_definition(TDSSOCKET * tds, const char *param_definition, size_t param_length);
- static TDSRET tds_put_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int flags);
- static inline TDSRET tds_put_data(TDSSOCKET * tds, TDSCOLUMN * curcol);
- static char *tds7_build_param_def_from_query(TDSSOCKET * tds, const char* converted_query, size_t 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 TDSRET tds_put_param_as_string(TDSSOCKET * tds, TDSPARAMINFO * params, int n);
- static TDSRET tds_send_emulated_execute(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params);
- 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 size_t
- tds_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;
- }
- /**
- * Utility to convert a constant ascii string to ucs2 and send to server.
- * Used to send internal store procedure names to server.
- * \tds
- * \param s constanst string to send
- */
- #define TDS_PUT_N_AS_UCS2(tds, s) do { \
- char buffer[sizeof(s)*2-2]; \
- tds_put_smallint(tds, sizeof(buffer)/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
- */
- const char *
- tds_convert_string(TDSSOCKET * tds, TDSICONV * char_conv, const char *s, int len, size_t *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);
- il = len < 0 ? strlen(s) : (size_t) len;
- if (char_conv->flags == TDS_ENCODING_MEMCPY) {
- *out_len = il;
- return s;
- }
- /* allocate needed buffer (+1 is to exclude 0 case) */
- ol = il * char_conv->to.charset.max_bytes_per_char / char_conv->from.charset.min_bytes_per_char + 1;
- buf = (char *) malloc(ol);
- if (!buf)
- return NULL;
- ib = s;
- 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_CHECKS
- void
- tds_convert_string_free(const char *original, const char *converted)
- {
- if (original != converted)
- free((char *) converted);
- }
- #endif
- /**
- * Flush query packet.
- * Used at the end of packet write to really send packet to server.
- * \tds
- */
- static TDSRET
- tds_query_flush_packet(TDSSOCKET *tds)
- {
- /* TODO depend on result ?? */
- tds_set_state(tds, TDS_PENDING);
- return tds_flush_packet(tds);
- }
- /**
- * Set current dynamic.
- * \tds
- * \param dyn dynamic to set
- */
- void
- tds_set_cur_dyn(TDSSOCKET *tds, TDSDYNAMIC *dyn)
- {
- if (dyn)
- ++dyn->ref_count;
- tds_release_cur_dyn(tds);
- tds->cur_dyn = dyn;
- }
- /**
- * 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.
- * \tds
- * \param query language query to submit
- * \return TDS_FAIL or TDS_SUCCESS
- */
- TDSRET
- tds_submit_query(TDSSOCKET * tds, const char *query)
- {
- return tds_submit_query_params(tds, query, NULL, NULL);
- }
- /**
- * Substitute ?-style placeholders with named (\@param) ones.
- * Sybase does not support ?-style placeholders so convert them.
- * Also the function replace parameters names.
- * \param query query string
- * \param[in,out] query_len pointer to query length.
- * On input length of input query, on output length
- * of output query
- * \param params parameters to send to server
- * \returns new query or NULL on error
- */
- static char *
- tds5_fix_dot_query(const char *query, size_t *query_len, TDSPARAMINFO * params)
- {
- int i;
- size_t len, pos;
- const char *e, *s;
- size_t size = *query_len + 30;
- char colname[32];
- char *out;
- out = (char *) malloc(size);
- if (!out)
- goto memory_error;
- pos = 0;
- s = query;
- for (i = 0;; ++i) {
- e = tds_next_placeholder(s);
- len = e ? e - s : strlen(s);
- if (pos + len + 12 >= size) {
- size = pos + len + 30;
- if (!TDS_RESIZE(out, size))
- goto memory_error;
- }
- memcpy(out + pos, s, len);
- pos += len;
- if (!e)
- break;
- pos += sprintf(out + pos, "@P%d", i + 1);
- if (i >= params->num_cols)
- goto memory_error;
- sprintf(colname, "@P%d", i + 1);
- if (!tds_dstr_copy(¶ms->columns[i]->column_name, colname))
- goto memory_error;
- s = e + 1;
- }
- out[pos] = 0;
- *query_len = pos;
- return out;
- memory_error:
- free(out);
- return NULL;
- }
- /**
- * Write data to wire
- * \tds
- * \param curcol column where store column information
- * \return TDS_FAIL on error or TDS_SUCCESS
- */
- static inline TDSRET
- tds_put_data(TDSSOCKET * tds, TDSCOLUMN * curcol)
- {
- return curcol->funcs->put_data(tds, curcol, 0);
- }
- /**
- * Start query packet of a given type
- * \tds
- * \param packet_type packet type
- * \param head extra information to put in a TDS7 header
- */
- static TDSRET
- tds_start_query_head(TDSSOCKET *tds, unsigned char packet_type, TDSHEADERS * head)
- {
- tds->out_flag = packet_type;
- if (IS_TDS72_PLUS(tds->conn)) {
- int qn_len = 0;
- const char *converted_msgtext = NULL;
- const char *converted_options = NULL;
- size_t converted_msgtext_len = 0;
- size_t converted_options_len = 0;
- if (head && head->qn_msgtext && head->qn_options) {
- converted_msgtext = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], head->qn_msgtext, (int)strlen(head->qn_msgtext), &converted_msgtext_len);
- if (!converted_msgtext) {
- tds_set_state(tds, TDS_IDLE);
- return TDS_FAIL;
- }
- converted_options = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], head->qn_options, (int)strlen(head->qn_options), &converted_options_len);
- if (!converted_options) {
- tds_convert_string_free(head->qn_msgtext, converted_msgtext);
- tds_set_state(tds, TDS_IDLE);
- return TDS_FAIL;
- }
- qn_len = 6 + 2 + converted_msgtext_len + 2 + converted_options_len;
- if (head->qn_timeout != 0)
- qn_len += 4;
- }
- tds_put_int(tds, 4 + 18 + qn_len); /* total length */
- tds_put_int(tds, 18); /* length: transaction descriptor */
- tds_put_smallint(tds, 2); /* type: transaction descriptor */
- tds_put_n(tds, tds->conn->tds72_transaction, 8); /* transaction */
- tds_put_int(tds, 1); /* request count */
- if (qn_len != 0) {
- tds_put_int(tds, qn_len); /* length: query notification */
- tds_put_smallint(tds, 1); /* type: query notification */
- tds_put_smallint(tds, converted_msgtext_len); /* notifyid */
- tds_put_n(tds, converted_msgtext, converted_msgtext_len);
- tds_put_smallint(tds, converted_options_len); /* ssbdeployment */
- tds_put_n(tds, converted_options, converted_options_len);
- if (head->qn_timeout != 0)
- tds_put_int(tds, head->qn_timeout); /* timeout */
- tds_convert_string_free(head->qn_options, converted_options);
- tds_convert_string_free(head->qn_msgtext, converted_msgtext);
- }
- }
- return TDS_SUCCESS;
- }
- /**
- * Start query packet of a given type
- * \tds
- * \param packet_type packet type
- */
- static void
- tds_start_query(TDSSOCKET *tds, unsigned char packet_type)
- {
- /* no need to check return value here because tds_start_query_head() cannot
- fail when given a NULL head parameter */
- tds_start_query_head(tds, packet_type, NULL);
- }
- /**
- * 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.
- * \tds
- * \param query language query to submit
- * \param params parameters of query
- * \return TDS_FAIL or TDS_SUCCESS
- */
- TDSRET
- tds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head)
- {
- size_t 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->conn)) {
- 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, tds_dstr_isempty(¶ms->columns[0]->column_name) ? 0 : TDS_PUT_DATA_USE_NAME);
- }
- free(new_query);
- } else if (!IS_TDS7_PLUS(tds->conn) || !params || !params->num_cols) {
- if (tds_start_query_head(tds, TDS_QUERY, head) != TDS_SUCCESS)
- return TDS_FAIL;
- tds_put_string(tds, query, (int)query_len);
- } else {
- TDSCOLUMN *param;
- size_t definition_len;
- int count, i;
- char *param_definition;
- size_t converted_query_len;
- const char *converted_query;
-
- converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, (int)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_convert_string_free(query, converted_query);
- tds_set_state(tds, TDS_IDLE);
- return TDS_FAIL;
- }
- }
-
- if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
- tds_convert_string_free(query, converted_query);
- free(param_definition);
- return TDS_FAIL;
- }
- /* procedure name */
- if (IS_TDS71_PLUS(tds->conn)) {
- tds_put_smallint(tds, -1);
- tds_put_smallint(tds, TDS_SP_EXECUTESQL);
- } else {
- 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_TDS71_PLUS(tds->conn))
- tds_put_n(tds, tds->conn->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);
- if (tds_put_data(tds, param) != TDS_SUCCESS)
- return TDS_FAIL;
- }
- tds->current_op = TDS_OP_EXECUTESQL;
- }
- return tds_query_flush_packet(tds);
- }
- /**
- * Format and submit a query
- * \tds
- * \param queryf query format. printf like expansion is performed on
- * this query.
- */
- TDSRET
- tds_submit_queryf(TDSSOCKET * tds, const char *queryf, ...)
- {
- va_list ap;
- char *query = NULL;
- TDSRET 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;
- }
- /**
- * Skip a comment in a query
- * \param s start of the string (or part of it)
- * \returns pointer to end of comment
- */
- 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. @verbatim Should be ', " or [. @endverbatim
- * \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
- * \param query query string
- */
- int
- tds_count_placeholders(const char *query)
- {
- const char *p = query - 1;
- int count = 0;
- for (;; ++count) {
- if (!(p = tds_next_placeholder(p + 1)))
- return count;
- }
- }
- /**
- * Skip a comment in a query
- * \param s start of the string (or part of it). Encoded in ucs2
- * \param end end of string
- * \returns pointer to end of comment
- */
- static const char *
- tds_skip_comment_ucs2le(const char *s, const char *end)
- {
- const char *p = s;
- if (p+4 <= end && memcmp(p, "-\0-", 4) == 0) {
- for (;(p+=2) < end;)
- if (p[0] == '\n' && p[1] == 0)
- return p + 2;
- } else if (p+4 <= end && memcmp(p, "/\0*", 4) == 0) {
- p += 2;
- end -= 2;
- for(;(p+=2) < end;)
- if (memcmp(p, "*\0/", 4) == 0)
- return p + 4;
- } else
- p += 2;
- return p;
- }
- /**
- * Return pointer to end of a quoted string.
- * At the beginning pointer should point to delimiter.
- * \param s start of string to skip encoded in ucs2
- * \param end pointer to end of string
- */
- static const char *
- tds_skip_quoted_ucs2le(const char *s, const char *end)
- {
- const char *p = s;
- char quote = (*s == '[') ? ']' : *s;
- assert(s[1] == 0 && s < end && (end - s) % 2 == 0);
- for (; (p += 2) != end;) {
- if (p[0] == quote && !p[1]) {
- p += 2;
- if (p == end || p[0] != quote || p[1])
- return p;
- }
- }
- return p;
- }
- /**
- * Found the next placeholder (? or \@param) in a string.
- * String must be encoded in ucs2.
- * \param start start of the string (or part of it)
- * \param end end of string
- * \param named true if named parameters should be returned
- * \returns either start of next placeholder or end if not found
- */
- static const char *
- tds_next_placeholder_ucs2le(const char *start, const char *end, int named)
- {
- const char *p = start;
- char prev = ' ', c;
- assert(p && start <= end && (end - start) % 2 == 0);
- for (; p != end;) {
- if (p[1]) {
- prev = ' ';
- p += 2;
- continue;
- }
- c = p[0];
- switch (c) {
- case '\'':
- case '\"':
- case '[':
- p = tds_skip_quoted_ucs2le(p, end);
- break;
- case '-':
- case '/':
- p = tds_skip_comment_ucs2le(p, end);
- c = ' ';
- break;
- case '?':
- return p;
- case '@':
- if (named && !isalnum((unsigned char) prev))
- return p;
- default:
- p += 2;
- break;
- }
- prev = c;
- }
- return end;
- }
- /**
- * Count number of placeholders (?) in a query
- * \param query query encoded in ucs2
- * \param query_end end of query
- * \return number of placeholders found
- */
- static int
- tds_count_placeholders_ucs2le(const char *query, const char *query_end)
- {
- const char *p = query - 2;
- int count = 0;
- for (;; ++count) {
- if ((p = tds_next_placeholder_ucs2le(p + 2, query_end, 0)) == query_end)
- return count;
- }
- }
- /**
- * Return declaration for column (like "varchar(20)")
- * \tds
- * \param curcol column
- * \param out buffer to hold declaration
- * \return TDS_FAIL or TDS_SUCCESS
- */
- TDSRET
- tds_get_column_declaration(TDSSOCKET * tds, TDSCOLUMN * curcol, char *out)
- {
- const char *fmt = NULL;
- /* unsigned int is required by printf format, don't use size_t */
- unsigned int max_len = IS_TDS7_PLUS(tds->conn) ? 8000 : 255;
- unsigned int size;
- CHECK_TDS_EXTRA(tds);
- CHECK_COLUMN_EXTRA(curcol);
- size = tds_fix_column_size(tds, curcol);
- switch (tds_get_conversion_type(curcol->on_server.column_type, curcol->on_server.column_size)) {
- case XSYBCHAR:
- case SYBCHAR:
- fmt = "CHAR(%u)";
- break;
- case SYBVARCHAR:
- case XSYBVARCHAR:
- if (curcol->column_varint_size == 8)
- fmt = "VARCHAR(MAX)";
- else
- fmt = "VARCHAR(%u)";
- break;
- case SYBINT1:
- fmt = "TINYINT";
- break;
- case SYBINT2:
- fmt = "SMALLINT";
- break;
- case SYBINT4:
- fmt = "INT";
- break;
- case SYBINT8:
- /* TODO even for Sybase ?? */
- fmt = "BIGINT";
- break;
- case SYBFLT8:
- fmt = "FLOAT";
- break;
- case SYBDATETIME:
- fmt = "DATETIME";
- break;
- case SYBBIT:
- fmt = "BIT";
- break;
- case SYBTEXT:
- fmt = "TEXT";
- break;
- case SYBLONGBINARY: /* TODO correct ?? */
- case SYBIMAGE:
- fmt = "IMAGE";
- break;
- case SYBMONEY4:
- fmt = "SMALLMONEY";
- break;
- case SYBMONEY:
- fmt = "MONEY";
- break;
- case SYBDATETIME4:
- fmt = "SMALLDATETIME";
- break;
- case SYBREAL:
- fmt = "REAL";
- break;
- case SYBBINARY:
- case XSYBBINARY:
- fmt = "BINARY(%u)";
- break;
- case SYBVARBINARY:
- case XSYBVARBINARY:
- if (curcol->column_varint_size == 8)
- fmt = "VARBINARY(MAX)";
- else
- fmt = "VARBINARY(%u)";
- break;
- case SYBNUMERIC:
- fmt = "NUMERIC(%d,%d)";
- goto numeric_decimal;
- case SYBDECIMAL:
- fmt = "DECIMAL(%d,%d)";
- numeric_decimal:
- sprintf(out, fmt, curcol->column_prec, curcol->column_scale);
- return TDS_SUCCESS;
- break;
- case SYBUNIQUE:
- if (IS_TDS7_PLUS(tds->conn))
- fmt = "UNIQUEIDENTIFIER";
- break;
- case SYBNTEXT:
- if (IS_TDS7_PLUS(tds->conn))
- fmt = "NTEXT";
- break;
- case SYBNVARCHAR:
- case XSYBNVARCHAR:
- if (curcol->column_varint_size == 8) {
- fmt = "NVARCHAR(MAX)";
- } else if (IS_TDS7_PLUS(tds->conn)) {
- fmt = "NVARCHAR(%u)";
- max_len = 4000;
- size /= 2u;
- }
- break;
- case XSYBNCHAR:
- if (IS_TDS7_PLUS(tds->conn)) {
- fmt = "NCHAR(%u)";
- max_len = 4000;
- size /= 2u;
- }
- break;
- case SYBVARIANT:
- if (IS_TDS7_PLUS(tds->conn))
- fmt = "SQL_VARIANT";
- break;
- /* TODO support scale !! */
- case SYBMSTIME:
- fmt = "TIME";
- break;
- case SYBMSDATE:
- fmt = "DATE";
- break;
- case SYBMSDATETIME2:
- fmt = "DATETIME2";
- break;
- case SYBMSDATETIMEOFFSET:
- fmt = "DATETIMEOFFSET";
- break;
- case SYBUINT2:
- fmt = "UNSIGNED SMALLINT";
- break;
- case SYBUINT4:
- fmt = "UNSIGNED INT";
- break;
- case SYBUINT8:
- fmt = "UNSIGNED BIGINT";
- break;
- /* nullable types should not occur here... */
- case SYBFLTN:
- case SYBMONEYN:
- case SYBDATETIMN:
- case SYBBITN:
- case SYBINTN:
- assert(0);
- /* TODO... */
- case SYBVOID:
- case SYBSINT1:
- default:
- tdsdump_log(TDS_DBG_ERROR, "Unknown type %d\n", tds_get_conversion_type(curcol->on_server.column_type, curcol->on_server.column_size));
- break;
- }
- if (fmt) {
- /* fill out */
- sprintf(out, fmt, size > 0 ? (size > max_len ? max_len : size) : 1u);
- return TDS_SUCCESS;
- }
- out[0] = 0;
- return TDS_FAIL;
- }
- /**
- * Return string with parameters definition, useful for TDS7+
- * \param tds state information for the socket and the TDS protocol
- * \param converted_query query to send to server in ucs2 encoding
- * \param converted_query_len query length in bytes
- * \param params parameters to build declaration
- * \param out_len length output buffer in bytes
- * \return allocated and filled string or NULL on failure (coded in ucs2le charset )
- */
- /* TODO find a better name for this function */
- static char *
- tds7_build_param_def_from_query(TDSSOCKET * tds, const char* converted_query, size_t converted_query_len, TDSPARAMINFO * params, size_t *out_len)
- {
- size_t len = 0, size = 512;
- char *param_str;
- char declaration[40];
- int i, count;
- assert(IS_TDS7_PLUS(tds->conn));
- assert(out_len);
- CHECK_TDS_EXTRA(tds);
- if (params)
- CHECK_PARAMINFO_EXTRA(params);
- count = tds_count_placeholders_ucs2le(converted_query, converted_query + converted_query_len);
-
- param_str = (char *) malloc(512);
- if (!param_str)
- return NULL;
- for (i = 0; i < count; ++i) {
- if (len > 0u) {
- param_str[len++] = ',';
- param_str[len++] = 0;
- }
- /* realloc on insufficient space */
- while ((len + (2u * 40u)) > size) {
- size += 512u;
- if (!TDS_RESIZE(param_str, size))
- goto Cleanup;
- }
- /* get this parameter declaration */
- sprintf(declaration, "@P%d ", i+1);
- if (params && i < params->num_cols) {
- if (TDS_FAILED(tds_get_column_declaration(tds, params->columns[i], declaration + strlen(declaration))))
- goto Cleanup;
- } else {
- strcat(declaration, "varchar(4000)");
- }
- /* convert it to ucs2 and append */
- len += tds_ascii_to_ucs2(param_str + len, declaration);
- }
- *out_len = len;
- return param_str;
- Cleanup:
- free(param_str);
- return NULL;
- }
- /**
- * Return string with parameters definition, useful for TDS7+
- * \param tds state information for the socket and the TDS protocol
- * \param query query to send to server encoded as ucs2
- * \param query_len query length in bytes
- * \param params parameters to build declaration
- * \param out_len length output buffer in bytes
- * \return allocated and filled string or NULL on failure (coded in ucs2le charset )
- */
- /* TODO find a better name for this function */
- static char *
- tds7_build_param_def_from_params(TDSSOCKET * tds, const char* query, size_t query_len, TDSPARAMINFO * params, size_t *out_len)
- {
- size_t size = 512;
- char *param_str;
- char declaration[40];
- size_t l = 0;
- int i;
- struct tds_ids {
- const char *p;
- size_t len;
- } *ids = NULL;
-
- assert(IS_TDS7_PLUS(tds->conn));
- assert(out_len);
-
- CHECK_TDS_EXTRA(tds);
- if (params)
- CHECK_PARAMINFO_EXTRA(params);
-
- param_str = (char *) malloc(512);
- if (!param_str)
- goto Cleanup;
- if (!params)
- goto no_params;
- /* try to detect missing names */
- if (params->num_cols) {
- ids = (struct tds_ids *) calloc(params->num_cols, sizeof(struct tds_ids));
- if (!ids)
- goto Cleanup;
- if (tds_dstr_isempty(¶ms->columns[0]->column_name)) {
- const char *s = query, *e, *id_end;
- const char *query_end = query + query_len;
- for (i = 0; i < params->num_cols; s = e + 2) {
- e = tds_next_placeholder_ucs2le(s, query_end, 1);
- if (e == query_end)
- break;
- if (e[0] != '@')
- continue;
- /* find end of param name */
- for (id_end = e + 2; id_end != query_end; id_end += 2)
- if (!id_end[1] && (id_end[0] != '_' && id_end[1] != '#' && !isalnum((unsigned char) id_end[0])))
- break;
- ids[i].p = e;
- ids[i].len = id_end - e;
- ++i;
- }
- }
- }
-
- for (i = 0; i < params->num_cols; ++i) {
- const char *ib;
- char *ob;
- size_t il, ol;
-
- if (l > 0u) {
- param_str[l++] = ',';
- param_str[l++] = 0;
- }
-
- /* realloc on insufficient space */
- il = ids[i].p ? ids[i].len : 2 * tds_dstr_len(¶ms->columns[i]->column_name);
- while ((l + (2u * 40u) + il) > size) {
- size += 512u;
- if (!TDS_RESIZE(param_str, size))
- goto Cleanup;
- }
-
- /* this part of buffer can be not-ascii compatible, use all ucs2... */
- if (ids[i].p) {
- memcpy(param_str + l, ids[i].p, ids[i].len);
- l += ids[i].len;
- } else {
- ib = tds_dstr_cstr(¶ms->columns[i]->column_name);
- il = tds_dstr_len(¶ms->columns[i]->column_name);
- ob = param_str + l;
- ol = size - l;
- memset(&tds->conn->char_convs[iso2server_metadata]->suppress, 0, sizeof(tds->conn->char_convs[iso2server_metadata]->suppress));
- if (tds_iconv(tds, tds->conn->char_convs[iso2server_metadata], to_server, &ib, &il, &ob, &ol) == (size_t) - 1)
- goto Cleanup;
- l = size - ol;
- }
- param_str[l++] = ' ';
- param_str[l++] = 0;
-
- /* get this parameter declaration */
- tds_get_column_declaration(tds, params->columns[i], declaration);
- if (!declaration[0])
- goto Cleanup;
-
- /* convert it to ucs2 and append */
- l += tds_ascii_to_ucs2(param_str + l, declaration);
-
- }
- free(ids);
- no_params:
- *out_len = l;
- return param_str;
-
- Cleanup:
- free(ids);
- free(param_str);
- return NULL;
- }
- /**
- * Output params types and query (required by sp_prepare/sp_executesql/sp_prepexec)
- * \param tds state information for the socket and the TDS protocol
- * \param query query (in ucs2le codings)
- * \param query_len query length in bytes
- */
- static void
- tds7_put_query_params(TDSSOCKET * tds, const char *query, size_t query_len)
- {
- size_t len;
- int i, num_placeholders;
- const char *s, *e;
- char buf[24];
- const char *const query_end = query + query_len;
- CHECK_TDS_EXTRA(tds);
- assert(IS_TDS7_PLUS(tds->conn));
- /* we use all "@PX" for parameters */
- num_placeholders = tds_count_placeholders_ucs2le(query, query_end);
- len = num_placeholders * 2;
- /* adjust for the length of X */
- for (i = 10; i <= num_placeholders; i *= 10) {
- len += num_placeholders - i + 1;
- }
- /* string with sql statement */
- /* replace placeholders with dummy parametes */
- tds_put_byte(tds, 0);
- tds_put_byte(tds, 0);
- tds_put_byte(tds, SYBNTEXT); /* must be Ntype */
- len = 2u * len + query_len;
- TDS_PUT_INT(tds, len);
- if (IS_TDS71_PLUS(tds->conn))
- tds_put_n(tds, tds->conn->collation, 5);
- TDS_PUT_INT(tds, len);
- s = query;
- /* TODO do a test with "...?" and "...?)" */
- for (i = 1;; ++i) {
- e = tds_next_placeholder_ucs2le(s, query_end, 0);
- assert(e && query <= e && e <= query_end);
- tds_put_n(tds, s, e - s);
- if (e == query_end)
- break;
- sprintf(buf, "@P%d", i);
- tds_put_string(tds, buf, -1);
- s = e + 2;
- }
- }
- /**
- * Send parameter definition to server
- * \tds
- * \param param_definition parameter definition string. Encoded in ucs2
- * \param param_length parameter definition string length in bytes
- */
- static void
- tds7_put_params_definition(TDSSOCKET * tds, const char *param_definition, size_t param_length)
- {
- CHECK_TDS_EXTRA(tds);
- /* string with parameters types */
- tds_put_byte(tds, 0);
- tds_put_byte(tds, 0);
- tds_put_byte(tds, SYBNTEXT); /* must be Ntype */
- /* put parameters definitions */
- TDS_PUT_INT(tds, param_length);
- if (IS_TDS71_PLUS(tds->conn))
- tds_put_n(tds, tds->conn->collation, 5);
- TDS_PUT_INT(tds, param_length ? param_length : -1);
- tds_put_n(tds, param_definition, param_length);
- }
- /**
- * tds_submit_prepare() creates a temporary stored procedure in the server.
- * Under TDS 4.2 dynamic statements are emulated building sql command
- * \param tds state information for the socket and the TDS protocol
- * \param query language query with given placeholders (?)
- * \param id string to identify the dynamic query. Pass NULL for automatic generation.
- * \param dyn_out will receive allocated TDSDYNAMIC*. Any older allocated dynamic won't be freed, Can be NULL.
- * \param params parameters to use. It can be NULL even if parameters are present. Used only for TDS7+
- * \return TDS_FAIL or TDS_SUCCESS
- */
- /* TODO parse all results ?? */
- TDSRET
- tds_submit_prepare(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params)
- {
- int id_len, query_len;
- TDSRET rc = TDS_FAIL;
- TDSDYNAMIC *dyn;
- CHECK_TDS_EXTRA(tds);
- if (params)
- CHECK_PARAMINFO_EXTRA(params);
- if (!query || !dyn_out)
- return TDS_FAIL;
- if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)
- return TDS_FAIL;
- /* allocate a structure for this thing */
- dyn = tds_alloc_dynamic(tds->conn, id);
- if (!dyn)
- return TDS_FAIL;
- tds_release_dynamic(dyn_out);
- *dyn_out = dyn;
- tds_release_cur_dyn(tds);
- /* TDS5 sometimes cannot accept prepare so we need to store query */
- if (!IS_TDS7_PLUS(tds->conn)) {
- dyn->query = strdup(query);
- if (!dyn->query)
- goto failure;
- }
- if (!IS_TDS50(tds->conn) && !IS_TDS7_PLUS(tds->conn)) {
- dyn->emulated = 1;
- tds_dynamic_deallocated(tds->conn, dyn);
- tds_set_state(tds, TDS_IDLE);
- return TDS_SUCCESS;
- }
- query_len = (int)strlen(query);
- tds_set_cur_dyn(tds, dyn);
- if (IS_TDS7_PLUS(tds->conn)) {
- size_t definition_len = 0;
- char *param_definition = NULL;
- size_t converted_query_len;
- const char *converted_query;
- converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, query_len, &converted_query_len);
- if (!converted_query)
- goto failure;
- param_definition = tds7_build_param_def_from_query(tds, converted_query, converted_query_len, params, &definition_len);
- if (!param_definition) {
- tds_convert_string_free(query, converted_query);
- goto failure;
- }
- tds_start_query(tds, TDS_RPC);
- /* procedure name */
- if (IS_TDS71_PLUS(tds->conn)) {
- tds_put_smallint(tds, -1);
- tds_put_smallint(tds, TDS_SP_PREPARE);
- } else {
- TDS_PUT_N_AS_UCS2(tds, "sp_prepare");
- }
- tds_put_smallint(tds, 0);
- /* return param handle (int) */
- tds_put_byte(tds, 0);
- tds_put_byte(tds, 1); /* result */
- tds_put_byte(tds, SYBINTN);
- tds_put_byte(tds, 4);
- tds_put_byte(tds, 0);
- tds7_put_params_definition(tds, param_definition, definition_len);
- tds7_put_query_params(tds, converted_query, converted_query_len);
- tds_convert_string_free(query, converted_query);
- free(param_definition);
- /* 1 param ?? why ? flags ?? */
- tds_put_byte(tds, 0);
- tds_put_byte(tds, 0);
- tds_put_byte(tds, SYBINTN);
- tds_put_byte(tds, 4);
- tds_put_byte(tds, 4);
- tds_put_int(tds, 1);
- tds->current_op = TDS_OP_PREPARE;
- } else {
- int dynproc_capability =
- tds_capability_has_req(tds->conn, TDS_REQ_PROTO_DYNPROC);
- unsigned toklen;
- tds->out_flag = TDS_NORMAL;
- id_len = (int)strlen(dyn->id);
- tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
- toklen = 5 + id_len + query_len;
- if (dynproc_capability) toklen += id_len + 16;
- tds_put_smallint(tds, toklen);
- tds_put_byte(tds, TDS_DYN_PREPARE);
- tds_put_byte(tds, 0x00);
- tds_put_byte(tds, id_len);
- tds_put_n(tds, dyn->id, id_len);
- /* TODO ICONV convert string, do not put with tds_put_n */
- /* TODO how to pass parameters type? like store procedures ? */
- if (dynproc_capability) {
- tds_put_smallint(tds, query_len + id_len + 16);
- tds_put_n(tds, "create proc ", 12);
- tds_put_n(tds, dyn->id, id_len);
- tds_put_n(tds, " as ", 4);
- } else {
- tds_put_smallint(tds, query_len);
- }
- tds_put_n(tds, query, query_len);
- }
- rc = tds_query_flush_packet(tds);
- if (TDS_SUCCEED(rc))
- return rc;
- failure:
- /* TODO correct if writing fail ?? */
- tds_set_state(tds, TDS_IDLE);
- tds_release_dynamic(dyn_out);
- tds_dynamic_deallocated(tds->conn, dyn);
- return rc;
- }
- /**
- * Submit a prepared query with parameters
- * \param tds state information for the socket and the TDS protocol
- * \param query language query with given placeholders (?)
- * \param params parameters to send
- * \return TDS_FAIL or TDS_SUCCESS
- */
- TDSRET
- tds_submit_execdirect(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head)
- {
- size_t query_len;
- TDSCOLUMN *param;
- TDSDYNAMIC *dyn;
- size_t id_len;
- CHECK_TDS_EXTRA(tds);
- CHECK_PARAMINFO_EXTRA(params);
- if (!query)
- return TDS_FAIL;
- query_len = strlen(query);
- if (IS_TDS7_PLUS(tds->conn)) {
- size_t definition_len = 0;
- int i;
- char *param_definition = NULL;
- size_t converted_query_len;
- const char *converted_query;
- if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)
- return TDS_FAIL;
- converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, (int)query_len, &converted_query_len);
- if (!converted_query) {
- tds_set_state(tds, TDS_IDLE);
- return TDS_FAIL;
- }
- param_definition = tds7_build_param_def_from_query(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;
- }
- if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
- tds_convert_string_free(query, converted_query);
- free(param_definition);
- return TDS_FAIL;
- }
- /* procedure name */
- if (IS_TDS71_PLUS(tds->conn)) {
- tds_put_smallint(tds, -1);
- tds_put_smallint(tds, TDS_SP_EXECUTESQL);
- } else {
- TDS_PUT_N_AS_UCS2(tds, "sp_executesql");
- }
- tds_put_smallint(tds, 0);
- tds7_put_query_params(tds, converted_query, converted_query_len);
- tds7_put_params_definition(tds, param_definition, definition_len);
- tds_convert_string_free(query, converted_query);
- free(param_definition);
- for (i = 0; i < params->num_cols; i++) {
- TDSRET ret;
- param = params->columns[i];
- /* TODO check error */
- tds_put_data_info(tds, param, 0);
- ret = tds_put_data(tds, param);
- if (TDS_FAILED(ret))
- return ret;
- }
- tds->current_op = TDS_OP_EXECUTESQL;
- return tds_query_flush_packet(tds);
- }
- /* allocate a structure for this thing */
- dyn = tds_alloc_dynamic(tds->conn, NULL);
- if (!dyn)
- return TDS_FAIL;
- /* check is no parameters */
- if (params && !params->num_cols)
- params = NULL;
- /* TDS 4.2, emulate prepared statements */
- /*
- * TODO Sybase seems to not support parameters in prepared execdirect
- * so use language or prepare and then exec
- */
- if (!IS_TDS50(tds->conn) || params) {
- TDSRET ret = TDS_SUCCESS;
- dyn->emulated = 1;
- dyn->params = params;
- dyn->query = strdup(query);
- if (!dyn->query)
- ret = TDS_FAIL;
- if (TDS_SUCCEED(ret))
- if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)
- ret = TDS_FAIL;
- if (TDS_SUCCEED(ret)) {
- ret = tds_send_emulated_execute(tds, dyn->query, dyn->params);
- if (TDS_SUCCEED(ret))
- ret = tds_query_flush_packet(tds);
- }
- /* do not free our parameters */
- dyn->params = NULL;
- tds_dynamic_deallocated(tds->conn, dyn);
- tds_release_dynamic(&dyn);
- return ret;
- }
- tds_release_cur_dyn(tds);
- tds->cur_dyn = dyn;
- if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)
- return TDS_FAIL;
- tds->out_flag = TDS_NORMAL;
- id_len = strlen(dyn->id);
- tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
- TDS_PUT_SMALLINT(tds, query_len + id_len * 2 + 21);
- tds_put_byte(tds, TDS_DYN_EXEC_IMMED);
- tds_put_byte(tds, params ? 0x01 : 0);
- TDS_PUT_BYTE(tds, id_len);
- tds_put_n(tds, dyn->id, id_len);
- /* TODO ICONV convert string, do not put with tds_put_n */
- /* TODO how to pass parameters type? like store procedures ? */
- TDS_PUT_SMALLINT(tds, query_len + id_len + 16);
- tds_put_n(tds, "create proc ", 12);
- tds_put_n(tds, dyn->id, (int)id_len);
- tds_put_n(tds, " as ", 4);
- tds_put_n(tds, query, (int)query_len);
- if (params)
- tds_put_params(tds, params, 0);
- return tds_flush_packet(tds);
- }
- /**
- * tds71_submit_prepexec() creates a temporary stored procedure in the server.
- * \param tds state information for the socket and the TDS protocol
- * \param query language query with given placeholders (?)
- * \param id string to identify the dynamic query. Pass NULL for automatic generation.
- * \param dyn_out will receive allocated TDSDYNAMIC*. Any older allocated dynamic won't be freed, Can be NULL.
- * \param params parameters to use. It can be NULL even if parameters are present. Used only for TDS7+
- * \return TDS_FAIL or TDS_SUCCESS
- */
- TDSRET
- tds71_submit_prepexec(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params)
- {
- int query_len;
- TDSRET rc = TDS_FAIL;
- TDSDYNAMIC *dyn;
- size_t definition_len = 0;
- char *param_definition = NULL;
- size_t converted_query_len;
- const char *converted_query;
- CHECK_TDS_EXTRA(tds);
- if (params)
- CHECK_PARAMINFO_EXTRA(params);
- if (!query || !dyn_out || !IS_TDS7_PLUS(tds->conn))
- return TDS_FAIL;
- if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)
- return TDS_FAIL;
- /* allocate a structure for this thing */
- dyn = tds_alloc_dynamic(tds->conn, id);
- if (!dyn)
- return TDS_FAIL;
- tds_release_dynamic(dyn_out);
- *dyn_out = dyn;
- tds_set_cur_dyn(tds, dyn);
- query_len = (int)strlen(query);
- converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, query_len, &converted_query_len);
- if (!converted_query)
- goto failure;
- param_definition = tds7_build_param_def_from_query(tds, converted_query, converted_query_len, params, &definition_len);
- if (!param_definition) {
- tds_convert_string_free(query, converted_query);
- goto failure;
- }
- tds_start_query(tds, TDS_RPC);
- /* procedure name */
- if (IS_TDS71_PLUS(tds->conn)) {
- tds_put_smallint(tds, -1);
- tds_put_smallint(tds, TDS_SP_PREPEXEC);
- } else {
- TDS_PUT_N_AS_UCS2(tds, "sp_prepexec");
- }
- tds_put_smallint(tds, 0);
- /* return param handle (int) */
- tds_put_byte(tds, 0);
- tds_put_byte(tds, 1); /* result */
- tds_put_byte(tds, SYBINTN);
- tds_put_byte(tds, 4);
- tds_put_byte(tds, 0);
- tds7_put_params_definition(tds, param_definition, definition_len);
- tds7_put_query_params(tds, converted_query, converted_query_len);
- tds_convert_string_free(query, converted_query);
- free(param_definition);
- if (params) {
- int i;
- for (i = 0; i < params->num_cols; i++) {
- TDSCOLUMN *param = params->columns[i];
- /* TODO check error */
- tds_put_data_info(tds, param, 0);
- rc = tds_put_data(tds, param);
- if (TDS_FAILED(rc))
- return rc;
- }
- }
- tds->current_op = TDS_OP_PREPEXEC;
- rc = tds_query_flush_packet(tds);
- if (TDS_SUCCEED(rc))
- return rc;
- failure:
- /* TODO correct if writing fail ?? */
- tds_set_state(tds, TDS_IDLE);
- tds_release_dynamic(dyn_out);
- tds_dynamic_deallocated(tds->conn, dyn);
- return rc;
- }
- /**
- * Get column size for wire
- */
- size_t
- tds_fix_column_size(TDSSOCKET * tds, TDSCOLUMN * curcol)
- {
- size_t size = curcol->on_server.column_size, min;
- if (!size) {
- size = curcol->column_size;
- if (is_unicode_type(curcol->on_server.column_type))
- size *= 2u;
- }
- switch (curcol->column_varint_size) {
- case 1:
- size = MAX(MIN(size, 255), 1);
- break;
- case 2:
- /* note that varchar(max)/varbinary(max) have a varint of 8 */
- if (curcol->on_server.column_type == XSYBNVARCHAR || curcol->on_server.column_type == XSYBNCHAR)
- min = 2;
- else
- min = 1;
- size = MAX(MIN(size, 8000u), min);
- break;
- case 4:
- if (curcol->on_server.column_type == SYBNTEXT)
- size = MAX(MIN(size, 0x7ffffffeu), 2u);
- else
- size = MAX(MIN(size, 0x7fffffffu), 1u);
- break;
- default:
- break;
- }
- return size;
- }
- /**
- * Put data information to wire
- * \param tds state information for the socket and the TDS protocol
- * \param curcol column where to store information
- * \param flags bit flags on how to send data (use TDS_PUT_DATA_USE_NAME for use name information)
- * \return TDS_SUCCESS or TDS_FAIL
- */
- static TDSRET
- tds_put_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int flags)
- {
- int len;
- CHECK_TDS_EXTRA(tds);
- CHECK_COLUMN_EXTRA(curcol);
- if (flags & TDS_PUT_DATA_USE_NAME) {
- len = tds_dstr_len(&curcol->column_name);
- tdsdump_log(TDS_DBG_ERROR, "tds_put_data_info putting param_name \n");
- if (IS_TDS7_PLUS(tds->conn)) {
- size_t converted_param_len;
- const char *converted_param;
- /* TODO use a fixed buffer to avoid error ? */
- converted_param =
- tds_convert_string(tds, tds->conn->char_convs[client2ucs2], tds_dstr_cstr(&curcol->column_name), len,
- &converted_param_len);
- if (!converted_param)
- return TDS_FAIL;
- if (!(flags & TDS_PUT_DATA_PREFIX_NAME)) {
- TDS_PUT_BYTE(tds, converted_param_len / 2);
- } else {
- TDS_PUT_BYTE(tds, converted_param_len / 2 + 1);
- tds_put_n(tds, "@", 2);
- }
- tds_put_n(tds, converted_param, converted_param_len);
- tds_convert_string_free(tds_dstr_cstr(&curcol->column_name), converted_param);
- } else {
- /* TODO ICONV convert */
- tds_put_byte(tds, len); /* param name len */
- tds_put_n(tds, tds_dstr_cstr(&curcol->column_name), len);
- }
- } else {
- tds_put_byte(tds, 0x00); /* param name len */
- }
- /*
- * TODO support other flags (use defaul null/no metadata)
- * bit 1 (2 as flag) in TDS7+ is "default value" bit
- * (what's the meaning of "default value" ?)
- */
- tdsdump_log(TDS_DBG_ERROR, "tds_put_data_info putting status \n");
- tds_put_byte(tds, curcol->column_output); /* status (input) */
- if (!IS_TDS7_PLUS(tds->conn))
- tds_put_int(tds, curcol->column_usertype); /* usertype */
- /* FIXME: column_type is wider than one byte. Do something sensible, not just lop off the high byte. */
- tds_put_byte(tds, curcol->on_server.column_type);
- if (curcol->funcs->put_info(tds, curcol) != TDS_SUCCESS)
- return TDS_FAIL;
- /* TODO needed in TDS4.2 ?? now is called only is TDS >= 5 */
- if (!IS_TDS7_PLUS(tds->conn))
- tds_put_byte(tds, 0x00); /* locale info length */
- return TDS_SUCCESS;
- }
- /**
- * Calc information length in bytes (useful for calculating full packet length)
- * \param tds state information for the socket and the TDS protocol
- * \param curcol column where to store information
- * \param flags bit flags on how to send data (use TDS_PUT_DATA_USE_NAME for use name information)
- * \return data info length
- */
- static int
- tds_put_data_info_length(TDSSOCKET * tds, TDSCOLUMN * curcol, int flags)
- {
- int len = 8;
- CHECK_TDS_EXTRA(tds);
- CHECK_COLUMN_EXTRA(curcol);
- #if ENABLE_EXTRA_CHECKS
- if (IS_TDS7_PLUS(tds->conn))
- tdsdump_log(TDS_DBG_ERROR, "tds_put_data_info_length called with TDS7+\n");
- #endif
- /* TODO ICONV convert string if needed (see also tds_put_data_info) */
- if (flags & TDS_PUT_DATA_USE_NAME)
- len += tds_dstr_len(&curcol->column_name);
- if (is_numeric_type(curcol->on_server.column_type))
- len += 2;
- if (curcol->column_varint_size == 5)
- return len + 4;
- return len + curcol->column_varint_size;
- }
- /**
- * Send dynamic request on TDS 7+ to be executed
- * \tds
- * \param dyn dynamic query to execute
- */
- static void
- tds7_send_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn)
- {
- TDSCOLUMN *param;
- TDSPARAMINFO *info;
- int i;
- /* procedure name */
- /* NOTE do not call this procedure using integer name (TDS_SP_EXECUTE) on mssql2k, it doesn't work! */
- TDS_PUT_N_AS_UCS2(tds, "sp_execute");
- tds_put_smallint(tds, 0); /* flags */
- /* id of prepared statement */
- tds_put_byte(tds, 0);
- tds_put_byte(tds, 0);
- tds_put_byte(tds, SYBINTN);
- tds_put_byte(tds, 4);
- tds_put_byte(tds, 4);
- tds_put_int(tds, dyn->num_id);
- info = dyn->params;
- if (info)
- for (i = 0; i < info->num_cols; i++) {
- param = info->columns[i];
- /* TODO check error */
- tds_put_data_info(tds, param, 0);
- /* FIXME handle error */
- tds_put_data(tds, param);
- }
- tds->current_op = TDS_OP_EXECUTE;
- }
- /**
- * tds_submit_execute() sends a previously prepared dynamic statement to the
- * server.
- * \param tds state information for the socket and the TDS protocol
- * \param dyn dynamic proc to execute. Must build from same tds.
- */
- TDSRET
- tds_submit_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn)
- {
- int id_len;
- CHECK_TDS_EXTRA(tds);
- /* TODO this dynamic should be in tds */
- CHECK_DYNAMIC_EXTRA(dyn);
- tdsdump_log(TDS_DBG_FUNC, "tds_submit_execute()\n");
- if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)
- return TDS_FAIL;
- tds_set_cur_dyn(tds, dyn);
- if (IS_TDS7_PLUS(tds->conn)) {
- /* check proper id */
- if (dyn->num_id == 0) {
- tds_set_state(tds, TDS_IDLE);
- return TDS_FAIL;
- }
- /* RPC on sp_execute */
- tds_start_query(tds, TDS_RPC);
- tds7_send_execute(tds, dyn);
- return tds_query_flush_packet(tds);
- }
- if (dyn->emulated) {
- TDSRET rc = tds_send_emulated_execute(tds, dyn->query, dyn->params);
- if (TDS_FAILED(rc))
- return rc;
- return tds_query_flush_packet(tds);
- }
- /* query has been prepared successfully, discard original query */
- if (dyn->query)
- TDS_ZERO_FREE(dyn->query);
- tds->out_flag = TDS_NORMAL;
- /* dynamic id */
- id_len = (int)strlen(dyn->id);
- tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
- tds_put_smallint(tds, id_len + 5);
- tds_put_byte(tds, 0x02);
- tds_put_byte(tds, dyn->params ? 0x01 : 0);
- tds_put_byte(tds, id_len);
- tds_put_n(tds, dyn->id, id_len);
- tds_put_smallint(tds, 0);
- if (dyn->params)
- tds_put_params(tds, dyn->params, 0);
- /* send it */
- return tds_query_flush_packet(tds);
- }
- /**
- * Send parameters to server.
- * \tds
- * \param info parameters to send
- * \param flags 0 or TDS_PUT_DATA_USE_NAME
- */
- static void
- tds_put_params(TDSSOCKET * tds, TDSPARAMINFO * info, int flags)
- {
- int i, len;
- CHECK_TDS_EXTRA(tds);
- CHECK_PARAMINFO_EXTRA(info);
- /* column descriptions */
- tds_put_byte(tds, TDS5_PARAMFMT_TOKEN);
- /* size */
- len = 2;
- for (i = 0; i < info->num_cols; i++)
- len += tds_put_data_info_length(tds, info->columns[i], flags);
- tds_put_smallint(tds, len);
- /* number of parameters */
- tds_put_smallint(tds, info->num_cols);
- /* column detail for each parameter */
- for (i = 0; i < info->num_cols; i++) {
- /* FIXME add error handling */
- tds_put_data_info(tds, info->columns[i], flags);
- }
- /* row data */
- tds_put_byte(tds, TDS5_PARAMS_TOKEN);
- for (i = 0; i < info->num_cols; i++) {
- /* FIXME handle error */
- tds_put_data(tds, info->columns[i]);
- }
- }
- /**
- * Check if dynamic request must be unprepared.
- * Depending on status and protocol version request should be unprepared
- * or not.
- * \tds
- * \param dyn dynamic request to check
- */
- int
- tds_needs_unprepare(TDSSOCKET * tds, TDSDYNAMIC * dyn)
- {
- CHECK_TDS_EXTRA(tds);
- CHECK_DYNAMIC_EXTRA(dyn);
- /* check if statement is prepared */
- if (IS_TDS7_PLUS(tds->conn) && !dyn->num_id)
- return 0;
- if (dyn->emulated || !dyn->id[0])
- return 0;
- return 1;
- }
- /**
- * Send a unprepare request for a prepared query
- * \param tds state information for the socket and the TDS protocol
- * \param dyn dynamic query
- * \result TDS_SUCCESS or TDS_FAIL
- */
- TDSRET
- tds_submit_unprepare(TDSSOCKET * tds, TDSDYNAMIC * dyn)
- {
- int id_len;
- CHECK_TDS_EXTRA(tds);
- /* TODO test dyn in tds */
- CHECK_DYNAMIC_EXTRA(dyn);
- if (!dyn)
- return TDS_FAIL;
- tdsdump_log(TDS_DBG_FUNC, "tds_submit_unprepare() %s\n", dyn->id);
- if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)
- return TDS_FAIL;
- tds_set_cur_dyn(tds, dyn);
- if (IS_TDS7_PLUS(tds->conn)) {
- /* RPC on sp_execute */
- tds_start_query(tds, TDS_RPC);
- /* procedure name */
- if (IS_TDS71_PLUS(tds->conn)) {
- /* save some byte for mssql2k */
- tds_put_smallint(tds, -1);
- tds_put_smallint(tds, TDS_SP_UNPREPARE);
- } else {
- TDS_PUT_N_AS_UCS2(tds, "sp_unprepare");
- }
- tds_put_smallint(tds, 0); /* flags */
- /* id of prepared statement */
- tds_put_byte(tds, 0);
- tds_put_byte(tds, 0);
- tds_put_byte(tds, SYBINTN);
- tds_put_byte(tds, 4);
- tds_put_byte(tds, 4);
- tds_put_int(tds, dyn->num_id);
- tds->current_op = TDS_OP_UNPREPARE;
- return tds_query_flush_packet(tds);
- }
- if (dyn->emulated) {
- tds_start_query(tds, TDS_QUERY);
- /* just a dummy select to return some data */
- tds_put_string(tds, "select 1 where 0=1", -1);
- return tds_query_flush_packet(tds);
- }
- tds->out_flag = TDS_NORMAL;
- /* dynamic id */
- id_len = (int)strlen(dyn->id);
- tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
- tds_put_smallint(tds, id_len + 5);
- tds_put_byte(tds, TDS_DYN_DE…
Large files files are truncated, but you can click here to view the full file