LCOV - code coverage report
Current view: top level - src/tds - query.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 1472 1746 84.3 %
Date: 2026-11-07 23:58:45 Functions: 62 65 95.4 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Brian Bruns
       3             :  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011  Frediano Ziglio
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU Library General Public
       7             :  * License as published by the Free Software Foundation; either
       8             :  * version 2 of the License, or (at your option) any later version.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13             :  * Library General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU Library General Public
      16             :  * License along with this library; if not, write to the
      17             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      18             :  * Boston, MA 02111-1307, USA.
      19             :  */
      20             : 
      21             : #include <config.h>
      22             : 
      23             : #include <stdarg.h>
      24             : #include <stdio.h>
      25             : 
      26             : #if HAVE_STDLIB_H
      27             : #include <stdlib.h>
      28             : #endif /* HAVE_STDLIB_H */
      29             : 
      30             : #if HAVE_STRING_H
      31             : #include <string.h>
      32             : #endif /* HAVE_STRING_H */
      33             : 
      34             : #include <ctype.h>
      35             : 
      36             : #include <freetds/tds.h>
      37             : #include <freetds/enum_cap.h>
      38             : #include <freetds/tds/iconv.h>
      39             : #include <freetds/tds/convert.h>
      40             : #include <freetds/utils/string.h>
      41             : #include <freetds/tds/checks.h>
      42             : #include <freetds/tds/stream.h>
      43             : #include <freetds/bytes.h>
      44             : #include <freetds/replacements.h>
      45             : 
      46             : #include <assert.h>
      47             : 
      48             : static TDSRET tds5_put_params(TDSSOCKET * tds, TDSPARAMINFO * info, int flags) TDS_WUR;
      49             : static void tds7_put_query_params(TDSSOCKET * tds, const char *query, size_t query_len);
      50             : static TDSRET tds_put_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int flags);
      51             : static inline TDSRET tds_put_data(TDSSOCKET * tds, TDSCOLUMN * curcol);
      52             : static TDSRET tds7_write_param_def_from_query(TDSSOCKET * tds, const char* converted_query,
      53             :                                               size_t converted_query_len, TDSPARAMINFO * params) TDS_WUR;
      54             : static TDSRET tds7_write_param_def_from_params(TDSSOCKET * tds, const char* query, size_t query_len,
      55             :                                                TDSPARAMINFO * params) TDS_WUR;
      56             : 
      57             : static TDSRET tds_put_param_as_string(TDSSOCKET * tds, TDSPARAMINFO * params, int n);
      58             : static TDSRET tds_send_emulated_execute(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params);
      59             : static int tds_count_placeholders_ucs2le(const char *query, const char *query_end);
      60             : 
      61             : #define TDS_PUT_DATA_USE_NAME 1
      62             : #define TDS_PUT_DATA_PREFIX_NAME 2
      63             : #define TDS_PUT_DATA_LONG_STATUS 4
      64             : 
      65             : /* All manner of client to server submittal functions */
      66             : 
      67             : /**
      68             :  * \ingroup libtds
      69             :  * \defgroup query Query
      70             :  * Function to handle query.
      71             :  */
      72             : 
      73             : /**
      74             :  * \addtogroup query
      75             :  * @{ 
      76             :  */
      77             : 
      78             : /**
      79             :  * Accept an ASCII string, convert it to UCS2-LE
      80             :  * The input is NUL-terminated, but the output does not contains the NUL.
      81             :  * \param buffer buffer where to store output
      82             :  * \param buf string to write
      83             :  * \return bytes written
      84             :  */
      85             : static size_t
      86        1138 : tds_ascii_to_ucs2(char *buffer, const char *buf)
      87             : {
      88             :         char *s;
      89        1138 :         assert(buffer && buf && *buf); /* This is an internal function.  Call it correctly. */
      90             : 
      91       11380 :         for (s = buffer; *buf != '\0'; ++buf) {
      92       11380 :                 *s++ = *buf;
      93       11380 :                 *s++ = '\0';
      94             :         }
      95             : 
      96        1138 :         return s - buffer;
      97             : }
      98             : 
      99             : /**
     100             :  * Utility to convert a constant ascii string to ucs2 and send to server.
     101             :  * Used to send internal store procedure names to server.
     102             :  * \tds
     103             :  * \param s  constanst string to send
     104             :  */
     105             : #define TDS_PUT_N_AS_UCS2(tds, s) do { \
     106             :         char buffer[sizeof(s)*2-2]; \
     107             :         tds_put_smallint(tds, sizeof(buffer)/2); \
     108             :         tds_put_n(tds, buffer, tds_ascii_to_ucs2(buffer, s)); \
     109             : } while(0)
     110             : 
     111             : /**
     112             :  * Convert a string in an allocated buffer
     113             :  * \param tds        state information for the socket and the TDS protocol
     114             :  * \param char_conv  information about the encodings involved
     115             :  * \param s          input string
     116             :  * \param len        input string length (in bytes), -1 for NUL-terminated
     117             :  * \param out_len    returned output length (in bytes)
     118             :  * \return string allocated (or input pointer if no conversion required) or NULL if error
     119             :  */
     120             : const char *
     121       11856 : tds_convert_string(TDSSOCKET * tds, TDSICONV * char_conv, const char *s, ptrdiff_t len, size_t *out_len)
     122             : {
     123             :         char *buf;
     124             : 
     125             :         const char *ib;
     126             :         char *ob;
     127             :         size_t il, ol;
     128             : 
     129             :         /* char_conv is only mostly const */
     130       11856 :         TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress;
     131             : 
     132       11856 :         CHECK_TDS_EXTRA(tds);
     133             : 
     134       11856 :         il = len < 0 ? strlen(s) : (size_t) len;
     135       11856 :         if (char_conv->flags == TDS_ENCODING_MEMCPY) {
     136           0 :                 *out_len = il;
     137           0 :                 return s;
     138             :         }
     139             : 
     140             :         /* allocate needed buffer (+1 is to exclude 0 case) */
     141       11856 :         ol = il * char_conv->to.charset.max_bytes_per_char / char_conv->from.charset.min_bytes_per_char + 1;
     142       11856 :         buf = tds_new(char, ol);
     143       11856 :         if (!buf) {
     144           0 :                 *out_len = 0;
     145           0 :                 return NULL;
     146             :         }
     147             : 
     148       11856 :         ib = s;
     149       11856 :         ob = buf;
     150       11856 :         memset(suppress, 0, sizeof(char_conv->suppress));
     151       11856 :         if (tds_iconv(tds, char_conv, to_server, &ib, &il, &ob, &ol) == (size_t)-1) {
     152           0 :                 free(buf);
     153           0 :                 return NULL;
     154             :         }
     155       11856 :         *out_len = ob - buf;
     156       11856 :         return buf;
     157             : }
     158             : 
     159             : #if ENABLE_EXTRA_CHECKS
     160             : void
     161        6594 : tds_convert_string_free(const char *original, const char *converted)
     162             : {
     163       11840 :         if (original != converted)
     164       11840 :                 free((char *) converted);
     165        6594 : }
     166             : #endif
     167             : 
     168             : /**
     169             :  * Flush query packet.
     170             :  * Used at the end of packet write to really send packet to server.
     171             :  * This also changes the state to TDS_PENDING.
     172             :  * \tds
     173             :  */
     174             : static TDSRET
     175             : tds_query_flush_packet(TDSSOCKET *tds)
     176             : {
     177       75164 :         TDSRET ret = tds_flush_packet(tds);
     178             :         /* TODO depend on result ?? */
     179       75164 :         tds_set_state(tds, TDS_PENDING);
     180             :         return ret;
     181             : }
     182             : 
     183             : /**
     184             :  * Set current dynamic.
     185             :  * \tds
     186             :  * \param dyn  dynamic to set
     187             :  */
     188             : void
     189        1010 : tds_set_cur_dyn(TDSSOCKET *tds, TDSDYNAMIC *dyn)
     190             : {
     191        2018 :         if (dyn)
     192        6940 :                 ++dyn->ref_count;
     193        6940 :         tds_release_cur_dyn(tds);
     194        6940 :         tds->cur_dyn = dyn;
     195        1010 : }
     196             : 
     197             : /**
     198             :  * Sends a language string to the database server for
     199             :  * processing.  TDS 4.2 is a plain text message with a packet type of 0x01,
     200             :  * TDS 7.0 is a unicode string with packet type 0x01, and TDS 5.0 uses a 
     201             :  * TDS_LANGUAGE_TOKEN to encapsulate the query and a packet type of 0x0f.
     202             :  * \tds
     203             :  * \param query language query to submit
     204             :  * \return TDS_FAIL or TDS_SUCCESS
     205             :  */
     206             : TDSRET
     207       36083 : tds_submit_query(TDSSOCKET * tds, const char *query)
     208             : {
     209       37979 :         return tds_submit_query_params(tds, query, NULL, NULL);
     210             : }
     211             : 
     212             : /**
     213             :  * Substitute ?-style placeholders with named (\@param) ones.
     214             :  * Sybase does not support ?-style placeholders so convert them.
     215             :  * Also the function replace parameter names.
     216             :  * \param query  query string
     217             :  * \param[in,out] query_len  pointer to query length.
     218             :  *                On input length of input query, on output length
     219             :  *                of output query
     220             :  * \param params  parameters to send to server
     221             :  * \returns new query or NULL on error
     222             :  */
     223             : static char *
     224           2 : tds5_fix_dot_query(const char *query, size_t *query_len, TDSPARAMINFO * params)
     225             : {
     226             :         int i;
     227             :         size_t len, pos;
     228             :         const char *e, *s;
     229           2 :         size_t size = *query_len + 30;
     230             :         char colname[32];
     231             :         char *out;
     232             : 
     233           2 :         out = tds_new(char, size);
     234           2 :         if (!out)
     235             :                 goto memory_error;
     236             :         pos = 0;
     237             : 
     238             :         s = query;
     239          12 :         for (i = 0;; ++i) {
     240          26 :                 e = tds_next_placeholder(s);
     241          14 :                 len = e ? e - s : strlen(s);
     242          14 :                 if (pos + len + 12 >= size) {
     243           0 :                         size = pos + len + 30;
     244           0 :                         if (!TDS_RESIZE(out, size))
     245             :                                 goto memory_error;
     246             :                 }
     247          14 :                 memcpy(out + pos, s, len);
     248          14 :                 pos += len;
     249          14 :                 if (!e)
     250             :                         break;
     251          12 :                 pos += sprintf(out + pos, "@P%d", i + 1);
     252          24 :                 if (!params || i >= params->num_cols)
     253             :                         goto memory_error;
     254          12 :                 sprintf(colname, "@P%d", i + 1);
     255          12 :                 if (!tds_dstr_copy(&params->columns[i]->column_name, colname))
     256             :                         goto memory_error;
     257             : 
     258          12 :                 s = e + 1;
     259             :         }
     260           2 :         out[pos] = 0;
     261           2 :         *query_len = pos;
     262           2 :         return out;
     263             : 
     264           0 : memory_error:
     265           0 :         free(out);
     266           0 :         return NULL;
     267             : }
     268             : 
     269             : /**
     270             :  * Write data to wire
     271             :  * \tds
     272             :  * \param curcol  column where store column information
     273             :  * \return TDS_FAIL on error or TDS_SUCCESS
     274             :  */
     275             : static inline TDSRET
     276             : tds_put_data(TDSSOCKET * tds, TDSCOLUMN * curcol)
     277             : {
     278        9752 :         return curcol->funcs->put_data(tds, curcol, false);
     279             : }
     280             : 
     281             : /**
     282             :  * Start query packet of a given type
     283             :  * \tds
     284             :  * \param packet_type  packet type
     285             :  * \param head         extra information to put in a TDS7 header
     286             :  */
     287             : static TDSRET
     288       66074 : tds_start_query_head(TDSSOCKET *tds, unsigned char packet_type, TDSHEADERS * head)
     289             : {
     290       66074 :         tds->out_flag = packet_type;
     291       66074 :         if (IS_TDS72_PLUS(tds->conn)) {
     292             :                 TDSFREEZE outer;
     293             : 
     294       33167 :                 tds_freeze(tds, &outer, 4);                    /* total length */
     295       33167 :                 tds_put_int(tds, 18);                          /* length: transaction descriptor */
     296       33167 :                 tds_put_smallint(tds, 2);                      /* type: transaction descriptor */
     297       33167 :                 tds_put_n(tds, tds->conn->tds72_transaction, 8);  /* transaction */
     298       33167 :                 tds_put_int(tds, 1);                           /* request count */
     299       33167 :                 if (head && head->qn_msgtext && head->qn_options) {
     300             :                         TDSFREEZE query;
     301             : 
     302           4 :                         tds_freeze(tds, &query, 4);                    /* length: query notification */
     303           4 :                         tds_put_smallint(tds, 1);                      /* type: query notification */
     304             : 
     305           4 :                         TDS_START_LEN_USMALLINT(tds) {
     306           4 :                                 tds_put_string(tds, head->qn_msgtext, -1);     /* notifyid */
     307           4 :                         } TDS_END_LEN
     308             : 
     309           4 :                         TDS_START_LEN_USMALLINT(tds) {
     310           4 :                                 tds_put_string(tds, head->qn_options, -1);     /* ssbdeployment */
     311           4 :                         } TDS_END_LEN
     312             : 
     313           4 :                         if (head->qn_timeout != 0)
     314           4 :                                 tds_put_int(tds, head->qn_timeout);    /* timeout */
     315             : 
     316           4 :                         tds_freeze_close_len(&query, tds_freeze_written(&query));
     317             :                 }
     318       33167 :                 tds_freeze_close_len(&outer, tds_freeze_written(&outer));
     319             :         }
     320       66074 :         return TDS_SUCCESS;
     321             : }
     322             : 
     323             : /**
     324             :  * Start query packet of a given type
     325             :  * \tds
     326             :  * \param packet_type  packet type
     327             :  */
     328             : void
     329         778 : tds_start_query(TDSSOCKET *tds, unsigned char packet_type)
     330             : {
     331             :         /* no need to check return value here because tds_start_query_head() cannot
     332             :         fail when given a NULL head parameter */
     333       10008 :         tds_start_query_head(tds, packet_type, NULL);
     334         778 : }
     335             : 
     336             : /**
     337             :  * Sends a language string to the database server for
     338             :  * processing.  TDS 4.2 is a plain text message with a packet type of 0x01,
     339             :  * TDS 7.0 is a unicode string with packet type 0x01, and TDS 5.0 uses a
     340             :  * TDS_LANGUAGE_TOKEN to encapsulate the query and a packet type of 0x0f.
     341             :  * \tds
     342             :  * \param query  language query to submit
     343             :  * \param params parameters of query
     344             :  * \return TDS_FAIL or TDS_SUCCESS
     345             :  */
     346             : TDSRET
     347       63954 : tds_submit_query_params(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head)
     348             : {
     349             :         size_t query_len;
     350       63954 :         int num_params = params ? params->num_cols : 0;
     351             :  
     352       63954 :         CHECK_TDS_EXTRA(tds);
     353       63954 :         if (params)
     354          30 :                 CHECK_PARAMINFO_EXTRA(params);
     355             :  
     356       63954 :         if (!query)
     357             :                 return TDS_FAIL;
     358             :  
     359       63954 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
     360             :                 return TDS_FAIL;
     361             :  
     362       63952 :         query_len = strlen(query);
     363             :  
     364       63952 :         if (IS_TDS50(tds->conn)) {
     365       10020 :                 char *new_query = NULL;
     366             :                 /* are there '?' style parameters ? */
     367       10020 :                 if (tds_next_placeholder(query)) {
     368           2 :                         if ((new_query = tds5_fix_dot_query(query, &query_len, params)) == NULL) {
     369           0 :                                 tds_set_state(tds, TDS_IDLE);
     370           0 :                                 return TDS_FAIL;
     371             :                         }
     372             :                         query = new_query;
     373             :                 }
     374             : 
     375       10020 :                 tds->out_flag = TDS_NORMAL;
     376       10020 :                 tds_put_byte(tds, TDS_LANGUAGE_TOKEN);
     377       10020 :                 TDS_START_LEN_UINT(tds) {
     378       10020 :                         tds_put_byte(tds, params ? 1 : 0);  /* 1 if there are params, 0 otherwise */
     379       10020 :                         tds_put_string(tds, query, query_len);
     380       10020 :                 } TDS_END_LEN
     381       10020 :                 if (params) {
     382             :                         /* add on parameters */
     383          12 :                         int flags = tds_dstr_isempty(&params->columns[0]->column_name) ? 0 : TDS_PUT_DATA_USE_NAME;
     384           6 :                         TDS_PROPAGATE(tds5_put_params(tds, params, flags));
     385             :                 }
     386       10020 :                 free(new_query);
     387       53932 :         } else if (!IS_TDS7_PLUS(tds->conn) || !params || !params->num_cols) {
     388       53908 :                 if (tds_start_query_head(tds, TDS_QUERY, head) != TDS_SUCCESS)
     389             :                         return TDS_FAIL;
     390       53908 :                 tds_put_string(tds, query, (int)query_len);
     391             :         } else {
     392             :                 TDSCOLUMN *param;
     393             :                 int count, i;
     394             :                 size_t converted_query_len;
     395             :                 const char *converted_query;
     396             :                 TDSFREEZE outer;
     397             :                 TDSRET rc;
     398             : 
     399          24 :                 converted_query =
     400          24 :                         tds_convert_string(tds, tds->conn->char_convs[client2ucs2],
     401             :                                            query, query_len, &converted_query_len);
     402          24 :                 if (!converted_query) {
     403           0 :                         tds_set_state(tds, TDS_IDLE);
     404           0 :                         return TDS_FAIL;
     405             :                 }
     406             : 
     407          48 :                 count = tds_count_placeholders_ucs2le(converted_query, converted_query + converted_query_len);
     408             : 
     409          24 :                 if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
     410             :                         tds_convert_string_free(query, converted_query);
     411             :                         return TDS_FAIL;
     412             :                 }
     413             : 
     414          24 :                 tds_freeze(tds, &outer, 0);
     415             : 
     416             :                 /* procedure name */
     417          24 :                 if (IS_TDS71_PLUS(tds->conn)) {
     418          24 :                         tds_put_smallint(tds, -1);
     419          24 :                         tds_put_smallint(tds, TDS_SP_EXECUTESQL);
     420             :                 } else {
     421           0 :                         TDS_PUT_N_AS_UCS2(tds, "sp_executesql");
     422             :                 }
     423          24 :                 tds_put_smallint(tds, 0);
     424             :  
     425             :                 /* string with sql statement */
     426          24 :                 if (!count) {
     427          16 :                         tds_put_byte(tds, 0);
     428          16 :                         tds_put_byte(tds, 0);
     429          16 :                         tds_put_byte(tds, SYBNTEXT);    /* must be Ntype */
     430          16 :                         TDS_PUT_INT(tds, converted_query_len);
     431          16 :                         if (IS_TDS71_PLUS(tds->conn))
     432          16 :                                 tds_put_n(tds, tds->conn->collation, 5);
     433          16 :                         TDS_PUT_INT(tds, converted_query_len);
     434          16 :                         tds_put_n(tds, converted_query, converted_query_len);
     435             : 
     436          16 :                         rc = tds7_write_param_def_from_params(tds, converted_query, converted_query_len, params);
     437             :                 } else {
     438           8 :                         tds7_put_query_params(tds, converted_query, converted_query_len);
     439             : 
     440           8 :                         rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
     441             :                 }
     442          24 :                 tds_convert_string_free(query, converted_query);
     443          24 :                 if (TDS_FAILED(rc)) {
     444           0 :                         tds_freeze_abort(&outer);
     445           0 :                         return rc;
     446             :                 }
     447          24 :                 tds_freeze_close(&outer);
     448             : 
     449         168 :                 for (i = 0; i < num_params; i++) {
     450         144 :                         param = params->columns[i];
     451         144 :                         TDS_PROPAGATE(tds_put_data_info(tds, param, 0));
     452         144 :                         TDS_PROPAGATE(tds_put_data(tds, param));
     453             :                 }
     454          24 :                 tds->current_op = TDS_OP_EXECUTESQL;
     455             :         }
     456       63952 :         return tds_query_flush_packet(tds);
     457             : }
     458             : 
     459             : /**
     460             :  * Format and submit a query
     461             :  * \tds
     462             :  * \param queryf  query format. printf like expansion is performed on
     463             :  *        this query.
     464             :  */
     465             : TDSRET
     466        1270 : tds_submit_queryf(TDSSOCKET * tds, const char *queryf, ...)
     467             : {
     468             :         va_list ap;
     469        1270 :         char *query = NULL;
     470        1270 :         TDSRET rc = TDS_FAIL;
     471             : 
     472        1270 :         CHECK_TDS_EXTRA(tds);
     473             : 
     474        1270 :         va_start(ap, queryf);
     475        1270 :         if (vasprintf(&query, queryf, ap) >= 0) {
     476        2540 :                 rc = tds_submit_query(tds, query);
     477        1270 :                 free(query);
     478             :         }
     479        1270 :         va_end(ap);
     480        1270 :         return rc;
     481             : }
     482             : 
     483             : /**
     484             :  * Skip a comment in a query
     485             :  * \param s    start of the string (or part of it)
     486             :  * \returns pointer to end of comment
     487             :  */
     488             : const char *
     489         960 : tds_skip_comment(const char *s)
     490             : {
     491         960 :         const char *p = s;
     492             : 
     493         960 :         if (*p == '-' && p[1] == '-') {
     494      218856 :                 for (;*++p != '\0';)
     495      218836 :                         if (*p == '\n')
     496         404 :                                 return p + 1;
     497         536 :         } else if (*p == '/' && p[1] == '*') {
     498         100 :                 ++p;
     499        2066 :                 for(;*++p != '\0';)
     500        1936 :                         if (*p == '*' && p[1] == '/')
     501          70 :                                 return p + 2;
     502             :         } else
     503         436 :                 ++p;
     504             : 
     505             :         return p;
     506             : }
     507             : 
     508             : /**
     509             :  * Skip quoting string (like 'sfsf', "dflkdj" or [dfkjd])
     510             :  * \param s pointer to first quoting character. @verbatim Should be ', " or [. @endverbatim
     511             :  * \return character after quoting
     512             :  */
     513             : const char *
     514       13928 : tds_skip_quoted(const char *s)
     515             : {
     516       32378 :         const char *p = s;
     517       32378 :         char quote = (*s == '[') ? ']' : *s;
     518             : 
     519      394507 :         for (; *++p;) {
     520      380579 :                 if (*p == quote) {
     521       34146 :                         if (*++p != quote)
     522             :                                 return p;
     523             :                 }
     524             :         }
     525             :         return p;
     526             : }
     527             : 
     528             : /**
     529             :  * Get position of next placeholder
     530             :  * \param start pointer to part of query to search
     531             :  * \return next placeholder or NULL if not found
     532             :  */
     533             : const char *
     534       53315 : tds_next_placeholder(const char *start)
     535             : {
     536       53315 :         const char *p = start;
     537             : 
     538       53315 :         if (!p)
     539             :                 return NULL;
     540             : 
     541             :         for (;;) {
     542     2046891 :                 switch (*p) {
     543             :                 case '\0':
     544             :                         return NULL;
     545       18220 :                 case '\'':
     546             :                 case '\"':
     547             :                 case '[':
     548             :                         p = tds_skip_quoted(p);
     549             :                         break;
     550             : 
     551         730 :                 case '-':
     552             :                 case '/':
     553         730 :                         p = tds_skip_comment(p);
     554         730 :                         break;
     555             : 
     556             :                 case '?':
     557             :                         return p;
     558     1974626 :                 default:
     559     1974626 :                         ++p;
     560     1974626 :                         break;
     561             :                 }
     562             :         }
     563             : }
     564             : 
     565             : /**
     566             :  * Count the number of placeholders ('?') in a query
     567             :  * \param query  query string
     568             :  */
     569             : int
     570       32191 : tds_count_placeholders(const char *query)
     571             : {
     572       33033 :         const char *p = query - 1;
     573       33033 :         int count = 0;
     574             : 
     575        7900 :         for (;; ++count) {
     576       47349 :                 if (!(p = tds_next_placeholder(p + 1)))
     577       32191 :                         return count;
     578             :         }
     579             : }
     580             : 
     581             : /**
     582             :  * Skip a comment in a query
     583             :  * \param s    start of the string (or part of it). Encoded in ucs2le
     584             :  * \param end  end of string
     585             :  * \returns pointer to end of comment
     586             :  */
     587             : static const char *
     588         128 : tds_skip_comment_ucs2le(const char *s, const char *end)
     589             : {
     590         128 :         const char *p = s;
     591             : 
     592         128 :         if (p+4 <= end && memcmp(p, "-\0-", 4) == 0) {
     593      120100 :                 for (;(p+=2) < end;)
     594      120080 :                         if (p[0] == '\n' && p[1] == 0)
     595          34 :                                 return p + 2;
     596          74 :         } else if (p+4 <= end && memcmp(p, "/\0*", 4) == 0) {
     597          50 :                 p += 2;
     598          50 :                 end -= 2;
     599         160 :                 for(;(p+=2) < end;)
     600          80 :                         if (memcmp(p, "*\0/", 4) == 0)
     601          20 :                                 return p + 4;
     602             :                 return end + 2;
     603             :         } else
     604          24 :                 p += 2;
     605             : 
     606             :         return p;
     607             : }
     608             : 
     609             : 
     610             : /**
     611             :  * Return pointer to end of a quoted string.
     612             :  * At the beginning pointer should point to delimiter.
     613             :  * \param s    start of string to skip encoded in ucs2le
     614             :  * \param end  pointer to end of string
     615             :  */
     616             : static const char *
     617         710 : tds_skip_quoted_ucs2le(const char *s, const char *end)
     618             : {
     619         710 :         const char *p = s;
     620         710 :         char quote = (*s == '[') ? ']' : *s;
     621             : 
     622         710 :         assert(s[1] == 0 && s < end && (end - s) % 2 == 0);
     623             : 
     624        3434 :         for (; (p += 2) != end;) {
     625        3434 :                 if (p[0] == quote && !p[1]) {
     626         830 :                         p += 2;
     627         830 :                         if (p == end || p[0] != quote || p[1])
     628             :                                 return p;
     629             :                 }
     630             :         }
     631             :         return p;
     632             : }
     633             : 
     634             : /**
     635             :  * Found the next placeholder (? or \@param) in a string.
     636             :  * String must be encoded in ucs2le.
     637             :  * \param start  start of the string (or part of it)
     638             :  * \param end    end of string
     639             :  * \param named  true if named parameters should be returned
     640             :  * \returns either start of next placeholder or end if not found
     641             :  */
     642             : static const char *
     643       28890 : tds_next_placeholder_ucs2le(const char *start, const char *end, int named)
     644             : {
     645       28890 :         const char *p = start;
     646       28890 :         char prev = ' ', c;
     647             : 
     648       28890 :         assert(p && start <= end && (end - start) % 2 == 0);
     649             : 
     650      504102 :         for (; p != end;) {
     651      490128 :                 if (p[1]) {
     652         288 :                         prev = ' ';
     653         288 :                         p += 2;
     654         288 :                         continue;
     655             :                 }
     656      489840 :                 c = p[0];
     657      489840 :                 switch (c) {
     658         480 :                 case '\'':
     659             :                 case '\"':
     660             :                 case '[':
     661         480 :                         p = tds_skip_quoted_ucs2le(p, end);
     662         480 :                         break;
     663             : 
     664          48 :                 case '-':
     665             :                 case '/':
     666          48 :                         p = tds_skip_comment_ucs2le(p, end);
     667          48 :                         c = ' ';
     668          48 :                         break;
     669             : 
     670             :                 case '?':
     671             :                         return p;
     672         192 :                 case '@':
     673         192 :                         if (named && !isalnum((unsigned char) prev))
     674             :                                 return p;
     675             :                 default:
     676      474396 :                         p += 2;
     677      474396 :                         break;
     678             :                 }
     679             :                 prev = c;
     680             :         }
     681             :         return end;
     682             : }
     683             : 
     684             : /**
     685             :  * Count the number of placeholders ('?') in a query
     686             :  * \param query      query encoded in ucs2le
     687             :  * \param query_end  end of query
     688             :  * \return number of placeholders found
     689             :  */
     690             : static int
     691             : tds_count_placeholders_ucs2le(const char *query, const char *query_end)
     692             : {
     693        9324 :         const char *p = query - 2;
     694        9324 :         int count = 0;
     695             : 
     696        9928 :         for (;; ++count) {
     697       19252 :                 if ((p = tds_next_placeholder_ucs2le(p + 2, query_end, 0)) == query_end)
     698             :                         return count;
     699             :         }
     700             : }
     701             : 
     702             : static const char*
     703             : tds50_char_declaration_from_usertype(TDSSOCKET *tds, TDS_INT usertype, unsigned int *p_size)
     704             : {
     705           0 :         switch (usertype) {
     706             :         case USER_CHAR_TYPE:
     707             :                 return "CHAR(%u)";
     708           0 :         case USER_VARCHAR_TYPE:
     709             :                 return "VARCHAR(%u)";
     710           0 :         case USER_SYSNAME_TYPE:
     711             :                 return "SYSNAME";
     712           0 :         case USER_NCHAR_TYPE:
     713           0 :                 *p_size /= tds->conn->ncharsize;
     714             :                 return "NCHAR(%u)";
     715           0 :         case USER_NVARCHAR_TYPE:
     716           0 :                 *p_size /= tds->conn->ncharsize;
     717             :                 return "NVARCHAR(%u)";
     718             :         }
     719             :         return NULL;
     720             : }
     721             : 
     722             : /**
     723             :  * Return declaration for column (like "varchar(20)").
     724             :  *
     725             :  * This depends on:
     726             :  * - on_server.column_type
     727             :  * - varint_size (for varchar(max) distinction)
     728             :  * - column_size
     729             :  * - precision/scale (numeric)
     730             :  *
     731             :  * \tds
     732             :  * \param curcol column
     733             :  * \param out    buffer to hold declaration
     734             :  * \return TDS_FAIL or TDS_SUCCESS
     735             :  */
     736             : TDSRET
     737        8316 : tds_get_column_declaration(TDSSOCKET * tds, TDSCOLUMN * curcol, char *out)
     738             : {
     739        8316 :         const char *fmt = NULL;
     740             :         /* unsigned int is required by printf format, don't use size_t */
     741        8316 :         unsigned int max_len = IS_TDS7_PLUS(tds->conn) ? 8000 : 255;
     742             :         unsigned int size;
     743             :         TDS_SERVER_TYPE conversion_type;
     744             : 
     745        8316 :         CHECK_TDS_EXTRA(tds);
     746        8316 :         CHECK_COLUMN_EXTRA(curcol);
     747             : 
     748        8316 :         size = (unsigned int) tds_fix_column_size(tds, curcol);
     749             : 
     750        8316 :         conversion_type = tds_get_conversion_type(curcol->on_server.column_type, curcol->on_server.column_size);
     751        8316 :         switch (conversion_type) {
     752         318 :         case XSYBCHAR:
     753         318 :                 if (IS_TDS50(tds->conn)) {
     754           0 :                         max_len = 32767;
     755           0 :                         fmt = tds50_char_declaration_from_usertype(tds, curcol->column_usertype, &size);
     756             :                         if (fmt != NULL)
     757             :                                 break;
     758             :                 }
     759             :         case SYBCHAR:
     760             :                 fmt = "CHAR(%u)";
     761             :                 break;
     762          10 :         case SYBVARCHAR:
     763          10 :                 if (IS_TDS50(tds->conn)) {
     764           0 :                         fmt = tds50_char_declaration_from_usertype(tds, curcol->column_usertype, &size);
     765             :                         if (fmt != NULL)
     766             :                                 break;
     767             :                 }
     768             :         case XSYBVARCHAR:
     769        1522 :                 if (curcol->column_varint_size == 8)
     770             :                         fmt = "VARCHAR(MAX)";
     771             :                 else
     772        1206 :                         fmt = "VARCHAR(%u)";
     773             :                 break;
     774         172 :         case SYBUINT1:
     775             :         case SYBINT1:
     776         172 :                 fmt = "TINYINT";
     777         172 :                 break;
     778         150 :         case SYBINT2:
     779         150 :                 fmt = "SMALLINT";
     780         150 :                 break;
     781         906 :         case SYBINT4:
     782         906 :                 fmt = "INT";
     783         906 :                 break;
     784          98 :         case SYBINT8:
     785             :         case SYB5INT8:
     786             :                 /* TODO even for Sybase ?? */
     787          98 :                 fmt = "BIGINT";
     788          98 :                 break;
     789         406 :         case SYBFLT8:
     790         406 :                 fmt = "FLOAT";
     791         406 :                 break;
     792         394 :         case SYBDATETIME:
     793         394 :                 fmt = "DATETIME";
     794         394 :                 break;
     795          30 :         case SYBDATE:
     796          30 :                 fmt = "DATE";
     797          30 :                 break;
     798          30 :         case SYBTIME:
     799          30 :                 fmt = "TIME";
     800          30 :                 break;
     801         336 :         case SYBBIT:
     802         336 :                 fmt = "BIT";
     803         336 :                 break;
     804         378 :         case SYBTEXT:
     805         378 :                 fmt = "TEXT";
     806         378 :                 break;
     807         148 :         case SYBLONGBINARY:     /* TODO correct ?? */
     808             :         case SYBIMAGE:
     809         148 :                 if (IS_TDS50(tds->conn)) {
     810          10 :                         switch (curcol->column_usertype) {
     811           0 :                         case USER_UNICHAR_TYPE:
     812           0 :                                 size /= 2u;
     813           0 :                                 max_len = 8192;
     814           0 :                                 fmt = "UNICHAR(%u)";
     815           0 :                                 break;
     816           0 :                         case USER_UNIVARCHAR_TYPE:
     817           0 :                                 size /= 2u;
     818           0 :                                 max_len = 8192;
     819           0 :                                 fmt = "UNIVARCHAR(%u)";
     820           0 :                                 break;
     821           0 :                         case USER_UNITEXT_TYPE:
     822           0 :                                 fmt = "UNITEXT";
     823           0 :                                 break;
     824             :                         }
     825          10 :                         if (fmt != NULL)
     826             :                                 break;
     827             :                 }
     828             :                 fmt = "IMAGE";
     829             :                 break;
     830          94 :         case SYBMONEY4:
     831          94 :                 fmt = "SMALLMONEY";
     832          94 :                 break;
     833         128 :         case SYBMONEY:
     834         128 :                 fmt = "MONEY";
     835         128 :                 break;
     836          94 :         case SYBDATETIME4:
     837          94 :                 fmt = "SMALLDATETIME";
     838          94 :                 break;
     839         334 :         case SYBREAL:
     840         334 :                 fmt = "REAL";
     841         334 :                 break;
     842          44 :         case SYBBINARY:
     843             :         case XSYBBINARY:
     844          44 :                 fmt = "BINARY(%u)";
     845          44 :                 break;
     846         352 :         case SYBVARBINARY:
     847             :         case XSYBVARBINARY:
     848         352 :                 if (curcol->column_varint_size == 8)
     849             :                         fmt = "VARBINARY(MAX)";
     850             :                 else
     851         252 :                         fmt = "VARBINARY(%u)";
     852             :                 break;
     853             :         case SYBNUMERIC:
     854             :                 fmt = "NUMERIC(%d,%d)";
     855             :                 goto numeric_decimal;
     856          78 :         case SYBDECIMAL:
     857          78 :                 fmt = "DECIMAL(%d,%d)";
     858         216 :               numeric_decimal:
     859         216 :                 sprintf(out, fmt, curcol->column_prec, curcol->column_scale);
     860         216 :                 return TDS_SUCCESS;
     861             :                 break;
     862          62 :         case SYBUNIQUE:
     863          62 :                 if (IS_TDS7_PLUS(tds->conn))
     864          62 :                         fmt = "UNIQUEIDENTIFIER";
     865             :                 break;
     866         236 :         case SYBNTEXT:
     867         236 :                 if (IS_TDS7_PLUS(tds->conn))
     868         236 :                         fmt = "NTEXT";
     869             :                 break;
     870         636 :         case SYBNVARCHAR:
     871             :         case XSYBNVARCHAR:
     872         636 :                 if (curcol->column_varint_size == 8) {
     873             :                         fmt = "NVARCHAR(MAX)";
     874         396 :                 } else if (IS_TDS7_PLUS(tds->conn)) {
     875         396 :                         fmt = "NVARCHAR(%u)";
     876         396 :                         max_len = 4000;
     877         396 :                         size /= 2u;
     878             :                 }
     879             :                 break;
     880         240 :         case XSYBNCHAR:
     881         240 :                 if (IS_TDS7_PLUS(tds->conn)) {
     882         240 :                         fmt = "NCHAR(%u)";
     883         240 :                         max_len = 4000;
     884         240 :                         size /= 2u;
     885             :                 }
     886             :                 break;
     887         490 :         case SYBVARIANT:
     888         490 :                 if (IS_TDS7_PLUS(tds->conn))
     889         490 :                         fmt = "SQL_VARIANT";
     890             :                 break;
     891             :                 /* TODO support scale !! */
     892          42 :         case SYBMSTIME:
     893          42 :                 fmt = "TIME";
     894          42 :                 break;
     895          42 :         case SYBMSDATE:
     896          42 :                 fmt = "DATE";
     897          42 :                 break;
     898         278 :         case SYBMSDATETIME2:
     899         278 :                 fmt = "DATETIME2";
     900         278 :                 break;
     901          18 :         case SYBMSDATETIMEOFFSET:
     902          18 :                 fmt = "DATETIMEOFFSET";
     903          18 :                 break;
     904          10 :         case SYB5BIGTIME:
     905          10 :                 fmt = "BIGTIME";
     906          10 :                 break;
     907          10 :         case SYB5BIGDATETIME:
     908          10 :                 fmt = "BIGDATETIME";
     909          10 :                 break;
     910          30 :         case SYBUINT2:
     911          30 :                 fmt = "UNSIGNED SMALLINT";
     912          30 :                 break;
     913          30 :         case SYBUINT4:
     914          30 :                 fmt = "UNSIGNED INT";
     915          30 :                 break;
     916          30 :         case SYBUINT8:
     917          30 :                 fmt = "UNSIGNED BIGINT";
     918          30 :                 break;
     919           0 :         case SYBXML:
     920             :         case SYBMSXML:
     921           0 :                 fmt = "XML";
     922           0 :                 break;
     923           0 :         case SYBUNITEXT:
     924           0 :                 fmt = "UNITEXT";
     925           0 :                 break;
     926             :                 /* nullable types should not occur here... */
     927             :         case SYBFLTN:
     928             :         case SYBMONEYN:
     929             :         case SYBDATETIMN:
     930             :         case SYBBITN:
     931             :         case SYBINTN:
     932             :         case SYBUINTN:
     933             :         case SYBDATEN:
     934             :         case SYBTIMEN:
     935           0 :                 assert(0);
     936             :                 /* TODO... */
     937             : #if ENABLE_EXTRA_CHECKS
     938             :         case SYBVOID:
     939             :         case SYBSINT1:
     940             :         case SYBMSUDT:
     941             :         case SYBMSTABLE:
     942             :         case SYBINTERVAL:
     943             : #else
     944             :         default:
     945             : #endif
     946           0 :                 tdsdump_log(TDS_DBG_ERROR, "Unknown type %d\n", conversion_type);
     947             :                 break;
     948             :         }
     949             : 
     950        8100 :         if (fmt) {
     951             :                 /* fill out */
     952        8100 :                 sprintf(out, fmt, size > 0 ? TDS_MIN(size, max_len) : 1u);
     953        8100 :                 return TDS_SUCCESS;
     954             :         }
     955             : 
     956           0 :         out[0] = 0;
     957           0 :         return TDS_FAIL;
     958             : }
     959             : 
     960             : /**
     961             :  * Write string with parameters definition, useful for TDS7+.
     962             :  * Looks like "@P1 INT, @P2 VARCHAR(100)"
     963             :  * \param tds     state information for the socket and the TDS protocol
     964             :  * \param converted_query     query to send to server in ucs2le encoding
     965             :  * \param converted_query_len query length in bytes
     966             :  * \param params  parameters to build declaration
     967             :  * \return result of write
     968             :  */
     969             : /* TODO find a better name for this function */
     970             : static TDSRET
     971        4650 : tds7_write_param_def_from_query(TDSSOCKET * tds, const char* converted_query, size_t converted_query_len, TDSPARAMINFO * params)
     972             : {
     973             :         char declaration[128], *p;
     974             :         int i, count;
     975             :         unsigned int written;
     976             :         TDSFREEZE outer, inner;
     977             : 
     978        4650 :         assert(IS_TDS7_PLUS(tds->conn));
     979             : 
     980        4650 :         CHECK_TDS_EXTRA(tds);
     981        4650 :         if (params)
     982        4120 :                 CHECK_PARAMINFO_EXTRA(params);
     983             : 
     984        9300 :         count = tds_count_placeholders_ucs2le(converted_query, converted_query + converted_query_len);
     985             : 
     986             :         /* string with parameters types */
     987        4650 :         tds_put_byte(tds, 0);
     988        4650 :         tds_put_byte(tds, 0);
     989        4650 :         tds_put_byte(tds, SYBNTEXT);    /* must be Ntype */
     990             : 
     991             :         /* put parameters definitions */
     992        4650 :         tds_freeze(tds, &outer, 4);
     993        4650 :         if (IS_TDS71_PLUS(tds->conn))
     994        4650 :                 tds_put_n(tds, tds->conn->collation, 5);
     995        4650 :         tds_freeze(tds, &inner, 4);
     996             : 
     997        9590 :         for (i = 0; i < count; ++i) {
     998        4940 :                 p = declaration;
     999        4940 :                 if (i)
    1000         612 :                         *p++ = ',';
    1001             : 
    1002             :                 /* get this parameter declaration */
    1003        4940 :                 p += sprintf(p, "@P%d ", i + 1);
    1004        4940 :                 if (!params || i >= params->num_cols) {
    1005             :                         /* type unknown, use a type able to hold any value */
    1006         384 :                         strcpy(p, IS_TDS72_PLUS(tds->conn) ? "varchar(max)" : "varchar(4000)");
    1007        4556 :                 } else if (TDS_FAILED(tds_get_column_declaration(tds, params->columns[i], p))) {
    1008           0 :                         tds_freeze_abort(&inner);
    1009           0 :                         tds_freeze_abort(&outer);
    1010           0 :                         return TDS_FAIL;
    1011             :                 }
    1012             : 
    1013        4940 :                 tds_put_string(tds, declaration, -1);
    1014             :         }
    1015             : 
    1016        4650 :         written = tds_freeze_written(&inner) - 4;
    1017        4650 :         tds_freeze_close_len(&inner, written ? written : -1);
    1018        4650 :         tds_freeze_close_len(&outer, written);
    1019        4650 :         return TDS_SUCCESS;
    1020             : }
    1021             : 
    1022             : /**
    1023             :  * Write string with parameters definition, useful for TDS7+.
    1024             :  * Looks like "@P1 INT, @P2 VARCHAR(100)"
    1025             :  * \param tds       state information for the socket and the TDS protocol
    1026             :  * \param query     query to send to server encoded in ucs2le
    1027             :  * \param query_len query length in bytes
    1028             :  * \param params    parameters to build declaration
    1029             :  * \return result of the operation
    1030             :  */
    1031             : /* TODO find a better name for this function */
    1032             : static TDSRET
    1033          16 : tds7_write_param_def_from_params(TDSSOCKET * tds, const char* query, size_t query_len, TDSPARAMINFO * params)
    1034             : {
    1035             :         char declaration[40];
    1036             :         int i;
    1037             :         struct tds_ids {
    1038             :                 const char *p;
    1039             :                 size_t len;
    1040          16 :         } *ids = NULL;
    1041             :         TDSFREEZE outer, inner;
    1042             :         unsigned int written;
    1043             : 
    1044          16 :         assert(IS_TDS7_PLUS(tds->conn));
    1045             : 
    1046          16 :         CHECK_TDS_EXTRA(tds);
    1047          16 :         if (params)
    1048          16 :                 CHECK_PARAMINFO_EXTRA(params);
    1049             : 
    1050             :         /* string with parameters types */
    1051          16 :         tds_put_byte(tds, 0);
    1052          16 :         tds_put_byte(tds, 0);
    1053          16 :         tds_put_byte(tds, SYBNTEXT);    /* must be Ntype */
    1054             : 
    1055             :         /* put parameters definitions */
    1056          16 :         tds_freeze(tds, &outer, 4);
    1057          16 :         if (IS_TDS71_PLUS(tds->conn))
    1058          16 :                 tds_put_n(tds, tds->conn->collation, 5);
    1059          16 :         tds_freeze(tds, &inner, 4);
    1060             : 
    1061          16 :         if (!params || !params->num_cols) {
    1062           0 :                 tds_freeze_close_len(&inner, -1);
    1063           0 :                 tds_freeze_close_len(&outer, 0);
    1064           0 :                 return TDS_SUCCESS;
    1065             :         }
    1066             : 
    1067             :         /* try to detect missing names */
    1068          16 :         ids = tds_new0(struct tds_ids, params->num_cols);
    1069          16 :         if (!ids)
    1070             :                 goto Cleanup;
    1071          32 :         if (tds_dstr_isempty(&params->columns[0]->column_name)) {
    1072           8 :                 const char *s = query, *e, *id_end;
    1073           8 :                 const char *query_end = query + query_len;
    1074             : 
    1075          56 :                 for (i = 0;  i < params->num_cols; s = e + 2) {
    1076          48 :                         e = tds_next_placeholder_ucs2le(s, query_end, 1);
    1077          48 :                         if (e == query_end)
    1078             :                                 break;
    1079          48 :                         if (e[0] != '@')
    1080           0 :                                 continue;
    1081             :                         /* find end of param name */
    1082         304 :                         for (id_end = e + 2; id_end != query_end; id_end += 2)
    1083         304 :                                 if (!id_end[1] && (id_end[0] != '_' && id_end[1] != '#' && !isalnum((unsigned char) id_end[0])))
    1084             :                                         break;
    1085          48 :                         ids[i].p = e;
    1086          48 :                         ids[i].len = id_end - e;
    1087          48 :                         ++i;
    1088             :                 }
    1089             :         }
    1090             : 
    1091          96 :         for (i = 0; i < params->num_cols; ++i) {
    1092          96 :                 if (i)
    1093          80 :                         tds_put_smallint(tds, ',');
    1094             : 
    1095             :                 /* this part of buffer can be not-ascii compatible, use all ucs2... */
    1096          96 :                 if (ids[i].p) {
    1097          48 :                         tds_put_n(tds, ids[i].p, ids[i].len);
    1098             :                 } else {
    1099          48 :                         const DSTR *name = &params->columns[i]->column_name;
    1100         144 :                         tds_put_string(tds, tds_dstr_cstr(name), (int) tds_dstr_len(name));
    1101             :                 }
    1102          96 :                 tds_put_smallint(tds, ' ');
    1103             : 
    1104             :                 /* get this parameter declaration */
    1105          96 :                 tds_get_column_declaration(tds, params->columns[i], declaration);
    1106          96 :                 if (!declaration[0])
    1107             :                         goto Cleanup;
    1108          96 :                 tds_put_string(tds, declaration, -1);
    1109             :         }
    1110          16 :         free(ids);
    1111             : 
    1112          16 :         written = tds_freeze_written(&inner) - 4;
    1113          16 :         tds_freeze_close_len(&inner, written);
    1114          16 :         tds_freeze_close_len(&outer, written);
    1115             : 
    1116          16 :         return TDS_SUCCESS;
    1117             : 
    1118           0 :       Cleanup:
    1119           0 :         free(ids);
    1120           0 :         tds_freeze_abort(&inner);
    1121           0 :         tds_freeze_abort(&outer);
    1122           0 :         return TDS_FAIL;
    1123             : }
    1124             : 
    1125             : 
    1126             : /**
    1127             :  * Output params types and query (required by sp_prepare/sp_executesql/sp_prepexec)
    1128             :  * \param tds       state information for the socket and the TDS protocol
    1129             :  * \param query     query (encoded in ucs2le)
    1130             :  * \param query_len query length in bytes
    1131             :  */
    1132             : static void
    1133        4650 : tds7_put_query_params(TDSSOCKET * tds, const char *query, size_t query_len)
    1134             : {
    1135             :         size_t len;
    1136             :         int i, num_placeholders;
    1137             :         const char *s, *e;
    1138             :         char buf[24];
    1139        4650 :         const char *const query_end = query + query_len;
    1140             : 
    1141        4650 :         CHECK_TDS_EXTRA(tds);
    1142             : 
    1143        4650 :         assert(IS_TDS7_PLUS(tds->conn));
    1144             : 
    1145             :         /* we use all "@PX" for parameters */
    1146        4650 :         num_placeholders = tds_count_placeholders_ucs2le(query, query_end);
    1147        4650 :         len = num_placeholders * 2;
    1148             :         /* adjust for the length of X */
    1149        4666 :         for (i = 10; i <= num_placeholders; i *= 10) {
    1150          16 :                 len += num_placeholders - i + 1;
    1151             :         }
    1152             : 
    1153             :         /* string with sql statement */
    1154             :         /* replace placeholders with dummy parametes */
    1155        4650 :         tds_put_byte(tds, 0);
    1156        4650 :         tds_put_byte(tds, 0);
    1157        4650 :         tds_put_byte(tds, SYBNTEXT);    /* must be Ntype */
    1158        4650 :         len = 2u * len + query_len;
    1159        4650 :         TDS_PUT_INT(tds, len);
    1160        4650 :         if (IS_TDS71_PLUS(tds->conn))
    1161        4650 :                 tds_put_n(tds, tds->conn->collation, 5);
    1162        4650 :         TDS_PUT_INT(tds, len);
    1163        4650 :         s = query;
    1164             :         /* TODO do a test with "...?" and "...?)" */
    1165        9590 :         for (i = 1;; ++i) {
    1166       14530 :                 e = tds_next_placeholder_ucs2le(s, query_end, 0);
    1167        9590 :                 assert(e && query <= e && e <= query_end);
    1168        9590 :                 tds_put_n(tds, s, e - s);
    1169        9590 :                 if (e == query_end)
    1170             :                         break;
    1171        4940 :                 sprintf(buf, "@P%d", i);
    1172        4940 :                 tds_put_string(tds, buf, -1);
    1173        4940 :                 s = e + 2;
    1174             :         }
    1175        4650 : }
    1176             : 
    1177             : /**
    1178             :  * Creates a temporary stored procedure in the server.
    1179             :  *
    1180             :  * Under TDS 4.2 dynamic statements are emulated building sql command.
    1181             :  * TDS 5 does not uses parameters type.
    1182             :  * TDS 7+ uses parameter types to prepare the query. You should
    1183             :  * prepare again the query if parameters changes.
    1184             :  * \param tds     state information for the socket and the TDS protocol
    1185             :  * \param query   language query with given placeholders (?)
    1186             :  * \param id      string to identify the dynamic query. Pass NULL for automatic generation.
    1187             :  * \param dyn_out will receive allocated TDSDYNAMIC*. Any older allocated dynamic won't be freed, Can be NULL.
    1188             :  * \param params  parameters to use. It can be NULL even if parameters are present. Used only for TDS7+
    1189             :  * \return TDS_FAIL or TDS_SUCCESS
    1190             :  */
    1191             : /* TODO parse all results ?? */
    1192             : TDSRET
    1193         692 : tds_submit_prepare(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params)
    1194             : {
    1195             :         int query_len;
    1196         692 :         TDSRET rc = TDS_FAIL;
    1197             :         TDSDYNAMIC *dyn;
    1198             : 
    1199         692 :         CHECK_TDS_EXTRA(tds);
    1200         692 :         if (params)
    1201          64 :                 CHECK_PARAMINFO_EXTRA(params);
    1202             : 
    1203         692 :         if (!query || !dyn_out)
    1204             :                 return TDS_FAIL;
    1205             : 
    1206         692 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    1207             :                 return TDS_FAIL;
    1208             : 
    1209             :         /* allocate a structure for this thing */
    1210         692 :         dyn = tds_alloc_dynamic(tds->conn, id);
    1211         692 :         if (!dyn)
    1212             :                 return TDS_FAIL;
    1213         692 :         tds_release_dynamic(dyn_out);
    1214         692 :         *dyn_out = dyn;
    1215         692 :         tds_release_cur_dyn(tds);
    1216             : 
    1217             :         /* TDS5 sometimes cannot accept prepare so we need to store query */
    1218         692 :         if (!IS_TDS7_PLUS(tds->conn)) {
    1219         364 :                 dyn->query = strdup(query);
    1220         364 :                 if (!dyn->query)
    1221             :                         goto failure;
    1222             :         }
    1223             : 
    1224         692 :         if (!IS_TDS50(tds->conn) && !IS_TDS7_PLUS(tds->conn)) {
    1225           0 :                 dyn->emulated = true;
    1226           0 :                 tds_dynamic_deallocated(tds->conn, dyn);
    1227           0 :                 tds_set_state(tds, TDS_IDLE);
    1228           0 :                 return TDS_SUCCESS;
    1229             :         }
    1230             : 
    1231         692 :         query_len = (int)strlen(query);
    1232             : 
    1233         692 :         tds_set_cur_dyn(tds, dyn);
    1234             : 
    1235         692 :         if (IS_TDS7_PLUS(tds->conn)) {
    1236             :                 size_t converted_query_len;
    1237             :                 const char *converted_query;
    1238             :                 TDSFREEZE outer;
    1239             :                 TDSRET rc;
    1240             : 
    1241         328 :                 converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, query_len, &converted_query_len);
    1242         328 :                 if (!converted_query)
    1243             :                         goto failure;
    1244             : 
    1245         328 :                 tds_freeze(tds, &outer, 0);
    1246         328 :                 tds_start_query(tds, TDS_RPC);
    1247             :                 /* procedure name */
    1248         328 :                 if (IS_TDS71_PLUS(tds->conn)) {
    1249         328 :                         tds_put_smallint(tds, -1);
    1250         328 :                         tds_put_smallint(tds, TDS_SP_PREPARE);
    1251             :                 } else {
    1252           0 :                         TDS_PUT_N_AS_UCS2(tds, "sp_prepare");
    1253             :                 }
    1254         328 :                 tds_put_smallint(tds, 0);
    1255             : 
    1256             :                 /* return param handle (int) */
    1257         328 :                 tds_put_byte(tds, 0);
    1258         328 :                 tds_put_byte(tds, 1);   /* result */
    1259         328 :                 tds_put_byte(tds, SYBINTN);
    1260         328 :                 tds_put_byte(tds, 4);
    1261         328 :                 tds_put_byte(tds, 0);
    1262             : 
    1263         328 :                 rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
    1264         328 :                 tds7_put_query_params(tds, converted_query, converted_query_len);
    1265         328 :                 tds_convert_string_free(query, converted_query);
    1266         328 :                 if (TDS_FAILED(rc)) {
    1267           0 :                         tds_freeze_abort(&outer);
    1268           0 :                         return rc;
    1269             :                 }
    1270         328 :                 tds_freeze_close(&outer);
    1271             : 
    1272             :                 /* options, 1 == RETURN_METADATA */
    1273         328 :                 tds_put_byte(tds, 0);
    1274         328 :                 tds_put_byte(tds, 0);
    1275         328 :                 tds_put_byte(tds, SYBINTN);
    1276         328 :                 tds_put_byte(tds, 4);
    1277         328 :                 tds_put_byte(tds, 4);
    1278         328 :                 tds_put_int(tds, 1);
    1279             : 
    1280         328 :                 tds->current_op = TDS_OP_PREPARE;
    1281             :         } else {
    1282         364 :                 tds->out_flag = TDS_NORMAL;
    1283             : 
    1284         364 :                 tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
    1285         364 :                 TDS_START_LEN_USMALLINT(tds) {
    1286         364 :                         tds_put_byte(tds, TDS_DYN_PREPARE);
    1287         364 :                         tds_put_byte(tds, 0x00);
    1288         364 :                         TDS_START_LEN_TINYINT(tds) {
    1289         364 :                                 tds_put_string(tds, dyn->id, -1);
    1290         364 :                         } TDS_END_LEN
    1291             : 
    1292             :                         /* TODO how to pass parameters type? like store procedures ? */
    1293         364 :                         TDS_START_LEN_USMALLINT(tds) {
    1294         728 :                                 if (tds_capability_has_req(tds->conn, TDS_REQ_PROTO_DYNPROC)) {
    1295         364 :                                         tds_put_n(tds, "create proc ", 12);
    1296         364 :                                         tds_put_string(tds, dyn->id, -1);
    1297         364 :                                         tds_put_n(tds, " as ", 4);
    1298             :                                 }
    1299         364 :                                 tds_put_string(tds, query, query_len);
    1300         364 :                         } TDS_END_LEN
    1301         364 :                 } TDS_END_LEN
    1302             :         }
    1303             : 
    1304         692 :         rc = tds_query_flush_packet(tds);
    1305         692 :         if (TDS_SUCCEED(rc))
    1306             :                 return rc;
    1307             : 
    1308          20 : failure:
    1309             :         /* TODO correct if writing fail ?? */
    1310          20 :         tds_set_state(tds, TDS_IDLE);
    1311             : 
    1312          20 :         tds_release_dynamic(dyn_out);
    1313          20 :         tds_dynamic_deallocated(tds->conn, dyn);
    1314          20 :         return rc;
    1315             : }
    1316             : 
    1317             : /**
    1318             :  * Submit a prepared query with parameters
    1319             :  * \param tds     state information for the socket and the TDS protocol
    1320             :  * \param query   language query with given placeholders (?)
    1321             :  * \param params  parameters to send
    1322             :  * \return TDS_FAIL or TDS_SUCCESS
    1323             :  */
    1324             : TDSRET
    1325         814 : tds_submit_execdirect(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params, TDSHEADERS * head)
    1326             : {
    1327             :         size_t query_len;
    1328             :         TDSCOLUMN *param;
    1329             :         TDSDYNAMIC *dyn;
    1330             :         unsigned int id_len;
    1331             :         TDSFREEZE outer;
    1332             : 
    1333         814 :         CHECK_TDS_EXTRA(tds);
    1334         814 :         CHECK_PARAMINFO_EXTRA(params);
    1335             : 
    1336         814 :         if (!query)
    1337             :                 return TDS_FAIL;
    1338         814 :         query_len = strlen(query);
    1339             : 
    1340         814 :         if (IS_TDS7_PLUS(tds->conn)) {
    1341             :                 int i;
    1342             :                 size_t converted_query_len;
    1343             :                 const char *converted_query;
    1344             :                 TDSRET rc;
    1345             : 
    1346         702 :                 if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    1347             :                         return TDS_FAIL;
    1348             : 
    1349         702 :                 converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, (int)query_len, &converted_query_len);
    1350         702 :                 if (!converted_query) {
    1351           0 :                         tds_set_state(tds, TDS_IDLE);
    1352           0 :                         return TDS_FAIL;
    1353             :                 }
    1354             : 
    1355         702 :                 if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS) {
    1356             :                         tds_convert_string_free(query, converted_query);
    1357             :                         return TDS_FAIL;
    1358             :                 }
    1359         702 :                 tds_freeze(tds, &outer, 0);
    1360             :                 /* procedure name */
    1361         702 :                 if (IS_TDS71_PLUS(tds->conn)) {
    1362         702 :                         tds_put_smallint(tds, -1);
    1363         702 :                         tds_put_smallint(tds, TDS_SP_EXECUTESQL);
    1364             :                 } else {
    1365           0 :                         TDS_PUT_N_AS_UCS2(tds, "sp_executesql");
    1366             :                 }
    1367         702 :                 tds_put_smallint(tds, 0);
    1368             : 
    1369         702 :                 tds7_put_query_params(tds, converted_query, converted_query_len);
    1370         702 :                 rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
    1371         702 :                 tds_convert_string_free(query, converted_query);
    1372         702 :                 if (TDS_FAILED(rc)) {
    1373           0 :                         tds_freeze_abort(&outer);
    1374           0 :                         return rc;
    1375             :                 }
    1376         702 :                 tds_freeze_close(&outer);
    1377             : 
    1378        1424 :                 for (i = 0; i < params->num_cols; i++) {
    1379         722 :                         param = params->columns[i];
    1380         722 :                         TDS_PROPAGATE(tds_put_data_info(tds, param, 0));
    1381         722 :                         TDS_PROPAGATE(tds_put_data(tds, param));
    1382             :                 }
    1383             : 
    1384         702 :                 tds->current_op = TDS_OP_EXECUTESQL;
    1385         702 :                 return tds_query_flush_packet(tds);
    1386             :         }
    1387             : 
    1388             :         /* allocate a structure for this thing */
    1389         112 :         dyn = tds_alloc_dynamic(tds->conn, NULL);
    1390             : 
    1391         112 :         if (!dyn)
    1392             :                 return TDS_FAIL;
    1393             :         /* check if no parameters */
    1394         112 :         if (params && !params->num_cols)
    1395           0 :                 params = NULL;
    1396             : 
    1397             :         /* TDS 4.2, emulate prepared statements */
    1398             :         /*
    1399             :          * TODO Sybase seems to not support parameters in prepared execdirect
    1400             :          * so use language or prepare and then exec
    1401             :          */
    1402         112 :         if (!IS_TDS50(tds->conn) || params) {
    1403         112 :                 TDSRET ret = TDS_SUCCESS;
    1404             : 
    1405         112 :                 if (!params) {
    1406           0 :                         ret = tds_submit_query(tds, query);
    1407             :                 } else {
    1408         112 :                         dyn->emulated = true;
    1409         112 :                         dyn->params = params;
    1410         112 :                         dyn->query = strdup(query);
    1411         112 :                         if (!dyn->query)
    1412             :                                 ret = TDS_FAIL;
    1413             :                         if (TDS_SUCCEED(ret))
    1414         112 :                                 if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    1415             :                                         ret = TDS_FAIL;
    1416         112 :                         if (TDS_SUCCEED(ret)) {
    1417         112 :                                 ret = tds_send_emulated_execute(tds, dyn->query, dyn->params);
    1418         112 :                                 if (TDS_SUCCEED(ret))
    1419         112 :                                         ret = tds_query_flush_packet(tds);
    1420             :                         }
    1421             :                         /* do not free our parameters */
    1422         112 :                         dyn->params = NULL;
    1423             :                 }
    1424         112 :                 tds_dynamic_deallocated(tds->conn, dyn);
    1425         112 :                 tds_release_dynamic(&dyn);
    1426         112 :                 return ret;
    1427             :         }
    1428             : 
    1429           0 :         tds_release_cur_dyn(tds);
    1430           0 :         tds->cur_dyn = dyn;
    1431             : 
    1432           0 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    1433             :                 return TDS_FAIL;
    1434             : 
    1435           0 :         tds->out_flag = TDS_NORMAL;
    1436             : 
    1437           0 :         id_len = (unsigned int) strlen(dyn->id);
    1438           0 :         tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
    1439           0 :         tds_freeze(tds, &outer, 2);
    1440           0 :         tds_put_byte(tds, TDS_DYN_EXEC_IMMED);
    1441           0 :         tds_put_byte(tds, params ? 0x01 : 0);
    1442           0 :         TDS_START_LEN_TINYINT(tds) {
    1443           0 :                 tds_put_string(tds, dyn->id, id_len);
    1444           0 :         } TDS_END_LEN
    1445             :         /* TODO how to pass parameters type? like store procedures ? */
    1446           0 :         TDS_START_LEN_USMALLINT(tds) {
    1447           0 :                 tds_put_n(tds, "create proc ", 12);
    1448           0 :                 tds_put_string(tds, dyn->id, id_len);
    1449           0 :                 tds_put_n(tds, " as ", 4);
    1450           0 :                 tds_put_string(tds, query, query_len);
    1451           0 :         } TDS_END_LEN
    1452           0 :         tds_freeze_close(&outer);
    1453             : 
    1454             :         if (params)
    1455             :                 TDS_PROPAGATE(tds5_put_params(tds, params, 0));
    1456             : 
    1457           0 :         return tds_flush_packet(tds);
    1458             : }
    1459             : 
    1460             : /**
    1461             :  * Creates a temporary stored procedure in the server and execute it.
    1462             :  * \param tds     state information for the socket and the TDS protocol
    1463             :  * \param query   language query with given placeholders ('?')
    1464             :  * \param id      string to identify the dynamic query. Pass NULL for automatic generation.
    1465             :  * \param dyn_out will receive allocated TDSDYNAMIC*. Any older allocated dynamic won't be freed. Can be NULL.
    1466             :  * \param params  parameters to use. It can be NULL even if parameters are present.
    1467             :  * \return TDS_FAIL or TDS_SUCCESS
    1468             :  */
    1469             : TDSRET
    1470        1846 : tds71_submit_prepexec(TDSSOCKET * tds, const char *query, const char *id, TDSDYNAMIC ** dyn_out, TDSPARAMINFO * params)
    1471             : {
    1472             :         int query_len;
    1473        1846 :         TDSRET rc = TDS_FAIL;
    1474             :         TDSDYNAMIC *dyn;
    1475             :         size_t converted_query_len;
    1476             :         const char *converted_query;
    1477             :         TDSFREEZE outer;
    1478             : 
    1479        1846 :         CHECK_TDS_EXTRA(tds);
    1480        1846 :         if (params)
    1481        1580 :                 CHECK_PARAMINFO_EXTRA(params);
    1482             : 
    1483        1846 :         if (!query || !dyn_out || !IS_TDS7_PLUS(tds->conn))
    1484             :                 return TDS_FAIL;
    1485             : 
    1486        1846 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    1487             :                 return TDS_FAIL;
    1488             : 
    1489             :         /* allocate a structure for this thing */
    1490        1846 :         dyn = tds_alloc_dynamic(tds->conn, id);
    1491        1846 :         if (!dyn)
    1492             :                 return TDS_FAIL;
    1493        1846 :         tds_release_dynamic(dyn_out);
    1494        1846 :         *dyn_out = dyn;
    1495             : 
    1496        1846 :         tds_set_cur_dyn(tds, dyn);
    1497             : 
    1498        1846 :         query_len = (int)strlen(query);
    1499             : 
    1500        1846 :         converted_query = tds_convert_string(tds, tds->conn->char_convs[client2ucs2], query, query_len, &converted_query_len);
    1501        1846 :         if (!converted_query)
    1502             :                 goto failure;
    1503             : 
    1504        1846 :         tds_freeze(tds, &outer, 0);
    1505        1846 :         tds_start_query(tds, TDS_RPC);
    1506             :         /* procedure name */
    1507        1846 :         if (IS_TDS71_PLUS(tds->conn)) {
    1508        1846 :                 tds_put_smallint(tds, -1);
    1509        1846 :                 tds_put_smallint(tds, TDS_SP_PREPEXEC);
    1510             :         } else {
    1511           0 :                 TDS_PUT_N_AS_UCS2(tds, "sp_prepexec");
    1512             :         }
    1513        1846 :         tds_put_smallint(tds, 0);
    1514             : 
    1515             :         /* return param handle (int) */
    1516        1846 :         tds_put_byte(tds, 0);
    1517        1846 :         tds_put_byte(tds, 1);   /* result */
    1518        1846 :         tds_put_byte(tds, SYBINTN);
    1519        1846 :         tds_put_byte(tds, 4);
    1520        1846 :         tds_put_byte(tds, 0);
    1521             : 
    1522        1846 :         rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
    1523        1846 :         tds7_put_query_params(tds, converted_query, converted_query_len);
    1524        1846 :         tds_convert_string_free(query, converted_query);
    1525        1846 :         if (TDS_FAILED(rc)) {
    1526           0 :                 tds_freeze_abort(&outer);
    1527           0 :                 return rc;
    1528             :         }
    1529        1846 :         tds_freeze_close(&outer);
    1530             : 
    1531        1846 :         if (params) {
    1532             :                 int i;
    1533             : 
    1534        1892 :                 for (i = 0; i < params->num_cols; i++) {
    1535        1892 :                         TDSCOLUMN *param = params->columns[i];
    1536        1892 :                         TDS_PROPAGATE(tds_put_data_info(tds, param, 0));
    1537        1892 :                         TDS_PROPAGATE(tds_put_data(tds, param));
    1538             :                 }
    1539             :         }
    1540             : 
    1541        1846 :         tds->current_op = TDS_OP_PREPEXEC;
    1542             : 
    1543        1846 :         rc = tds_query_flush_packet(tds);
    1544        1846 :         if (TDS_SUCCEED(rc))
    1545             :                 return rc;
    1546             : 
    1547           0 : failure:
    1548             :         /* TODO correct if writing fail ?? */
    1549           0 :         tds_set_state(tds, TDS_IDLE);
    1550             : 
    1551           0 :         tds_release_dynamic(dyn_out);
    1552           0 :         tds_dynamic_deallocated(tds->conn, dyn);
    1553           0 :         return rc;
    1554             : }
    1555             : 
    1556             : /**
    1557             :  * Get column size for wire
    1558             :  */
    1559             : size_t
    1560      132346 : tds_fix_column_size(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * curcol)
    1561             : {
    1562      132346 :         size_t size = curcol->on_server.column_size, min;
    1563             : 
    1564      132346 :         if (!size) {
    1565        1390 :                 size = curcol->column_size;
    1566        1390 :                 if (is_unicode_type(curcol->on_server.column_type))
    1567         776 :                         size *= 2u;
    1568             :         }
    1569             : 
    1570      132346 :         switch (curcol->column_varint_size) {
    1571       12058 :         case 1:
    1572       12058 :                 size = TDS_CLAMP(size, 1, 255);
    1573             :                 break;
    1574       60500 :         case 2:
    1575             :                 /* note that varchar(max)/varbinary(max) have a varint of 8 */
    1576       60500 :                 if (curcol->on_server.column_type == XSYBNVARCHAR || curcol->on_server.column_type == XSYBNCHAR)
    1577             :                         min = 2;
    1578             :                 else
    1579       57524 :                         min = 1;
    1580       60500 :                 size = TDS_CLAMP(size, min, 8000u);
    1581             :                 break;
    1582         508 :         case 4:
    1583         508 :                 if (curcol->on_server.column_type == SYBNTEXT)
    1584             :                         size = 0x7ffffffeu;
    1585             :                 else
    1586         508 :                         size = 0x7fffffffu;
    1587             :                 break;
    1588             :         default:
    1589             :                 break;
    1590             :         }
    1591      132346 :         return size;
    1592             : }
    1593             : 
    1594             : /**
    1595             :  * Put data information to wire
    1596             :  * \param tds    state information for the socket and the TDS protocol
    1597             :  * \param curcol column where to store information
    1598             :  * \param flags  bit flags on how to send data (use TDS_PUT_DATA_USE_NAME for use name information)
    1599             :  * \return TDS_SUCCESS or TDS_FAIL
    1600             :  */
    1601             : static TDSRET
    1602        9752 : tds_put_data_info(TDSSOCKET * tds, TDSCOLUMN * curcol, int flags)
    1603             : {
    1604             :         int len;
    1605             : 
    1606        9752 :         CHECK_TDS_EXTRA(tds);
    1607        9752 :         CHECK_COLUMN_EXTRA(curcol);
    1608             : 
    1609        9752 :         if (flags & TDS_PUT_DATA_USE_NAME) {
    1610        5028 :                 len = (int) tds_dstr_len(&curcol->column_name);
    1611        2514 :                 tdsdump_log(TDS_DBG_ERROR, "tds_put_data_info putting param_name \n");
    1612             : 
    1613        2514 :                 if (IS_TDS7_PLUS(tds->conn)) {
    1614        2190 :                         TDS_START_LEN_TINYINT(tds) { /* param name len */
    1615        2190 :                                 if ((flags & TDS_PUT_DATA_PREFIX_NAME) != 0)
    1616         144 :                                         tds_put_smallint(tds, '@');
    1617        4380 :                                 tds_put_string(tds, tds_dstr_cstr(&curcol->column_name), len);
    1618        2190 :                         } TDS_END_LEN_STRING
    1619             :                 } else {
    1620         324 :                         TDS_START_LEN_TINYINT(tds) { /* param name len */
    1621         648 :                                 tds_put_string(tds, tds_dstr_cstr(&curcol->column_name), len);
    1622         324 :                         } TDS_END_LEN
    1623             :                 }
    1624             :         } else {
    1625        7238 :                 tds_put_byte(tds, 0x00);        /* param name len */
    1626             :         }
    1627             :         /*
    1628             :          * TODO support other flags (use defaul null/no metadata)
    1629             :          * bit 1 (2 as flag) in TDS7+ is "default value" bit 
    1630             :          * (what's the meaning of "default value" ?)
    1631             :          */
    1632             : 
    1633        9752 :         tdsdump_log(TDS_DBG_ERROR, "tds_put_data_info putting status \n");
    1634        9752 :         if (flags & TDS_PUT_DATA_LONG_STATUS)
    1635           0 :                 tds_put_int(tds, curcol->column_output);     /* status (input) */
    1636             :         else
    1637        9752 :                 tds_put_byte(tds, curcol->column_output);    /* status (input) */
    1638        9752 :         if (!IS_TDS7_PLUS(tds->conn))
    1639         806 :                 tds_put_int(tds, curcol->column_usertype);   /* usertype */
    1640        9752 :         TDS_PUT_BYTE(tds, curcol->on_server.column_type);
    1641             : 
    1642        9752 :         if (curcol->funcs->put_info(tds, curcol) != TDS_SUCCESS)
    1643             :                 return TDS_FAIL;
    1644             : 
    1645             :         /* TODO needed in TDS4.2 ?? now is called only if TDS >= 5 */
    1646        9752 :         if (!IS_TDS7_PLUS(tds->conn))
    1647         806 :                 tds_put_byte(tds, 0x00);        /* locale info length */
    1648             : 
    1649             :         return TDS_SUCCESS;
    1650             : }
    1651             : 
    1652             : /**
    1653             :  * Send dynamic request on TDS 7+ to be executed
    1654             :  * \tds
    1655             :  * \param dyn  dynamic query to execute
    1656             :  */
    1657             : static TDSRET
    1658        1138 : tds7_send_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn)
    1659             : {
    1660             :         TDSCOLUMN *param;
    1661             :         TDSPARAMINFO *info;
    1662             :         int i;
    1663             : 
    1664             :         /* procedure name */
    1665             :         /* NOTE do not call this procedure using integer name (TDS_SP_EXECUTE) on mssql2k, it doesn't work! */
    1666        1138 :         TDS_PUT_N_AS_UCS2(tds, "sp_execute");
    1667        1138 :         tds_put_smallint(tds, 0);       /* flags */
    1668             : 
    1669             :         /* id of prepared statement */
    1670        1138 :         tds_put_byte(tds, 0);
    1671        1138 :         tds_put_byte(tds, 0);
    1672        1138 :         tds_put_byte(tds, SYBINTN);
    1673        1138 :         tds_put_byte(tds, 4);
    1674        1138 :         tds_put_byte(tds, 4);
    1675        1138 :         tds_put_int(tds, dyn->num_id);
    1676             : 
    1677        1138 :         info = dyn->params;
    1678        1138 :         if (info)
    1679        2232 :                 for (i = 0; i < info->num_cols; i++) {
    1680        2232 :                         param = info->columns[i];
    1681        2232 :                         TDS_PROPAGATE(tds_put_data_info(tds, param, 0));
    1682        2232 :                         TDS_PROPAGATE(tds_put_data(tds, param));
    1683             :                 }
    1684             : 
    1685        1138 :         tds->current_op = TDS_OP_EXECUTE;
    1686             :         return TDS_SUCCESS;
    1687             : }
    1688             : 
    1689             : /**
    1690             :  * Sends a previously prepared dynamic statement to the server.
    1691             :  * \param tds state information for the socket and the TDS protocol
    1692             :  * \param dyn dynamic proc to execute. Must build from same tds.
    1693             :  */
    1694             : TDSRET
    1695        1008 : tds_submit_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn)
    1696             : {
    1697        1008 :         CHECK_TDS_EXTRA(tds);
    1698             :         /* TODO this dynamic should be in tds */
    1699        1008 :         CHECK_DYNAMIC_EXTRA(dyn);
    1700             : 
    1701        1008 :         tdsdump_log(TDS_DBG_FUNC, "tds_submit_execute()\n");
    1702             : 
    1703        1008 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    1704             :                 return TDS_FAIL;
    1705             : 
    1706        1008 :         tds_set_cur_dyn(tds, dyn);
    1707             : 
    1708        1008 :         if (IS_TDS7_PLUS(tds->conn)) {
    1709             :                 /* check proper id */
    1710         554 :                 if (dyn->num_id == 0) {
    1711           0 :                         tds_set_state(tds, TDS_IDLE);
    1712           0 :                         return TDS_FAIL;
    1713             :                 }
    1714             : 
    1715             :                 /* RPC on sp_execute */
    1716         554 :                 tds_start_query(tds, TDS_RPC);
    1717             : 
    1718         554 :                 tds7_send_execute(tds, dyn);
    1719             : 
    1720         554 :                 return tds_query_flush_packet(tds);
    1721             :         }
    1722             : 
    1723         454 :         if (dyn->emulated) {
    1724          84 :                 TDS_PROPAGATE(tds_send_emulated_execute(tds, dyn->query, dyn->params));
    1725          84 :                 return tds_query_flush_packet(tds);
    1726             :         }
    1727             : 
    1728             :         /* query has been prepared successfully, discard original query */
    1729         370 :         if (dyn->query)
    1730         236 :                 TDS_ZERO_FREE(dyn->query);
    1731             : 
    1732         370 :         tds->out_flag = TDS_NORMAL;
    1733             :         /* dynamic id */
    1734         370 :         tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
    1735         370 :         TDS_START_LEN_USMALLINT(tds) {
    1736         370 :                 tds_put_byte(tds, 0x02);
    1737         370 :                 tds_put_byte(tds, dyn->params ? 0x01 : 0);
    1738         370 :                 TDS_START_LEN_TINYINT(tds) {
    1739         370 :                         tds_put_string(tds, dyn->id, -1);
    1740         370 :                 } TDS_END_LEN
    1741         370 :                 tds_put_smallint(tds, 0);
    1742         370 :         } TDS_END_LEN
    1743             : 
    1744         370 :         if (dyn->params)
    1745         342 :                 TDS_PROPAGATE(tds5_put_params(tds, dyn->params, 0));
    1746             : 
    1747             :         /* send it */
    1748         370 :         return tds_query_flush_packet(tds);
    1749             : }
    1750             : 
    1751             : /**
    1752             :  * Send parameters to server.
    1753             :  * \tds
    1754             :  * \param info   parameters to send
    1755             :  * \param flags  0 or TDS_PUT_DATA_USE_NAME
    1756             :  */
    1757             : static TDSRET
    1758         518 : tds5_put_params(TDSSOCKET * tds, TDSPARAMINFO * info, int flags)
    1759             : {
    1760             :         int i;
    1761         518 :         bool wide = false;
    1762             : 
    1763         518 :         CHECK_TDS_EXTRA(tds);
    1764         518 :         CHECK_PARAMINFO_EXTRA(info);
    1765             : 
    1766             :         /* column descriptions */
    1767           0 :         for (;;) {
    1768             :                 TDSFREEZE outer, inner;
    1769             : 
    1770         518 :                 tds_freeze(tds, &outer, 0);
    1771         518 :                 if (wide) {
    1772           0 :                         tds_put_byte(tds, TDS5_PARAMFMT2_TOKEN);
    1773           0 :                         tds_freeze(tds, &inner, 4);
    1774           0 :                         flags |= TDS_PUT_DATA_LONG_STATUS;
    1775             :                 } else {
    1776         518 :                         tds_put_byte(tds, TDS5_PARAMFMT_TOKEN);
    1777         518 :                         tds_freeze(tds, &inner, 2);
    1778             :                 }
    1779             : 
    1780             :                 /* number of parameters */
    1781         518 :                 tds_put_smallint(tds, info->num_cols);
    1782             : 
    1783             :                 /* column detail for each parameter */
    1784        1324 :                 for (i = 0; i < info->num_cols; i++)
    1785         806 :                         TDS_PROPAGATE(tds_put_data_info(tds, info->columns[i], flags));
    1786             : 
    1787             :                 /* if we fits we are fine */
    1788         518 :                 if (wide || tds_freeze_written(&inner) - 2 < 0x10000u) {
    1789         518 :                         tds_freeze_close(&inner);
    1790         518 :                         tds_freeze_close(&outer);
    1791         518 :                         break;
    1792             :                 }
    1793             : 
    1794             :                 /* try again with wide */
    1795           0 :                 tds_freeze_abort(&inner);
    1796           0 :                 tds_freeze_abort(&outer);
    1797           0 :                 if (!tds_capability_has_req(tds->conn, TDS_REQ_WIDETABLE))
    1798             :                         return TDS_FAIL;
    1799           0 :                 wide = true;
    1800             :         }
    1801             : 
    1802             :         /* row data */
    1803         518 :         tds_put_byte(tds, TDS5_PARAMS_TOKEN);
    1804        1324 :         for (i = 0; i < info->num_cols; i++)
    1805        1612 :                 TDS_PROPAGATE(tds_put_data(tds, info->columns[i]));
    1806             :         return TDS_SUCCESS;
    1807             : }
    1808             : 
    1809             : /**
    1810             :  * Check if dynamic request must be unprepared.
    1811             :  * Depending on status and protocol version request should be unprepared
    1812             :  * or not.
    1813             :  * \tds
    1814             :  * \param dyn  dynamic request to check
    1815             :  */
    1816             : int
    1817        2260 : tds_needs_unprepare(TDSCONNECTION * conn, TDSDYNAMIC * dyn)
    1818             : {
    1819             :         CHECK_CONN_EXTRA(conn);
    1820        2260 :         CHECK_DYNAMIC_EXTRA(dyn);
    1821             : 
    1822             :         /* check if statement is prepared */
    1823        2260 :         if (IS_TDS7_PLUS(conn) && !dyn->num_id)
    1824             :                 return 0;
    1825             : 
    1826        2228 :         if (dyn->emulated || !dyn->id[0])
    1827             :                 return 0;
    1828             : 
    1829        2168 :         return 1;
    1830             : }
    1831             : 
    1832             : /**
    1833             :  * Unprepare dynamic on idle.
    1834             :  * This let libTDS close the prepared statement when possible.
    1835             :  * \tds
    1836             :  * \param dyn  dynamic request to close
    1837             :  */
    1838             : TDSRET
    1839           8 : tds_deferred_unprepare(TDSCONNECTION * conn, TDSDYNAMIC * dyn)
    1840             : {
    1841             :         CHECK_CONN_EXTRA(conn);
    1842           8 :         CHECK_DYNAMIC_EXTRA(dyn);
    1843             : 
    1844           8 :         if (!tds_needs_unprepare(conn, dyn)) {
    1845           0 :                 tds_dynamic_deallocated(conn, dyn);
    1846           0 :                 return TDS_SUCCESS;
    1847             :         }
    1848             : 
    1849           8 :         dyn->defer_close = true;
    1850           8 :         conn->pending_close = 1;
    1851             : 
    1852           8 :         return TDS_SUCCESS;
    1853             : }
    1854             : 
    1855             : /**
    1856             :  * Send a unprepare request for a prepared query
    1857             :  * \param tds state information for the socket and the TDS protocol
    1858             :  * \param dyn dynamic query
    1859             :  * \result TDS_SUCCESS or TDS_FAIL
    1860             :  */
    1861             : TDSRET
    1862        2384 : tds_submit_unprepare(TDSSOCKET * tds, TDSDYNAMIC * dyn)
    1863             : {
    1864        2384 :         CHECK_TDS_EXTRA(tds);
    1865             :         /* TODO test dyn in tds */
    1866        2384 :         CHECK_DYNAMIC_EXTRA(dyn);
    1867             : 
    1868        2384 :         if (!dyn)
    1869             :                 return TDS_FAIL;
    1870             : 
    1871        2384 :         tdsdump_log(TDS_DBG_FUNC, "tds_submit_unprepare() %s\n", dyn->id);
    1872             : 
    1873        2384 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    1874             :                 return TDS_FAIL;
    1875             : 
    1876        2384 :         tds_set_cur_dyn(tds, dyn);
    1877             : 
    1878        2384 :         if (IS_TDS7_PLUS(tds->conn)) {
    1879             :                 /* RPC on sp_execute */
    1880        2094 :                 tds_start_query(tds, TDS_RPC);
    1881             : 
    1882             :                 /* procedure name */
    1883        2094 :                 if (IS_TDS71_PLUS(tds->conn)) {
    1884             :                         /* save some byte for mssql2k */
    1885        2094 :                         tds_put_smallint(tds, -1);
    1886        2094 :                         tds_put_smallint(tds, TDS_SP_UNPREPARE);
    1887             :                 } else {
    1888           0 :                         TDS_PUT_N_AS_UCS2(tds, "sp_unprepare");
    1889             :                 }
    1890        2094 :                 tds_put_smallint(tds, 0);       /* flags */
    1891             : 
    1892             :                 /* id of prepared statement */
    1893        2094 :                 tds_put_byte(tds, 0);
    1894        2094 :                 tds_put_byte(tds, 0);
    1895        2094 :                 tds_put_byte(tds, SYBINTN);
    1896        2094 :                 tds_put_byte(tds, 4);
    1897        2094 :                 tds_put_byte(tds, 4);
    1898        2094 :                 tds_put_int(tds, dyn->num_id);
    1899             : 
    1900        2094 :                 tds->current_op = TDS_OP_UNPREPARE;
    1901        2094 :                 return tds_query_flush_packet(tds);
    1902             :         }
    1903             : 
    1904         290 :         if (dyn->emulated) {
    1905           6 :                 tds_start_query(tds, TDS_QUERY);
    1906             : 
    1907             :                 /* just a dummy select to return some data */
    1908           6 :                 tds_put_string(tds, "select 1 where 0=1", -1);
    1909           6 :                 return tds_query_flush_packet(tds);
    1910             :         }
    1911             : 
    1912         284 :         tds->out_flag = TDS_NORMAL;
    1913             :         /* dynamic id */
    1914         284 :         tds_put_byte(tds, TDS5_DYNAMIC_TOKEN);
    1915         284 :         TDS_START_LEN_USMALLINT(tds) {
    1916         284 :                 tds_put_byte(tds, TDS_DYN_DEALLOC);
    1917         284 :                 tds_put_byte(tds, 0x00);
    1918         284 :                 TDS_START_LEN_TINYINT(tds) {
    1919         284 :                         tds_put_string(tds, dyn->id, -1);
    1920         284 :                 } TDS_END_LEN
    1921         284 :                 tds_put_smallint(tds, 0);
    1922         284 :         } TDS_END_LEN
    1923             : 
    1924             :         /* send it */
    1925         284 :         tds->current_op = TDS_OP_DYN_DEALLOC;
    1926         284 :         return tds_query_flush_packet(tds);
    1927             : }
    1928             : 
    1929             : /**
    1930             :  * Send RPC as string query.
    1931             :  * This function is used on old protocol which does not support RPC queries.
    1932             :  * \tds
    1933             :  * \param rpc_name  name of RPC to invoke
    1934             :  * \param params    parameters to send to server
    1935             :  * \returns TDS_FAIL or TDS_SUCCESS
    1936             :  */
    1937             : static TDSRET
    1938           0 : tds4_send_emulated_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params)
    1939             : {
    1940             :         TDSCOLUMN *param;
    1941             :         int i, n;
    1942           0 :         int num_params = params ? params->num_cols : 0;
    1943           0 :         const char *sep = " ";
    1944             :         char buf[80];
    1945             : 
    1946             :         /* create params and set */
    1947           0 :         for (i = 0, n = 0; i < num_params; ++i) {
    1948             : 
    1949           0 :                 param = params->columns[i];
    1950             : 
    1951             :                 /* declare and set output parameters */
    1952           0 :                 if (!param->column_output)
    1953           0 :                         continue;
    1954           0 :                 ++n;
    1955           0 :                 sprintf(buf, " DECLARE @P%d ", n);
    1956           0 :                 tds_get_column_declaration(tds, param, buf + strlen(buf));
    1957           0 :                 sprintf(buf + strlen(buf), " SET @P%d=", n);
    1958           0 :                 tds_put_string(tds, buf, -1);
    1959           0 :                 tds_put_param_as_string(tds, params, i);
    1960             :         }
    1961             : 
    1962             :         /* put exec statement */
    1963           0 :         tds_put_string(tds, " EXEC ", 6);
    1964           0 :         tds_put_string(tds, rpc_name, -1);
    1965             : 
    1966             :         /* put arguments */
    1967           0 :         for (i = 0, n = 0; i < num_params; ++i) {
    1968           0 :                 param = params->columns[i];
    1969           0 :                 tds_put_string(tds, sep, -1);
    1970           0 :                 if (!tds_dstr_isempty(&param->column_name)) {
    1971           0 :                         tds_put_string(tds, tds_dstr_cstr(&param->column_name), (int)tds_dstr_len(&param->column_name));
    1972           0 :                         tds_put_string(tds, "=", 1);
    1973             :                 }
    1974           0 :                 if (param->column_output) {
    1975           0 :                         ++n;
    1976           0 :                         sprintf(buf, "@P%d OUTPUT", n);
    1977           0 :                         tds_put_string(tds, buf, -1);
    1978             :                 } else {
    1979           0 :                         tds_put_param_as_string(tds, params, i);
    1980             :                 }
    1981           0 :                 sep = ",";
    1982             :         }
    1983             : 
    1984           0 :         return tds_query_flush_packet(tds);
    1985             : }
    1986             : 
    1987             : /**
    1988             :  * Calls a RPC from server. Output parameters will be stored in tds->param_info.
    1989             :  * \param tds      state information for the socket and the TDS protocol
    1990             :  * \param rpc_name name of RPC
    1991             :  * \param params   parameters information. NULL for no parameters
    1992             :  */
    1993             : TDSRET
    1994        1458 : tds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params, TDSHEADERS * head)
    1995             : {
    1996             :         TDSCOLUMN *param;
    1997             :         int i;
    1998        1458 :         int num_params = params ? params->num_cols : 0;
    1999             : 
    2000        1458 :         CHECK_TDS_EXTRA(tds);
    2001        1458 :         if (params)
    2002        1400 :                 CHECK_PARAMINFO_EXTRA(params);
    2003             : 
    2004        1458 :         assert(tds);
    2005        1458 :         assert(rpc_name);
    2006             : 
    2007        1458 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2008             :                 return TDS_FAIL;
    2009             : 
    2010             :         /* distinguish from dynamic query  */
    2011        1458 :         tds_release_cur_dyn(tds);
    2012             : 
    2013        1458 :         if (IS_TDS7_PLUS(tds->conn)) {
    2014        1278 :                 if (tds_start_query_head(tds, TDS_RPC, head) != TDS_SUCCESS)
    2015             :                         return TDS_FAIL;
    2016             : 
    2017             :                 /* procedure name */
    2018        1278 :                 TDS_START_LEN_USMALLINT(tds) {
    2019        1278 :                         tds_put_string(tds, rpc_name, -1);
    2020        1278 :                 } TDS_END_LEN_STRING
    2021             : 
    2022             :                 /*
    2023             :                  * TODO support flags
    2024             :                  * bit 0 (1 as flag) in TDS7/TDS5 is "recompile"
    2025             :                  * bit 1 (2 as flag) in TDS7+ is "no metadata" bit 
    2026             :                  * (I don't know meaning of "no metadata")
    2027             :                  */
    2028        1278 :                 tds_put_smallint(tds, 0);
    2029             : 
    2030        3324 :                 for (i = 0; i < num_params; i++) {
    2031        2046 :                         param = params->columns[i];
    2032        2046 :                         TDS_PROPAGATE(tds_put_data_info(tds, param, TDS_PUT_DATA_USE_NAME));
    2033        2046 :                         TDS_PROPAGATE(tds_put_data(tds, param));
    2034             :                 }
    2035             : 
    2036        1278 :                 return tds_query_flush_packet(tds);
    2037             :         }
    2038             : 
    2039         180 :         if (IS_TDS50(tds->conn)) {
    2040         180 :                 tds->out_flag = TDS_NORMAL;
    2041             : 
    2042             :                 /* DBRPC */
    2043         180 :                 tds_put_byte(tds, TDS_DBRPC_TOKEN);
    2044         180 :                 TDS_START_LEN_USMALLINT(tds) {
    2045         180 :                         TDS_START_LEN_TINYINT(tds) {
    2046         180 :                                 tds_put_string(tds, rpc_name, -1);
    2047         180 :                         } TDS_END_LEN
    2048             :                         /* TODO flags */
    2049         180 :                         tds_put_smallint(tds, num_params ? 2 : 0);
    2050         180 :                 } TDS_END_LEN
    2051             : 
    2052         180 :                 if (num_params)
    2053         170 :                         TDS_PROPAGATE(tds5_put_params(tds, params, TDS_PUT_DATA_USE_NAME));
    2054             : 
    2055             :                 /* send it */
    2056         180 :                 return tds_query_flush_packet(tds);
    2057             :         }
    2058             : 
    2059             :         /* emulate it for TDS4.x, send RPC for mssql */
    2060           0 :         if (tds->conn->tds_version < 0x500)
    2061           0 :                 return tds4_send_emulated_rpc(tds, rpc_name, params);
    2062             : 
    2063             :         /* TODO continue, support for TDS4?? */
    2064           0 :         tds_set_state(tds, TDS_IDLE);
    2065           0 :         return TDS_FAIL;
    2066             : }
    2067             : 
    2068             : /**
    2069             :  * tds_send_cancel() sends an empty packet (8 byte header only)
    2070             :  * tds_process_cancel should be called directly after this.
    2071             :  * \param tds state information for the socket and the TDS protocol
    2072             :  * \remarks
    2073             :  *      tcp will either deliver the packet or time out. 
    2074             :  *      (TIME_WAIT determines how long it waits between retries.)  
    2075             :  *      
    2076             :  *      On sending the cancel, we may get EAGAIN.  We then select(2) until we know
    2077             :  *      either 1) it succeeded or 2) it didn't.  On failure, close the socket,
    2078             :  *      tell the app, and fail the function.  
    2079             :  *      
    2080             :  *      On success, we read(2) and wait for a reply with select(2).  If we get
    2081             :  *      one, great.  If the client's timeout expires, we tell him, but all we can
    2082             :  *      do is wait some more or give up and close the connection.  If he tells us
    2083             :  *      to cancel again, we wait some more.  
    2084             :  */
    2085             : TDSRET
    2086       11450 : tds_send_cancel(TDSSOCKET * tds)
    2087             : {
    2088             : #if ENABLE_ODBC_MARS
    2089        5732 :         CHECK_TDS_EXTRA(tds);
    2090             : 
    2091        5732 :         tdsdump_log(TDS_DBG_FUNC, "tds_send_cancel: %sin_cancel and %sidle\n", 
    2092             :                                 (tds->in_cancel? "":"not "), (tds->state == TDS_IDLE? "":"not "));
    2093             : 
    2094             :         /* one cancel is sufficient */
    2095        5732 :         if (tds->in_cancel || tds->state == TDS_IDLE) {
    2096             :                 return TDS_SUCCESS;
    2097             :         }
    2098             : 
    2099         616 :         tds->in_cancel = 1;
    2100             : 
    2101         616 :         if (tds_mutex_trylock(&tds->conn->list_mtx)) {
    2102             :                 /* TODO check */
    2103             :                 /* signal other socket */
    2104           0 :                 tds_wakeup_send(&tds->conn->wakeup, 1);
    2105           0 :                 return TDS_SUCCESS;
    2106             :         }
    2107         616 :         if (tds->conn->in_net_tds) {
    2108         121 :                 tds_mutex_unlock(&tds->conn->list_mtx);
    2109             :                 /* TODO check */
    2110             :                 /* signal other socket */
    2111         121 :                 tds_wakeup_send(&tds->conn->wakeup, 1);
    2112         121 :                 return TDS_SUCCESS;
    2113             :         }
    2114         495 :         tds_mutex_unlock(&tds->conn->list_mtx);
    2115             : 
    2116             :         /*
    2117             :         problem: if we are in in_net and we got a signal ??
    2118             :         on timeout we and a cancel, directly in in_net
    2119             :         if we hold the lock and we get a signal lock create a death lock
    2120             : 
    2121             :         if we use a recursive mutex and we can get the lock there are 2 cases
    2122             :         - same thread, we could add a packet and signal, no try ok
    2123             :         - first thread locking, we could add a packet but are we sure it get processed ??, no try ok
    2124             :         if recursive mutex and we can't get another thread, wait
    2125             : 
    2126             :         if mutex is not recursive and we get the lock (try)
    2127             :         - nobody locked, if in_net it could be same or another
    2128             :         if mutex is not recursive and we can't get the lock
    2129             :         - another thread is locking, sending signal require not exiting and global list (not protected by list_mtx)
    2130             :         - same thread have lock, we can't wait nothing without deathlock, setting a flag in tds and signaling could help
    2131             : 
    2132             :         if a tds is waiting for data or is waiting for a condition or for a signal in poll
    2133             :         pass cancel request on socket ??
    2134             :          */
    2135             : 
    2136         495 :         tds->out_flag = TDS_CANCEL;
    2137         495 :         tds->out_pos = 8;
    2138         495 :         tdsdump_log(TDS_DBG_FUNC, "tds_send_cancel: sending cancel packet\n");
    2139         495 :         return tds_flush_packet(tds);
    2140             : #else
    2141             :         TDSRET rc;
    2142             : 
    2143             :         /*
    2144             :          * if we are not able to get the lock signal other thread
    2145             :          * this means that either:
    2146             :          * - another thread is processing data
    2147             :          * - we got called from a signal inside processing thread
    2148             :          * - we got called from message handler
    2149             :          */
    2150        5718 :         if (tds_mutex_trylock(&tds->wire_mtx)) {
    2151             :                 /* TODO check */
    2152         201 :                 if (!tds->in_cancel)
    2153         133 :                         tds->in_cancel = 1;
    2154             :                 /* signal other socket */
    2155         201 :                 tds_wakeup_send(&tds->conn->wakeup, 1);
    2156         201 :                 return TDS_SUCCESS;
    2157             :         }
    2158             : 
    2159        5517 :         CHECK_TDS_EXTRA(tds);
    2160             : 
    2161        5517 :         tdsdump_log(TDS_DBG_FUNC, "tds_send_cancel: %sin_cancel and %sidle\n", 
    2162             :                                 (tds->in_cancel? "":"not "), (tds->state == TDS_IDLE? "":"not "));
    2163             : 
    2164             :         /* one cancel is sufficient */
    2165        5517 :         if (tds->in_cancel || tds->state == TDS_IDLE) {
    2166        5040 :                 tds_mutex_unlock(&tds->wire_mtx);
    2167        5040 :                 return TDS_SUCCESS;
    2168             :         }
    2169             : 
    2170         477 :         rc = tds_put_cancel(tds);
    2171         477 :         tds_mutex_unlock(&tds->wire_mtx);
    2172             : 
    2173         477 :         return rc;
    2174             : #endif
    2175             : }
    2176             : 
    2177             : /**
    2178             :  * Quote a string properly. Output string is always NUL-terminated
    2179             :  * \param buffer   output buffer. If NULL function will just return
    2180             :  *        required bytes
    2181             :  * \param quoting  quote character (should be one of '\'', '"', ']')
    2182             :  * \param id       string to quote
    2183             :  * \param len      length of string to quote
    2184             :  * \returns size of output string
    2185             :  */
    2186             : static size_t
    2187       11651 : tds_quote(char *buffer, char quoting, const char *id, size_t len)
    2188             : {
    2189             :         size_t size;
    2190             :         const char *src, *pend;
    2191             :         char *dst;
    2192             : 
    2193       11651 :         pend = id + len;
    2194             : 
    2195             :         /* quote */
    2196       11651 :         src = id;
    2197       11651 :         if (!buffer) {
    2198        7743 :                 size = 2u + len;
    2199       43184 :                 for (; src != pend; ++src)
    2200       35441 :                         if (*src == quoting)
    2201           0 :                                 ++size;
    2202             :                 return size;
    2203             :         }
    2204             : 
    2205        3908 :         dst = buffer;
    2206        3908 :         *dst++ = (quoting == ']') ? '[' : quoting;
    2207       31423 :         for (; src != pend; ++src) {
    2208       27515 :                 if (*src == quoting)
    2209           0 :                         *dst++ = quoting;
    2210       27515 :                 *dst++ = *src;
    2211             :         }
    2212        3908 :         *dst++ = quoting;
    2213        3908 :         *dst = 0;
    2214        3908 :         return dst - buffer;
    2215             : }
    2216             : 
    2217             : /**
    2218             :  * Quote an id
    2219             :  * \param tds    state information for the socket and the TDS protocol
    2220             :  * \param buffer buffer to store quoted id. If NULL do not write anything 
    2221             :  *        (useful to compute quote length)
    2222             :  * \param id     id to quote
    2223             :  * \param idlen  id length (< 0 for NUL terminated)
    2224             :  * \result written chars (not including needed terminator)
    2225             :  * \see tds_quote_id_rpc
    2226             :  */
    2227             : size_t
    2228       11611 : tds_quote_id(TDSSOCKET * tds, char *buffer, const char *id, ptrdiff_t idlen)
    2229             : {
    2230             :         size_t i, len;
    2231             : 
    2232       11611 :         CHECK_TDS_EXTRA(tds);
    2233             : 
    2234       11611 :         len = idlen < 0 ? strlen(id) : (size_t) idlen;
    2235             : 
    2236             :         /* quote always for mssql */
    2237       11611 :         if (TDS_IS_MSSQL(tds) || tds->conn->product_version >= TDS_SYB_VER(12, 5, 1))
    2238       11611 :                 return tds_quote(buffer, ']', id, len);
    2239             : 
    2240             :         /* need quote ?? */
    2241           0 :         for (i = 0; i < len; ++i) {
    2242           0 :                 char c = id[i];
    2243             : 
    2244           0 :                 if (c >= 'a' && c <= 'z')
    2245           0 :                         continue;
    2246           0 :                 if (c >= 'A' && c <= 'Z')
    2247           0 :                         continue;
    2248           0 :                 if (i > 0 && c >= '0' && c <= '9')
    2249           0 :                         continue;
    2250           0 :                 if (c == '_')
    2251           0 :                         continue;
    2252           0 :                 return tds_quote(buffer, '\"', id, len);
    2253             :         }
    2254             : 
    2255           0 :         if (buffer) {
    2256           0 :                 memcpy(buffer, id, len);
    2257           0 :                 buffer[len] = '\0';
    2258             :         }
    2259             :         return len;
    2260             : }
    2261             : 
    2262             : /**
    2263             :  * Quote an id for a RPC call
    2264             :  * \param tds    state information for the socket and the TDS protocol
    2265             :  * \param buffer buffer to store quoted id. If NULL do not write anything
    2266             :  *        (useful to compute quote length)
    2267             :  * \param id     id to quote
    2268             :  * \param idlen  id length (< 0 for NUL terminated)
    2269             :  * \result written chars (not including needed terminator)
    2270             :  * \see tds_quote_id
    2271             :  */
    2272             : size_t
    2273          40 : tds_quote_id_rpc(TDSSOCKET * tds, char *buffer, const char *id, ptrdiff_t idlen)
    2274             : {
    2275             :         size_t len;
    2276             :         /* We are quoting for RPC calls, not base language queries. For RPC calls Sybase
    2277             :          * servers don't accept '[]' style quoting so don't use them but use normal
    2278             :          * identifier quoting ('""') */
    2279          40 :         char quote_id_char = TDS_IS_MSSQL(tds) ? ']' : '\"';
    2280             : 
    2281          40 :         CHECK_TDS_EXTRA(tds);
    2282             : 
    2283          40 :         len = idlen < 0 ? strlen(id) : (size_t) idlen;
    2284             : 
    2285          40 :         return tds_quote(buffer, quote_id_char, id, len);
    2286             : }
    2287             : 
    2288             : /**
    2289             :  * Quote a string
    2290             :  * \param tds    state information for the socket and the TDS protocol
    2291             :  * \param buffer buffer to store quoted id. If NULL do not write anything 
    2292             :  *        (useful to compute quote length)
    2293             :  * \param str    string to quote (not necessary NUL-terminated)
    2294             :  * \param len    length of string (-1 for NUL-terminated)
    2295             :  * \result written chars (not including needed terminator)
    2296             :  */
    2297             : size_t
    2298           0 : tds_quote_string(TDSSOCKET * tds TDS_UNUSED, char *buffer, const char *str, ptrdiff_t len)
    2299             : {
    2300           0 :         return tds_quote(buffer, '\'', str, len < 0 ? strlen(str) : (size_t) len);
    2301             : }
    2302             : 
    2303             : /**
    2304             :  * Set current cursor.
    2305             :  * Current cursor is the one will receive output from server.
    2306             :  * \tds
    2307             :  * \param cursor  cursor to set as current
    2308             :  */
    2309             : static inline void
    2310             : tds_set_cur_cursor(TDSSOCKET *tds, TDSCURSOR *cursor)
    2311             : {
    2312        4094 :         ++cursor->ref_count;
    2313        4094 :         if (tds->cur_cursor)
    2314           6 :                 tds_release_cursor(&tds->cur_cursor);
    2315        4094 :         tds->cur_cursor = cursor;
    2316             : }
    2317             : 
    2318             : TDSRET
    2319        2354 : tds_cursor_declare(TDSSOCKET * tds, TDSCURSOR * cursor, bool *something_to_send)
    2320             : {
    2321        2354 :         CHECK_TDS_EXTRA(tds);
    2322             : 
    2323        2354 :         if (!cursor)
    2324             :                 return TDS_FAIL;
    2325             : 
    2326        2354 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_declare() cursor id = %d\n", cursor->cursor_id);
    2327             : 
    2328        2354 :         if (IS_TDS7_PLUS(tds->conn)) {
    2329        2346 :                 cursor->srv_status |= TDS_CUR_ISTAT_DECLARED;
    2330        2346 :                 cursor->srv_status |= TDS_CUR_ISTAT_CLOSED;
    2331        2346 :                 cursor->srv_status |= TDS_CUR_ISTAT_RDONLY;
    2332             :         }
    2333             : 
    2334        2354 :         if (IS_TDS50(tds->conn)) {
    2335           8 :                 if (!*something_to_send) {
    2336           8 :                         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2337             :                                 return TDS_FAIL;
    2338             : 
    2339           8 :                         tds->out_flag = TDS_NORMAL;
    2340             :                 }
    2341           8 :                 if (tds->state != TDS_WRITING || tds->out_flag != TDS_NORMAL)
    2342             :                         return TDS_FAIL;
    2343             : 
    2344           8 :                 tds_put_byte(tds, TDS_CURDECLARE_TOKEN);
    2345             : 
    2346             :                 /* length of the data stream that follows */
    2347           8 :                 TDS_START_LEN_USMALLINT(tds) {
    2348           8 :                         TDS_START_LEN_TINYINT(tds) {
    2349           8 :                                 tds_put_string(tds, cursor->cursor_name, -1);
    2350           8 :                         } TDS_END_LEN
    2351           8 :                         tds_put_byte(tds, 1);   /* cursor option is read only=1, unused=0 */
    2352           8 :                         tds_put_byte(tds, 0);   /* status unused=0 */
    2353           8 :                         TDS_START_LEN_USMALLINT(tds) {
    2354           8 :                                 tds_put_string(tds, cursor->query, -1);
    2355           8 :                         } TDS_END_LEN
    2356           8 :                         tds_put_tinyint(tds, 0);        /* number of columns = 0 , valid value applicable only for updatable cursor */
    2357           8 :                 } TDS_END_LEN
    2358           8 :                 *something_to_send = true;
    2359             :         }
    2360             : 
    2361             :         return TDS_SUCCESS;
    2362             : }
    2363             : 
    2364             : TDSRET
    2365        2354 : tds_cursor_open(TDSSOCKET * tds, TDSCURSOR * cursor, TDSPARAMINFO *params, bool *something_to_send)
    2366             : {
    2367        2354 :         CHECK_TDS_EXTRA(tds);
    2368             : 
    2369        2354 :         if (!cursor)
    2370             :                 return TDS_FAIL;
    2371             : 
    2372        2354 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_open() cursor id = %d\n", cursor->cursor_id);
    2373             : 
    2374        2354 :         if (!*something_to_send) {
    2375        2348 :                 if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2376             :                         return TDS_FAIL;
    2377             :         }
    2378        2354 :         if (tds->state != TDS_WRITING)
    2379             :                 return TDS_FAIL;
    2380             : 
    2381        2354 :         tds_set_cur_cursor(tds, cursor);
    2382             : 
    2383        2354 :         if (IS_TDS50(tds->conn)) {
    2384           8 :                 tds->out_flag = TDS_NORMAL;
    2385           8 :                 tds_put_byte(tds, TDS_CUROPEN_TOKEN);
    2386           8 :                 TDS_START_LEN_USMALLINT(tds) {
    2387             : 
    2388             :                         /*tds_put_int(tds, cursor->cursor_id); *//* Only if cursor id is passed as zero, the cursor name need to be sent */
    2389             : 
    2390           8 :                         tds_put_int(tds, 0);
    2391           8 :                         TDS_START_LEN_TINYINT(tds) {
    2392           8 :                                 tds_put_string(tds, cursor->cursor_name, -1);
    2393           8 :                         } TDS_END_LEN
    2394           8 :                         tds_put_byte(tds, 0);   /* Cursor status : 0 for no arguments */
    2395           8 :                 } TDS_END_LEN
    2396           8 :                 *something_to_send = true;
    2397             :         }
    2398        2354 :         if (IS_TDS7_PLUS(tds->conn)) {
    2399             :                 const char *converted_query;
    2400             :                 size_t converted_query_len;
    2401        2346 :                 int num_params = params ? params->num_cols : 0;
    2402             :                 TDSFREEZE outer;
    2403        2346 :                 TDSRET rc = TDS_SUCCESS;
    2404             : 
    2405             :                 /* cursor statement */
    2406        2346 :                 converted_query =
    2407        2346 :                         tds_convert_string(tds, tds->conn->char_convs[client2ucs2], cursor->query,
    2408        2346 :                                            strlen(cursor->query), &converted_query_len);
    2409        2346 :                 if (!converted_query) {
    2410           0 :                         if (!*something_to_send)
    2411           0 :                                 tds_set_state(tds, TDS_IDLE);
    2412           0 :                         return TDS_FAIL;
    2413             :                 }
    2414             : 
    2415        2346 :                 tds_freeze(tds, &outer, 0);
    2416             : 
    2417             :                 /* RPC call to sp_cursoropen */
    2418        2346 :                 tds_start_query(tds, TDS_RPC);
    2419             : 
    2420             :                 /* procedure identifier by number */
    2421             : 
    2422        2346 :                 if (IS_TDS71_PLUS(tds->conn)) {
    2423        2346 :                         tds_put_smallint(tds, -1);
    2424        2346 :                         tds_put_smallint(tds, TDS_SP_CURSOROPEN);
    2425             :                 } else {
    2426           0 :                         TDS_PUT_N_AS_UCS2(tds, "sp_cursoropen");
    2427             :                 }
    2428             : 
    2429        2346 :                 tds_put_smallint(tds, 0);       /* flags */
    2430             : 
    2431             :                 /* return cursor handle (int) */
    2432             : 
    2433        2346 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2434        2346 :                 tds_put_byte(tds, 1);   /* output parameter  */
    2435        2346 :                 tds_put_byte(tds, SYBINTN);
    2436        2346 :                 tds_put_byte(tds, 4);
    2437        2346 :                 tds_put_byte(tds, 0);
    2438             : 
    2439        2346 :                 if (num_params) {
    2440        1766 :                         tds7_put_query_params(tds, converted_query, converted_query_len);
    2441             :                 } else {
    2442         580 :                         tds_put_byte(tds, 0);
    2443         580 :                         tds_put_byte(tds, 0);
    2444         580 :                         tds_put_byte(tds, SYBNTEXT);    /* must be Ntype */
    2445         580 :                         TDS_PUT_INT(tds, converted_query_len);
    2446         580 :                         if (IS_TDS71_PLUS(tds->conn))
    2447         580 :                                 tds_put_n(tds, tds->conn->collation, 5);
    2448         580 :                         TDS_PUT_INT(tds, converted_query_len);
    2449         580 :                         tds_put_n(tds, converted_query, (int)converted_query_len);
    2450             :                 }
    2451             : 
    2452             :                 /* type */
    2453        2346 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2454        2346 :                 tds_put_byte(tds, 1);   /* output parameter  */
    2455        2346 :                 tds_put_byte(tds, SYBINTN);
    2456        2346 :                 tds_put_byte(tds, 4);
    2457        2346 :                 tds_put_byte(tds, 4);
    2458        2346 :                 tds_put_int(tds, num_params ? cursor->type | 0x1000 : cursor->type);
    2459             : 
    2460             :                 /* concurrency */
    2461        2346 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2462        2346 :                 tds_put_byte(tds, 1);   /* output parameter  */
    2463        2346 :                 tds_put_byte(tds, SYBINTN);
    2464        2346 :                 tds_put_byte(tds, 4);
    2465        2346 :                 tds_put_byte(tds, 4);
    2466        2346 :                 tds_put_int(tds, cursor->concurrency);
    2467             : 
    2468             :                 /* row count */
    2469        2346 :                 tds_put_byte(tds, 0);
    2470        2346 :                 tds_put_byte(tds, 1);   /* output parameter  */
    2471        2346 :                 tds_put_byte(tds, SYBINTN);
    2472        2346 :                 tds_put_byte(tds, 4);
    2473        2346 :                 tds_put_byte(tds, 4);
    2474        2346 :                 tds_put_int(tds, 0);
    2475             : 
    2476        2346 :                 if (num_params) {
    2477             :                         int i;
    2478             : 
    2479        1766 :                         rc = tds7_write_param_def_from_query(tds, converted_query, converted_query_len, params);
    2480             : 
    2481        3532 :                         for (i = 0; i < num_params; i++) {
    2482        1766 :                                 TDSCOLUMN *param = params->columns[i];
    2483             :                                 /* TODO check error */
    2484        1766 :                                 tds_put_data_info(tds, param, 0);
    2485             :                                 /* FIXME handle error */
    2486        1766 :                                 tds_put_data(tds, param);
    2487             :                         }
    2488             :                 }
    2489        4692 :                 tds_convert_string_free(cursor->query, converted_query);
    2490        2346 :                 if (TDS_FAILED(rc)) {
    2491           0 :                         tds_freeze_abort(&outer);
    2492           0 :                         if (!*something_to_send)
    2493           0 :                                 tds_set_state(tds, TDS_IDLE);
    2494             :                         return rc;
    2495             :                 }
    2496        2346 :                 tds_freeze_close(&outer);
    2497             : 
    2498        2346 :                 *something_to_send = true;
    2499        2346 :                 tds->current_op = TDS_OP_CURSOROPEN;
    2500        2346 :                 tdsdump_log(TDS_DBG_ERROR, "tds_cursor_open (): RPC call set up \n");
    2501             :         }
    2502             : 
    2503             : 
    2504        2354 :         tdsdump_log(TDS_DBG_ERROR, "tds_cursor_open (): cursor open completed\n");
    2505             :         return TDS_SUCCESS;
    2506             : }
    2507             : 
    2508             : TDSRET
    2509         288 : tds_cursor_setrows(TDSSOCKET * tds, TDSCURSOR * cursor, bool *something_to_send)
    2510             : {
    2511         288 :         CHECK_TDS_EXTRA(tds);
    2512             : 
    2513         288 :         if (!cursor)
    2514             :                 return TDS_FAIL;
    2515             : 
    2516         288 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_setrows() cursor id = %d\n", cursor->cursor_id);
    2517             : 
    2518         288 :         if (IS_TDS7_PLUS(tds->conn)) {
    2519         280 :                 cursor->srv_status &= ~TDS_CUR_ISTAT_DECLARED;
    2520         280 :                 cursor->srv_status |= TDS_CUR_ISTAT_CLOSED;
    2521         280 :                 cursor->srv_status |= TDS_CUR_ISTAT_ROWCNT;
    2522             :         }
    2523             : 
    2524         288 :         if (IS_TDS50(tds->conn)) {
    2525           8 :                 if (!*something_to_send) {
    2526           2 :                         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2527             :                                 return TDS_FAIL;
    2528             : 
    2529           2 :                         tds->out_flag = TDS_NORMAL;
    2530             :                 }
    2531           8 :                 if (tds->state != TDS_WRITING  || tds->out_flag != TDS_NORMAL)
    2532             :                         return TDS_FAIL;
    2533             : 
    2534           8 :                 tds_set_cur_cursor(tds, cursor);
    2535           8 :                 tds_put_byte(tds, TDS_CURINFO_TOKEN);
    2536             : 
    2537           8 :                 TDS_START_LEN_USMALLINT(tds) {
    2538             :                         /* length of data stream that follows */
    2539             : 
    2540             :                         /* tds_put_int(tds, tds->cursor->cursor_id); */ /* Cursor id */
    2541             : 
    2542           8 :                         tds_put_int(tds, 0);
    2543           8 :                         TDS_START_LEN_TINYINT(tds) {
    2544           8 :                                 tds_put_string(tds, cursor->cursor_name, -1);
    2545           8 :                         } TDS_END_LEN
    2546           8 :                         tds_put_byte(tds, 1);   /* Command  TDS_CUR_CMD_SETCURROWS */
    2547           8 :                         tds_put_byte(tds, 0x00);        /* Status - TDS_CUR_ISTAT_ROWCNT 0x0020 */
    2548           8 :                         tds_put_byte(tds, 0x20);        /* Status - TDS_CUR_ISTAT_ROWCNT 0x0020 */
    2549           8 :                         tds_put_int(tds, cursor->cursor_rows);       /* row count to set */
    2550           8 :                 } TDS_END_LEN
    2551           8 :                 *something_to_send = true;
    2552             : 
    2553             :         }
    2554             :         return TDS_SUCCESS;
    2555             : }
    2556             : 
    2557             : static void
    2558         824 : tds7_put_cursor_fetch(TDSSOCKET * tds, TDS_INT cursor_id, TDS_TINYINT fetch_type, TDS_INT i_row, TDS_INT num_rows)
    2559             : {
    2560         824 :         if (IS_TDS71_PLUS(tds->conn)) {
    2561         824 :                 tds_put_smallint(tds, -1);
    2562         824 :                 tds_put_smallint(tds, TDS_SP_CURSORFETCH);
    2563             :         } else {
    2564           0 :                 TDS_PUT_N_AS_UCS2(tds, "sp_cursorfetch");
    2565             :         }
    2566             : 
    2567             :         /* This flag tells the SP only to */
    2568             :         /* output a dummy metadata token  */
    2569             : 
    2570         824 :         tds_put_smallint(tds, 2);
    2571             : 
    2572             :         /* input cursor handle (int) */
    2573             : 
    2574         824 :         tds_put_byte(tds, 0);   /* no parameter name */
    2575         824 :         tds_put_byte(tds, 0);   /* input parameter  */
    2576         824 :         tds_put_byte(tds, SYBINTN);
    2577         824 :         tds_put_byte(tds, 4);
    2578         824 :         tds_put_byte(tds, 4);
    2579         824 :         tds_put_int(tds, cursor_id);
    2580             : 
    2581             :         /* fetch type - 2 = NEXT */
    2582             : 
    2583         824 :         tds_put_byte(tds, 0);   /* no parameter name */
    2584         824 :         tds_put_byte(tds, 0);   /* input parameter  */
    2585         824 :         tds_put_byte(tds, SYBINTN);
    2586         824 :         tds_put_byte(tds, 4);
    2587         824 :         tds_put_byte(tds, 4);
    2588         824 :         tds_put_int(tds, fetch_type);
    2589             : 
    2590             :         /* row number */
    2591         824 :         tds_put_byte(tds, 0);   /* no parameter name */
    2592         824 :         tds_put_byte(tds, 0);   /* input parameter  */
    2593         824 :         tds_put_byte(tds, SYBINTN);
    2594         824 :         tds_put_byte(tds, 4);
    2595         824 :         if ((fetch_type & 0x30) != 0) {
    2596          72 :                 tds_put_byte(tds, 4);
    2597          72 :                 tds_put_int(tds, i_row);
    2598             :         } else {
    2599         752 :                 tds_put_byte(tds, 0);
    2600             :         }
    2601             : 
    2602             :         /* number of rows to fetch */
    2603         824 :         tds_put_byte(tds, 0);   /* no parameter name */
    2604         824 :         tds_put_byte(tds, 0);   /* input parameter  */
    2605         824 :         tds_put_byte(tds, SYBINTN);
    2606         824 :         tds_put_byte(tds, 4);
    2607         824 :         tds_put_byte(tds, 4);
    2608         824 :         tds_put_int(tds, num_rows);
    2609         824 : }
    2610             : 
    2611             : TDSRET
    2612         846 : tds_cursor_fetch(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_CURSOR_FETCH fetch_type, TDS_INT i_row)
    2613             : {
    2614         846 :         CHECK_TDS_EXTRA(tds);
    2615             : 
    2616         846 :         if (!cursor)
    2617             :                 return TDS_FAIL;
    2618             : 
    2619         846 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_fetch() cursor id = %d\n", cursor->cursor_id);
    2620             : 
    2621         846 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2622             :                 return TDS_FAIL;
    2623             : 
    2624         846 :         tds_set_cur_cursor(tds, cursor);
    2625             : 
    2626         846 :         if (IS_TDS50(tds->conn)) {
    2627          30 :                 size_t len = strlen(cursor->cursor_name);
    2628          30 :                 size_t row_len = 0;
    2629             : 
    2630          30 :                 tds->out_flag = TDS_NORMAL;
    2631          30 :                 tds_put_byte(tds, TDS_CURFETCH_TOKEN);
    2632             : 
    2633          30 :                 if (len > (255-10))
    2634           0 :                         len = (255-10);
    2635          30 :                 if (fetch_type == TDS_CURSOR_FETCH_ABSOLUTE || fetch_type == TDS_CURSOR_FETCH_RELATIVE)
    2636           0 :                         row_len = 4;
    2637             : 
    2638             :                 /*tds_put_smallint(tds, 8); */
    2639             : 
    2640          30 :                 TDS_PUT_SMALLINT(tds, 6 + len + row_len);       /* length of the data stream that follows */
    2641             : 
    2642             :                 /*tds_put_int(tds, cursor->cursor_id); *//* cursor id returned by the server */
    2643             : 
    2644          30 :                 tds_put_int(tds, 0);
    2645          30 :                 TDS_PUT_BYTE(tds, len);
    2646          30 :                 tds_put_n(tds, cursor->cursor_name, len);
    2647          30 :                 tds_put_tinyint(tds, fetch_type);
    2648             : 
    2649             :                 /* optional argument to fetch row at absolute/relative position */
    2650          30 :                 if (row_len)
    2651           0 :                         tds_put_int(tds, i_row);
    2652          30 :                 return tds_query_flush_packet(tds);
    2653             :         }
    2654             : 
    2655         816 :         if (IS_TDS7_PLUS(tds->conn)) {
    2656             : 
    2657             :                 /* RPC call to sp_cursorfetch */
    2658             :                 static const unsigned char mssql_fetch[7] = {
    2659             :                         0,
    2660             :                         2,    /* TDS_CURSOR_FETCH_NEXT */
    2661             :                         4,    /* TDS_CURSOR_FETCH_PREV */
    2662             :                         1,    /* TDS_CURSOR_FETCH_FIRST */
    2663             :                         8,    /* TDS_CURSOR_FETCH_LAST */
    2664             :                         0x10, /* TDS_CURSOR_FETCH_ABSOLUTE */
    2665             :                         0x20  /* TDS_CURSOR_FETCH_RELATIVE */
    2666             :                 };
    2667             : 
    2668         816 :                 tds_start_query(tds, TDS_RPC);
    2669             : 
    2670             :                 /* TODO enum for 2 ... */
    2671         816 :                 if (cursor->type == 2 && fetch_type == TDS_CURSOR_FETCH_ABSOLUTE) {
    2672             :                         /* strangely dynamic cursor do not support absolute so emulate it with first + relative */
    2673           8 :                         tds7_put_cursor_fetch(tds, cursor->cursor_id, 1, 0, 0);
    2674             :                         /* TODO define constant */
    2675           8 :                         tds_put_byte(tds, IS_TDS72_PLUS(tds->conn) ? 0xff : 0x80);
    2676           8 :                         tds7_put_cursor_fetch(tds, cursor->cursor_id, 0x20, i_row, cursor->cursor_rows);
    2677             :                 } else {
    2678             :                         /* TODO check fetch_type ?? */
    2679         808 :                         tds7_put_cursor_fetch(tds, cursor->cursor_id, mssql_fetch[fetch_type], i_row, cursor->cursor_rows);
    2680             :                 }
    2681             : 
    2682         816 :                 tds->current_op = TDS_OP_CURSORFETCH;
    2683         816 :                 return tds_query_flush_packet(tds);
    2684             :         }
    2685             : 
    2686           0 :         tds_set_state(tds, TDS_IDLE);
    2687           0 :         return TDS_SUCCESS;
    2688             : }
    2689             : 
    2690             : TDSRET
    2691          24 : tds_cursor_get_cursor_info(TDSSOCKET *tds, TDSCURSOR *cursor, TDS_UINT *prow_number, TDS_UINT *prow_count)
    2692             : {
    2693             :         int done_flags;
    2694             :         TDSRET retcode;
    2695             :         TDS_INT result_type;
    2696             : 
    2697          24 :         CHECK_TDS_EXTRA(tds);
    2698             : 
    2699          24 :         if (!cursor)
    2700             :                 return TDS_FAIL;
    2701             : 
    2702          24 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_get_cursor_info() cursor id = %d\n", cursor->cursor_id);
    2703             : 
    2704             :         /* Assume not known */
    2705          24 :         assert(prow_number && prow_count);
    2706          24 :         *prow_number = 0;
    2707          24 :         *prow_count = 0;
    2708             : 
    2709          24 :         if (IS_TDS7_PLUS(tds->conn)) {
    2710             :                 /* Change state to querying */
    2711          24 :                 if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2712             :                         return TDS_FAIL;
    2713             : 
    2714             :                 /* Remember the server has been sent a command for this cursor */
    2715          24 :                 tds_set_cur_cursor(tds, cursor);
    2716             : 
    2717             :                 /* General initialization of server command */
    2718          24 :                 tds_start_query(tds, TDS_RPC);
    2719             : 
    2720             :                 /* Create and send query to server */
    2721          24 :                 if (IS_TDS71_PLUS(tds->conn)) {
    2722          24 :                         tds_put_smallint(tds, -1);
    2723          24 :                         tds_put_smallint(tds, TDS_SP_CURSORFETCH);
    2724             :                 } else {
    2725           0 :                         TDS_PUT_N_AS_UCS2(tds, "sp_cursorfetch");
    2726             :                 }
    2727             : 
    2728             :                 /* This flag tells the SP only to */
    2729             :                 /* output a dummy metadata token  */
    2730             : 
    2731          24 :                 tds_put_smallint(tds, 2);
    2732             : 
    2733             :                 /* input cursor handle (int) */
    2734             : 
    2735          24 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2736          24 :                 tds_put_byte(tds, 0);   /* input parameter  */
    2737          24 :                 tds_put_byte(tds, SYBINTN);
    2738          24 :                 tds_put_byte(tds, 4);
    2739          24 :                 tds_put_byte(tds, 4);
    2740          24 :                 tds_put_int(tds, cursor->cursor_id);
    2741             : 
    2742          24 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2743          24 :                 tds_put_byte(tds, 0);   /* input parameter  */
    2744          24 :                 tds_put_byte(tds, SYBINTN);
    2745          24 :                 tds_put_byte(tds, 4);
    2746          24 :                 tds_put_byte(tds, 4);
    2747          24 :                 tds_put_int(tds, 0x100);        /* FETCH_INFO */
    2748             : 
    2749             :                 /* row number */
    2750          24 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2751          24 :                 tds_put_byte(tds, 1);   /* output parameter  */
    2752          24 :                 tds_put_byte(tds, SYBINTN);
    2753          24 :                 tds_put_byte(tds, 4);
    2754          24 :                 tds_put_byte(tds, 0);
    2755             : 
    2756             :                 /* number of rows fetched */
    2757          24 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2758          24 :                 tds_put_byte(tds, 1);   /* output parameter  */
    2759          24 :                 tds_put_byte(tds, SYBINTN);
    2760          24 :                 tds_put_byte(tds, 4);
    2761          24 :                 tds_put_byte(tds, 0);
    2762             : 
    2763             :                 /* Adjust current state */
    2764          24 :                 tds->current_op = TDS_OP_NONE;
    2765          24 :                 TDS_PROPAGATE(tds_query_flush_packet(tds));
    2766             : 
    2767             :                 /* Process answer from server */
    2768             :                 for (;;) {
    2769          72 :                         retcode = tds_process_tokens(tds, &result_type, &done_flags, TDS_RETURN_PROC);
    2770          72 :                         tdsdump_log(TDS_DBG_FUNC, "tds_cursor_get_cursor_info: tds_process_tokens returned %d\n", retcode);
    2771          72 :                         tdsdump_log(TDS_DBG_FUNC, "    result_type=%d, TDS_DONE_COUNT=%x, TDS_DONE_ERROR=%x\n", 
    2772             :                                                   result_type, (done_flags & TDS_DONE_COUNT), (done_flags & TDS_DONE_ERROR));
    2773          72 :                         switch (retcode) {
    2774             :                         case TDS_NO_MORE_RESULTS:
    2775             :                                 return TDS_SUCCESS;
    2776          48 :                         case TDS_SUCCESS:
    2777          48 :                                 if (result_type==TDS_PARAM_RESULT) {
    2778             :                                         /* Status is updated when TDS_STATUS_RESULT token arrives, before the params are processed */
    2779          24 :                                         if (tds->has_status && tds->ret_status==0) {
    2780          24 :                                                 TDSPARAMINFO *pinfo = tds->current_results;
    2781             : 
    2782             :                                                 /* Make sure the params retuned have the correct type and size */
    2783          24 :                                                 if (pinfo && pinfo->num_cols==2
    2784          24 :                                                           && pinfo->columns[0]->on_server.column_type==SYBINTN
    2785          24 :                                                           && pinfo->columns[1]->on_server.column_type==SYBINTN
    2786          24 :                                                           && pinfo->columns[0]->column_size==4
    2787          24 :                                                           && pinfo->columns[1]->column_size==4) {
    2788             :                                                         /* Take the values */
    2789          24 :                                                         *prow_number = (TDS_UINT)(*(TDS_INT *) pinfo->columns[0]->column_data);
    2790          24 :                                                         *prow_count  = (TDS_UINT)(*(TDS_INT *) pinfo->columns[1]->column_data);
    2791          24 :                                                         tdsdump_log(TDS_DBG_FUNC, "----------------> prow_number=%u, prow_count=%u\n", 
    2792             :                                                                                   *prow_count, *prow_number);
    2793             :                                                 }
    2794             :                                         }
    2795             :                                 }
    2796             :                                 break;
    2797             :                         default:
    2798             :                                 return retcode;
    2799             :                         }
    2800             :                 }
    2801             :         }
    2802             : 
    2803             :         return TDS_SUCCESS;
    2804             : }
    2805             : 
    2806             : TDSRET
    2807         288 : tds_cursor_close(TDSSOCKET * tds, TDSCURSOR * cursor)
    2808             : {
    2809         288 :         CHECK_TDS_EXTRA(tds);
    2810             : 
    2811         288 :         if (!cursor)
    2812             :                 return TDS_FAIL;
    2813             : 
    2814         288 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_close() cursor id = %d\n", cursor->cursor_id);
    2815             : 
    2816         288 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2817             :                 return TDS_FAIL;
    2818             : 
    2819         288 :         tds_set_cur_cursor(tds, cursor);
    2820             : 
    2821         288 :         if (IS_TDS50(tds->conn)) {
    2822           8 :                 tds->out_flag = TDS_NORMAL;
    2823           8 :                 tds_put_byte(tds, TDS_CURCLOSE_TOKEN);
    2824           8 :                 tds_put_smallint(tds, 5);       /* length of the data stream that follows */
    2825           8 :                 tds_put_int(tds, cursor->cursor_id); /* cursor id returned by the server is available now */
    2826             : 
    2827           8 :                 if (cursor->status.dealloc == TDS_CURSOR_STATE_REQUESTED) {
    2828           2 :                         tds_put_byte(tds, 0x01);        /* Close option: TDS_CUR_COPT_DEALLOC */
    2829           2 :                         cursor->status.dealloc = TDS_CURSOR_STATE_SENT;
    2830             :                 }
    2831             :                 else
    2832           6 :                         tds_put_byte(tds, 0x00);        /* Close option: TDS_CUR_COPT_UNUSED */
    2833             : 
    2834             :         }
    2835         288 :         if (IS_TDS7_PLUS(tds->conn)) {
    2836             : 
    2837             :                 /* RPC call to sp_cursorclose */
    2838         280 :                 tds_start_query(tds, TDS_RPC);
    2839             : 
    2840         280 :                 if (IS_TDS71_PLUS(tds->conn)) {
    2841         280 :                         tds_put_smallint(tds, -1);
    2842         280 :                         tds_put_smallint(tds, TDS_SP_CURSORCLOSE);
    2843             :                 } else {
    2844           0 :                         TDS_PUT_N_AS_UCS2(tds, "sp_cursorclose");
    2845             :                 }
    2846             : 
    2847             :                 /* This flag tells the SP to output only a dummy metadata token  */
    2848             : 
    2849         280 :                 tds_put_smallint(tds, 2);
    2850             : 
    2851             :                 /* input cursor handle (int) */
    2852             : 
    2853         280 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2854         280 :                 tds_put_byte(tds, 0);   /* input parameter  */
    2855         280 :                 tds_put_byte(tds, SYBINTN);
    2856         280 :                 tds_put_byte(tds, 4);
    2857         280 :                 tds_put_byte(tds, 4);
    2858         280 :                 tds_put_int(tds, cursor->cursor_id);
    2859         280 :                 tds->current_op = TDS_OP_CURSORCLOSE;
    2860             :         }
    2861         288 :         return tds_query_flush_packet(tds);
    2862             : 
    2863             : }
    2864             : 
    2865             : TDSRET
    2866         248 : tds_cursor_setname(TDSSOCKET * tds, TDSCURSOR * cursor)
    2867             : {
    2868             :         TDSFREEZE outer;
    2869             :         unsigned int written;
    2870             : 
    2871         248 :         CHECK_TDS_EXTRA(tds);
    2872             : 
    2873         248 :         if (!cursor)
    2874             :                 return TDS_FAIL;
    2875             : 
    2876         248 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_setname() cursor id = %d\n", cursor->cursor_id);
    2877             : 
    2878         248 :         if (!IS_TDS7_PLUS(tds->conn))
    2879             :                 return TDS_SUCCESS;
    2880             : 
    2881         248 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2882             :                 return TDS_FAIL;
    2883             : 
    2884         248 :         tds_set_cur_cursor(tds, cursor);
    2885             : 
    2886             :         /* RPC call to sp_cursoroption */
    2887         248 :         tds_start_query(tds, TDS_RPC);
    2888             : 
    2889         248 :         if (IS_TDS71_PLUS(tds->conn)) {
    2890         248 :                 tds_put_smallint(tds, -1);
    2891         248 :                 tds_put_smallint(tds, TDS_SP_CURSOROPTION);
    2892             :         } else {
    2893           0 :                 TDS_PUT_N_AS_UCS2(tds, "sp_cursoroption");
    2894             :         }
    2895             : 
    2896         248 :         tds_put_smallint(tds, 0);
    2897             : 
    2898             :         /* input cursor handle (int) */
    2899         248 :         tds_put_byte(tds, 0);   /* no parameter name */
    2900         248 :         tds_put_byte(tds, 0);   /* input parameter  */
    2901         248 :         tds_put_byte(tds, SYBINTN);
    2902         248 :         tds_put_byte(tds, 4);
    2903         248 :         tds_put_byte(tds, 4);
    2904         248 :         tds_put_int(tds, cursor->cursor_id);
    2905             : 
    2906             :         /* code, 2 == set cursor name */
    2907         248 :         tds_put_byte(tds, 0);   /* no parameter name */
    2908         248 :         tds_put_byte(tds, 0);   /* input parameter  */
    2909         248 :         tds_put_byte(tds, SYBINTN);
    2910         248 :         tds_put_byte(tds, 4);
    2911         248 :         tds_put_byte(tds, 4);
    2912         248 :         tds_put_int(tds, 2);
    2913             : 
    2914             :         /* cursor name */
    2915         248 :         tds_put_byte(tds, 0);
    2916         248 :         tds_put_byte(tds, 0);
    2917         248 :         tds_put_byte(tds, XSYBNVARCHAR);
    2918         248 :         tds_freeze(tds, &outer, 2);
    2919         248 :         if (IS_TDS71_PLUS(tds->conn))
    2920         248 :                 tds_put_n(tds, tds->conn->collation, 5);
    2921         248 :         TDS_START_LEN_USMALLINT(tds) {
    2922         248 :                 tds_put_string(tds, cursor->cursor_name, -1);
    2923         248 :                 written = tds_freeze_written(current_freeze) - 2;
    2924         248 :         } TDS_END_LEN
    2925         248 :         tds_freeze_close_len(&outer, written);
    2926             : 
    2927         248 :         tds->current_op = TDS_OP_CURSOROPTION;
    2928             : 
    2929         248 :         return tds_query_flush_packet(tds);
    2930             : }
    2931             : 
    2932             : TDSRET
    2933         320 : tds_cursor_update(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_CURSOR_OPERATION op, TDS_INT i_row, TDSPARAMINFO *params)
    2934             : {
    2935         320 :         CHECK_TDS_EXTRA(tds);
    2936             : 
    2937         320 :         if (!cursor)
    2938             :                 return TDS_FAIL;
    2939             : 
    2940         320 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_update() cursor id = %d\n", cursor->cursor_id);
    2941             : 
    2942             :         /* client must provide parameters for update */
    2943         320 :         if (op == TDS_CURSOR_UPDATE && (!params || params->num_cols <= 0))
    2944             :                 return TDS_FAIL;
    2945             : 
    2946         320 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    2947             :                 return TDS_FAIL;
    2948             : 
    2949         320 :         tds_set_cur_cursor(tds, cursor);
    2950             : 
    2951         320 :         if (IS_TDS50(tds->conn)) {
    2952           0 :                 tds->out_flag = TDS_NORMAL;
    2953             : 
    2954             :                 /* FIXME finish*/
    2955           0 :                 tds_set_state(tds, TDS_IDLE);
    2956           0 :                 return TDS_FAIL;
    2957             :         }
    2958         320 :         if (IS_TDS7_PLUS(tds->conn)) {
    2959             : 
    2960             :                 /* RPC call to sp_cursorclose */
    2961         320 :                 tds_start_query(tds, TDS_RPC);
    2962             : 
    2963         320 :                 if (IS_TDS71_PLUS(tds->conn)) {
    2964         320 :                         tds_put_smallint(tds, -1);
    2965         320 :                         tds_put_smallint(tds, TDS_SP_CURSOR);
    2966             :                 } else {
    2967           0 :                         TDS_PUT_N_AS_UCS2(tds, "sp_cursor");
    2968             :                 }
    2969             : 
    2970         320 :                 tds_put_smallint(tds, 0);
    2971             : 
    2972             :                 /* input cursor handle (int) */
    2973         320 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2974         320 :                 tds_put_byte(tds, 0);   /* input parameter  */
    2975         320 :                 tds_put_byte(tds, SYBINTN);
    2976         320 :                 tds_put_byte(tds, 4);
    2977         320 :                 tds_put_byte(tds, 4);
    2978         320 :                 tds_put_int(tds, cursor->cursor_id);
    2979             : 
    2980             :                 /* cursor operation */
    2981         320 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2982         320 :                 tds_put_byte(tds, 0);   /* input parameter  */
    2983         320 :                 tds_put_byte(tds, SYBINTN);
    2984         320 :                 tds_put_byte(tds, 4);
    2985         320 :                 tds_put_byte(tds, 4);
    2986         320 :                 tds_put_int(tds, 32 | op);
    2987             : 
    2988             :                 /* row number */
    2989         320 :                 tds_put_byte(tds, 0);   /* no parameter name */
    2990         320 :                 tds_put_byte(tds, 0);   /* input parameter  */
    2991         320 :                 tds_put_byte(tds, SYBINTN);
    2992         320 :                 tds_put_byte(tds, 4);
    2993         320 :                 tds_put_byte(tds, 4);
    2994         320 :                 tds_put_int(tds, i_row);
    2995             : 
    2996             :                 /* update require table name */
    2997         320 :                 if (op == TDS_CURSOR_UPDATE) {
    2998             :                         TDSCOLUMN *param;
    2999             :                         unsigned int n, num_params;
    3000          80 :                         const char *table_name = NULL;
    3001             :                         TDSFREEZE outer;
    3002             :                         unsigned int written;
    3003             : 
    3004             :                         /* empty table name */
    3005          80 :                         tds_put_byte(tds, 0);
    3006          80 :                         tds_put_byte(tds, 0);
    3007          80 :                         tds_put_byte(tds, XSYBNVARCHAR);
    3008          80 :                         num_params = params->num_cols;
    3009          80 :                         for (n = 0; n < num_params; ++n) {
    3010          80 :                                 param = params->columns[n];
    3011         160 :                                 if (!tds_dstr_isempty(&param->table_name)) {
    3012         160 :                                         table_name = tds_dstr_cstr(&param->table_name);
    3013          80 :                                         break;
    3014             :                                 }
    3015             :                         }
    3016             : 
    3017          80 :                         tds_freeze(tds, &outer, 2);
    3018          80 :                         if (IS_TDS71_PLUS(tds->conn))
    3019          80 :                                 tds_put_n(tds, tds->conn->collation, 5);
    3020          80 :                         TDS_START_LEN_USMALLINT(tds) {
    3021          80 :                                 if (table_name)
    3022          80 :                                         tds_put_string(tds, table_name, -1);
    3023          80 :                                 written = tds_freeze_written(current_freeze) - 2;
    3024          80 :                         } TDS_END_LEN
    3025          80 :                         tds_freeze_close_len(&outer, written);
    3026             : 
    3027             :                         /* output columns to update */
    3028         224 :                         for (n = 0; n < num_params; ++n) {
    3029         144 :                                 param = params->columns[n];
    3030             :                                 /* TODO check error */
    3031         144 :                                 tds_put_data_info(tds, param, TDS_PUT_DATA_USE_NAME|TDS_PUT_DATA_PREFIX_NAME);
    3032             :                                 /* FIXME handle error */
    3033         144 :                                 tds_put_data(tds, param);
    3034             :                         }
    3035             :                 }
    3036             : 
    3037         320 :                 tds->current_op = TDS_OP_CURSOR;
    3038             :         }
    3039         320 :         return tds_query_flush_packet(tds);
    3040             : }
    3041             : 
    3042             : /**
    3043             :  * Check if a cursor is allocated into the server.
    3044             :  * If is not allocated it assures is removed from the connection list
    3045             :  * \tds
    3046             :  * \return true if allocated false otherwise
    3047             :  */
    3048             : static bool
    3049        2336 : tds_cursor_check_allocated(TDSCONNECTION * conn, TDSCURSOR * cursor)
    3050             : {
    3051        2336 :         if (cursor->srv_status == TDS_CUR_ISTAT_UNUSED || (cursor->srv_status & TDS_CUR_ISTAT_DEALLOC) != 0
    3052         286 :             || (IS_TDS7_PLUS(conn) && (cursor->srv_status & TDS_CUR_ISTAT_CLOSED) != 0)) {
    3053        2330 :                 tds_cursor_deallocated(conn, cursor);
    3054        2330 :                 return false;
    3055             :         }
    3056             : 
    3057             :         return true;
    3058             : }
    3059             : 
    3060             : /**
    3061             :  * Send a deallocation request to server
    3062             :  */
    3063             : TDSRET
    3064        2336 : tds_cursor_dealloc(TDSSOCKET * tds, TDSCURSOR * cursor)
    3065             : {
    3066        2336 :         TDSRET res = TDS_SUCCESS;
    3067             : 
    3068        2336 :         CHECK_TDS_EXTRA(tds);
    3069             : 
    3070        2336 :         if (!cursor)
    3071             :                 return TDS_FAIL;
    3072             : 
    3073        2336 :         if (!tds_cursor_check_allocated(tds->conn, cursor))
    3074             :                 return TDS_SUCCESS;
    3075             : 
    3076           6 :         tdsdump_log(TDS_DBG_INFO1, "tds_cursor_dealloc() cursor id = %d\n", cursor->cursor_id);
    3077             : 
    3078           6 :         if (IS_TDS50(tds->conn)) {
    3079           6 :                 if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    3080             :                         return TDS_FAIL;
    3081           6 :                 tds_set_cur_cursor(tds, cursor);
    3082             : 
    3083           6 :                 tds->out_flag = TDS_NORMAL;
    3084           6 :                 tds_put_byte(tds, TDS_CURCLOSE_TOKEN);
    3085           6 :                 tds_put_smallint(tds, 5);       /* length of the data stream that follows */
    3086           6 :                 tds_put_int(tds, cursor->cursor_id); /* cursor id returned by the server is available now */
    3087           6 :                 tds_put_byte(tds, 0x01);        /* Close option: TDS_CUR_COPT_DEALLOC */
    3088           6 :                 res = tds_query_flush_packet(tds);
    3089             :         }
    3090             : 
    3091             :         /*
    3092             :          * in TDS 5 the cursor deallocate function involves
    3093             :          * a server interaction. The cursor will be freed
    3094             :          * when we receive acknowledgement of the cursor
    3095             :          * deallocate from the server. for TDS 7 we do it
    3096             :          * here...
    3097             :          */
    3098           6 :         if (IS_TDS7_PLUS(tds->conn)) {
    3099           0 :                 if (cursor->status.dealloc == TDS_CURSOR_STATE_SENT ||
    3100             :                         cursor->status.dealloc == TDS_CURSOR_STATE_REQUESTED) {
    3101           0 :                         tdsdump_log(TDS_DBG_ERROR, "tds_cursor_dealloc(): freeing cursor \n");
    3102             :                 }
    3103             :         }
    3104             : 
    3105             :         return res;
    3106             : }
    3107             : 
    3108             : /**
    3109             :  * Deallocate cursor on idle.
    3110             :  * This let libTDS close the cursor when possible.
    3111             :  * \tds
    3112             :  * \param cursor   cursor to close
    3113             :  */
    3114             : TDSRET
    3115           0 : tds_deferred_cursor_dealloc(TDSCONNECTION *conn, TDSCURSOR * cursor)
    3116             : {
    3117             :         CHECK_CONN_EXTRA(conn);
    3118           0 :         CHECK_CURSOR_EXTRA(cursor);
    3119             : 
    3120             :         /* do not mark if already deallocated */
    3121           0 :         if (!tds_cursor_check_allocated(conn, cursor))
    3122             :                 return TDS_SUCCESS;
    3123             : 
    3124           0 :         cursor->defer_close = true;
    3125           0 :         conn->pending_close = 1;
    3126             : 
    3127           0 :         return TDS_SUCCESS;
    3128             : }
    3129             : 
    3130             : /**
    3131             :  * Send a string to server while quoting it.
    3132             :  * \tds
    3133             :  * \param s    string start
    3134             :  * \param end  string end
    3135             :  */
    3136             : static void
    3137         732 : tds_quote_and_put(TDSSOCKET * tds, const char *s, const char *end)
    3138             : {
    3139             :         char buf[256];
    3140             :         int i;
    3141             : 
    3142         732 :         CHECK_TDS_EXTRA(tds);
    3143             :         
    3144        2906 :         for (i = 0; s != end; ++s) {
    3145        2174 :                 buf[i++] = *s;
    3146        2174 :                 if (*s == '\'')
    3147           0 :                         buf[i++] = '\'';
    3148        2174 :                 if (i >= 254) {
    3149           0 :                         tds_put_string(tds, buf, i);
    3150           0 :                         i = 0;
    3151             :                 }
    3152             :         }
    3153         732 :         tds_put_string(tds, buf, i);
    3154         732 : }
    3155             : 
    3156             : typedef struct tds_quoteout_stream {
    3157             :         TDSOUTSTREAM stream;
    3158             :         TDSSOCKET *tds;
    3159             :         char buffer[2048];
    3160             : } TDSQUOTEOUTSTREAM;
    3161             : 
    3162             : static int
    3163         802 : tds_quoteout_stream_write(TDSOUTSTREAM *stream, size_t len)
    3164             : {
    3165         802 :         TDSQUOTEOUTSTREAM *s = (TDSQUOTEOUTSTREAM *) stream;
    3166         802 :         TDSSOCKET *tds = s->tds;
    3167             :         uint16_t buf[sizeof(s->buffer)];
    3168             : 
    3169         802 :         assert(len <= stream->buf_len);
    3170             : 
    3171             : #define QUOTE(type, ch) do { \
    3172             :         type *src, *dst = (type *) buf, *end = (type *) (s->buffer + len); \
    3173             : \
    3174             :         for (src = (type *) s->buffer; src < end; ++src) { \
    3175             :                 if (*src == (ch)) \
    3176             :                         *dst++ = *src; \
    3177             :                 *dst++ = *src; \
    3178             :         } \
    3179             :         tds_put_n(tds, buf, (char *) dst - (char *) buf); \
    3180             : } while(0)
    3181             : 
    3182         802 :         if (IS_TDS7_PLUS(tds->conn))
    3183         560 :                 QUOTE(uint16_t, TDS_HOST2LE('\''));
    3184             :         else
    3185         242 :                 QUOTE(char, '\'');
    3186             : 
    3187             : #undef QUOTE
    3188             : 
    3189         802 :         return len;
    3190             : }
    3191             : 
    3192             : static void
    3193             : tds_quoteout_stream_init(TDSQUOTEOUTSTREAM * stream, TDSSOCKET * tds)
    3194             : {
    3195         678 :         stream->stream.write = tds_quoteout_stream_write;
    3196         678 :         stream->stream.buffer = stream->buffer;
    3197         678 :         stream->stream.buf_len = sizeof(stream->buffer);
    3198         678 :         stream->tds = tds;
    3199             : }
    3200             : 
    3201             : static TDSRET
    3202         678 : tds_put_char_param_as_string(TDSSOCKET * tds, const TDSCOLUMN *curcol)
    3203             : {
    3204             :         TDS_CHAR *src;
    3205         678 :         TDSICONV *char_conv = curcol->char_conv;
    3206             :         int from, to;
    3207             :         TDSSTATICINSTREAM r;
    3208             :         TDSQUOTEOUTSTREAM w;
    3209             : 
    3210         678 :         src = (TDS_CHAR *) curcol->column_data;
    3211         678 :         if (is_blob_col(curcol))
    3212          70 :                 src = ((TDSBLOB *)src)->textvalue;
    3213             : 
    3214         678 :         if (is_unicode_type(curcol->on_server.column_type))
    3215           0 :                 tds_put_string(tds, "N", 1);
    3216         678 :         tds_put_string(tds, "\'", 1);
    3217             : 
    3218             :         /* Compute proper characted conversion.
    3219             :          * The conversion should be to UTF16/UCS2 for MS SQL.
    3220             :          * Avoid double conversion, convert directly from client to server.
    3221             :          */
    3222         678 :         from = char_conv ? char_conv->from.charset.canonic : tds->conn->char_convs[client2ucs2]->from.charset.canonic;
    3223         678 :         to = tds->conn->char_convs[IS_TDS7_PLUS(tds->conn) ? client2ucs2 : client2server_chardata]->to.charset.canonic;
    3224         678 :         if (!char_conv || char_conv->to.charset.canonic != to)
    3225         568 :                 char_conv = tds_iconv_get_info(tds->conn, from, to);
    3226         678 :         if (!char_conv)
    3227             :                 return TDS_FAIL;
    3228             : 
    3229         678 :         tds_staticin_stream_init(&r, src, curcol->column_cur_size);
    3230         678 :         tds_quoteout_stream_init(&w, tds);
    3231             : 
    3232         678 :         tds_convert_stream(tds, char_conv, to_server, &r.stream, &w.stream);
    3233             : 
    3234         678 :         tds_put_string(tds, "\'", 1);
    3235         678 :         return TDS_SUCCESS;
    3236             : }
    3237             : 
    3238             : /**
    3239             :  * Send a parameter to server.
    3240             :  * Parameters are converted to string and sent to server.
    3241             :  * \tds
    3242             :  * \param params   parameters structure
    3243             :  * \param n        number of parameter to send
    3244             :  * \returns TDS_FAIL or TDS_SUCCESS
    3245             :  */
    3246             : static TDSRET
    3247        1484 : tds_put_param_as_string(TDSSOCKET * tds, TDSPARAMINFO * params, int n)
    3248             : {
    3249        1484 :         TDSCOLUMN *curcol = params->columns[n];
    3250             :         CONV_RESULT cr;
    3251             :         TDS_INT res;
    3252             :         TDS_CHAR *src;
    3253        1484 :         int src_len = curcol->column_cur_size;
    3254             : 
    3255             :         int i;
    3256             :         char buf[256];
    3257        1484 :         bool quote = false;
    3258             : 
    3259        1484 :         CHECK_TDS_EXTRA(tds);
    3260        1484 :         CHECK_PARAMINFO_EXTRA(params);
    3261             : 
    3262        1484 :         if (src_len < 0) {
    3263             :                 /* on TDS 4 TEXT/IMAGE cannot be NULL, use empty */
    3264          42 :                 if (!IS_TDS50_PLUS(tds->conn) && type_has_textptr(curcol->on_server.column_type))
    3265           0 :                         tds_put_string(tds, "''", 2);
    3266             :                 else
    3267          42 :                         tds_put_string(tds, "NULL", 4);
    3268             :                 return TDS_SUCCESS;
    3269             :         }
    3270             : 
    3271        1442 :         if (is_char_type(curcol->on_server.column_type))
    3272         678 :                 return tds_put_char_param_as_string(tds, curcol);
    3273             : 
    3274         764 :         src = (TDS_CHAR *) curcol->column_data;
    3275         764 :         if (is_blob_col(curcol))
    3276          28 :                 src = ((TDSBLOB *)src)->textvalue;
    3277             : 
    3278             :         /* we could try to use only tds_convert but is not good in all cases */
    3279         764 :         switch (curcol->on_server.column_type) {
    3280             :         /* binary/char, do conversion in line */
    3281          32 :         case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY:
    3282          32 :                 tds_put_string(tds, "0x", 2);
    3283      423242 :                 for (i=0; src_len; ++src, --src_len) {
    3284      423210 :                         buf[i++] = tds_hex_digits[*src >> 4 & 0xF];
    3285      423210 :                         buf[i++] = tds_hex_digits[*src & 0xF];
    3286      423210 :                         if (i == 256) {
    3287        3300 :                                 tds_put_string(tds, buf, i);
    3288        3300 :                                 i = 0;
    3289             :                         }
    3290             :                 }
    3291          32 :                 tds_put_string(tds, buf, i);
    3292          32 :                 break;
    3293             :         /* TODO date, use iso format */
    3294          18 :         case SYBDATETIME:
    3295             :         case SYBDATETIME4:
    3296             :         case SYBDATETIMN:
    3297             :         case SYBMSTIME:
    3298             :         case SYBMSDATE:
    3299             :         case SYBMSDATETIME2:
    3300             :         case SYBMSDATETIMEOFFSET:
    3301             :         case SYBTIME:
    3302             :         case SYBDATE:
    3303             :         case SYB5BIGTIME:
    3304             :         case SYB5BIGDATETIME:
    3305             :                 /* TODO use an ISO context */
    3306             :         case SYBUNIQUE:
    3307          18 :                 quote = true;
    3308         732 :         default:
    3309         732 :                 res = tds_convert(tds_get_ctx(tds), tds_get_conversion_type(curcol->on_server.column_type, curcol->column_size), src, src_len, SYBCHAR, &cr);
    3310         732 :                 if (res < 0)
    3311             :                         return TDS_FAIL;
    3312             : 
    3313         732 :                 if (quote)
    3314          18 :                         tds_put_string(tds, "\'", 1);
    3315         732 :                 tds_quote_and_put(tds, cr.c, cr.c + res);
    3316         732 :                 if (quote)
    3317          18 :                         tds_put_string(tds, "\'", 1);
    3318         732 :                 free(cr.c);
    3319             :         }
    3320             :         return TDS_SUCCESS;
    3321             : }
    3322             : 
    3323             : /**
    3324             :  * Emulate prepared execute traslating to a normal language
    3325             :  */
    3326             : static TDSRET
    3327         842 : tds_send_emulated_execute(TDSSOCKET * tds, const char *query, TDSPARAMINFO * params)
    3328             : {
    3329             :         int num_placeholders, i;
    3330             :         const char *s, *e;
    3331             : 
    3332         842 :         CHECK_TDS_EXTRA(tds);
    3333             : 
    3334         842 :         assert(query);
    3335             : 
    3336         842 :         num_placeholders = tds_count_placeholders(query);
    3337        1684 :         if (num_placeholders && num_placeholders > params->num_cols)
    3338             :                 return TDS_FAIL;
    3339             :         
    3340             :         /* 
    3341             :          * NOTE: even for TDS5 we use this packet so to avoid computing 
    3342             :          * entire sql command
    3343             :          */
    3344         842 :         tds->out_flag = TDS_QUERY;
    3345         842 :         if (!num_placeholders) {
    3346           0 :                 tds_put_string(tds, query, -1);
    3347           0 :                 return TDS_SUCCESS;
    3348             :         }
    3349             : 
    3350             :         s = query;
    3351        1484 :         for (i = 0;; ++i) {
    3352        3810 :                 e = tds_next_placeholder(s);
    3353        2326 :                 tds_put_string(tds, s, (int)(e ? e - s : -1));
    3354        2326 :                 if (!e)
    3355             :                         break;
    3356             :                 /* now translate parameter in string */
    3357        1484 :                 tds_put_param_as_string(tds, params, i);
    3358             : 
    3359        1484 :                 s = e + 1;
    3360             :         }
    3361             :         
    3362             :         return TDS_SUCCESS;
    3363             : }
    3364             : 
    3365             : enum { MUL_STARTED = 1 };
    3366             : 
    3367             : TDSRET
    3368         154 : tds_multiple_init(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDS_MULTIPLE_TYPE type, TDSHEADERS * head)
    3369             : {
    3370             :         unsigned char packet_type;
    3371         154 :         multiple->type = type;
    3372         154 :         multiple->flags = 0;
    3373             : 
    3374         154 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    3375             :                 return TDS_FAIL;
    3376             : 
    3377         154 :         packet_type = TDS_QUERY;
    3378         154 :         switch (type) {
    3379             :         case TDS_MULTIPLE_QUERY:
    3380             :                 break;
    3381          66 :         case TDS_MULTIPLE_EXECUTE:
    3382             :         case TDS_MULTIPLE_RPC:
    3383          66 :                 if (IS_TDS7_PLUS(tds->conn))
    3384          64 :                         packet_type = TDS_RPC;
    3385             :                 break;
    3386             :         }
    3387         154 :         if (tds_start_query_head(tds, packet_type, head) != TDS_SUCCESS)
    3388             :                 return TDS_FAIL;
    3389             : 
    3390         154 :         return TDS_SUCCESS;
    3391             : }
    3392             : 
    3393             : TDSRET
    3394         154 : tds_multiple_done(TDSSOCKET *tds, TDSMULTIPLE *multiple)
    3395             : {
    3396         154 :         assert(tds && multiple);
    3397             : 
    3398         154 :         return tds_query_flush_packet(tds);
    3399             : }
    3400             : 
    3401             : TDSRET
    3402         640 : tds_multiple_query(TDSSOCKET *tds, TDSMULTIPLE *multiple, const char *query, TDSPARAMINFO * params)
    3403             : {
    3404         640 :         assert(multiple->type == TDS_MULTIPLE_QUERY);
    3405             : 
    3406         640 :         if (multiple->flags & MUL_STARTED)
    3407         552 :                 tds_put_string(tds, " ", 1);
    3408         640 :         multiple->flags |= MUL_STARTED;
    3409             : 
    3410         640 :         return tds_send_emulated_execute(tds, query, params);
    3411             : }
    3412             : 
    3413             : TDSRET
    3414         590 : tds_multiple_execute(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDSDYNAMIC * dyn)
    3415             : {
    3416         590 :         assert(multiple->type == TDS_MULTIPLE_EXECUTE);
    3417             : 
    3418         590 :         if (IS_TDS7_PLUS(tds->conn)) {
    3419         584 :                 if (multiple->flags & MUL_STARTED) {
    3420             :                         /* TODO define constant */
    3421         520 :                         tds_put_byte(tds, IS_TDS72_PLUS(tds->conn) ? 0xff : 0x80);
    3422             :                 }
    3423         584 :                 multiple->flags |= MUL_STARTED;
    3424             : 
    3425         584 :                 tds7_send_execute(tds, dyn);
    3426             : 
    3427         584 :                 return TDS_SUCCESS;
    3428             :         }
    3429             : 
    3430           6 :         if (multiple->flags & MUL_STARTED)
    3431           4 :                 tds_put_string(tds, " ", 1);
    3432           6 :         multiple->flags |= MUL_STARTED;
    3433             : 
    3434           6 :         return tds_send_emulated_execute(tds, dyn->query, dyn->params);
    3435             : }
    3436             : 
    3437             : /**
    3438             :  * Send option commands to server.
    3439             :  * Option commands are used to change server options.
    3440             :  * \tds
    3441             :  * \param command  command type.
    3442             :  * \param option   option to set/get.
    3443             :  * \param param    parameter value
    3444             :  * \param param_size  length of parameter value in bytes
    3445             :  */
    3446             : TDSRET
    3447          80 : tds_submit_optioncmd(TDSSOCKET * tds, TDS_OPTION_CMD command, TDS_OPTION option, TDS_OPTION_ARG *param, TDS_INT param_size)
    3448             : {
    3449             :         char cmd[128];
    3450             :  
    3451          80 :         CHECK_TDS_EXTRA(tds);
    3452             :  
    3453          80 :         tdsdump_log(TDS_DBG_FUNC, "tds_submit_optioncmd() \n");
    3454             :  
    3455          80 :         if (IS_TDS50(tds->conn)) {
    3456          16 :                 if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    3457             :                         return TDS_FAIL;
    3458             :  
    3459          16 :                 tds->out_flag = TDS_NORMAL;
    3460          16 :                 tds_put_byte(tds, TDS_OPTIONCMD_TOKEN);
    3461             :  
    3462          16 :                 tds_put_smallint(tds, 3 + param_size);
    3463          16 :                 tds_put_byte(tds, command);
    3464          16 :                 tds_put_byte(tds, option);
    3465          16 :                 tds_put_byte(tds, param_size);
    3466          16 :                 if (param_size)
    3467           8 :                         tds_put_n(tds, param, param_size);
    3468             :  
    3469          16 :                 tds_query_flush_packet(tds);
    3470             :  
    3471          16 :                 TDS_PROPAGATE(tds_process_simple_query(tds));
    3472          16 :                 return TDS_SUCCESS;
    3473             :         }
    3474             :  
    3475          64 :         if (!IS_TDS7_PLUS(tds->conn))
    3476             :                 return TDS_SUCCESS;
    3477             : 
    3478          64 :         cmd[0] = 0;
    3479          64 :         if (command == TDS_OPT_SET) {
    3480             :                 char datefmt[4];
    3481             : 
    3482          32 :                 switch (option) {
    3483           8 :                 case TDS_OPT_ANSINULL :
    3484           8 :                         sprintf(cmd, "SET ANSI_NULLS %s", param->ti ? "ON" : "OFF");
    3485           8 :                         break;
    3486           0 :                 case TDS_OPT_ARITHABORTON :
    3487           0 :                         strcpy(cmd, "SET ARITHABORT ON");
    3488           0 :                         break;
    3489           0 :                 case TDS_OPT_ARITHABORTOFF :
    3490           0 :                         strcpy(cmd, "SET ARITHABORT OFF");
    3491           0 :                         break;
    3492           0 :                 case TDS_OPT_ARITHIGNOREON :
    3493           0 :                         strcpy(cmd, "SET ARITHIGNORE ON");
    3494           0 :                         break;
    3495           0 :                 case TDS_OPT_ARITHIGNOREOFF :
    3496           0 :                         strcpy(cmd, "SET ARITHIGNORE OFF");
    3497           0 :                         break;
    3498           8 :                 case TDS_OPT_CHAINXACTS :
    3499           8 :                         sprintf(cmd, "SET IMPLICIT_TRANSACTIONS %s", param->ti ? "ON" : "OFF");
    3500           8 :                         break;
    3501           0 :                 case TDS_OPT_CURCLOSEONXACT :
    3502           0 :                         sprintf(cmd, "SET CURSOR_CLOSE_ON_COMMIT %s", param->ti ? "ON" : "OFF");
    3503           0 :                         break;
    3504           0 :                 case TDS_OPT_NOCOUNT :
    3505           0 :                         sprintf(cmd, "SET NOCOUNT %s", param->ti ? "ON" : "OFF");
    3506           0 :                         break;
    3507           0 :                 case TDS_OPT_QUOTED_IDENT :
    3508           0 :                         sprintf(cmd, "SET QUOTED_IDENTIFIER %s", param->ti ? "ON" : "OFF");
    3509           0 :                         break;
    3510           0 :                 case TDS_OPT_TRUNCABORT :
    3511           0 :                         sprintf(cmd, "SET ANSI_WARNINGS %s", param->ti ? "OFF" : "ON");
    3512           0 :                         break;
    3513           8 :                 case TDS_OPT_DATEFIRST :
    3514           8 :                         sprintf(cmd, "SET DATEFIRST %d", param->ti);
    3515           8 :                         break;
    3516           8 :                 case TDS_OPT_DATEFORMAT :
    3517           8 :                          switch (param->ti) {
    3518           0 :                                 case TDS_OPT_FMTDMY: strcpy(datefmt,"dmy"); break;
    3519           0 :                                 case TDS_OPT_FMTDYM: strcpy(datefmt,"dym"); break;
    3520           0 :                                 case TDS_OPT_FMTMDY: strcpy(datefmt,"mdy"); break;
    3521           8 :                                 case TDS_OPT_FMTMYD: strcpy(datefmt,"myd"); break;
    3522           0 :                                 case TDS_OPT_FMTYDM: strcpy(datefmt,"ydm"); break;
    3523           0 :                                 case TDS_OPT_FMTYMD: strcpy(datefmt,"ymd"); break;
    3524             :                         }
    3525           8 :                         sprintf(cmd, "SET DATEFORMAT %s", datefmt);
    3526           8 :                         break;
    3527           0 :                 case TDS_OPT_TEXTSIZE:
    3528           0 :                         sprintf(cmd, "SET TEXTSIZE %d", (int) param->i);
    3529           0 :                         break;
    3530             :                 /* TODO */
    3531             :                 case TDS_OPT_STAT_TIME:
    3532             :                 case TDS_OPT_STAT_IO:
    3533             :                 case TDS_OPT_ROWCOUNT:
    3534             :                 case TDS_OPT_NATLANG:
    3535             :                 case TDS_OPT_ISOLATION:
    3536             :                 case TDS_OPT_AUTHON:
    3537             :                 case TDS_OPT_CHARSET:
    3538             :                 case TDS_OPT_SHOWPLAN:
    3539             :                 case TDS_OPT_NOEXEC:
    3540             :                 case TDS_OPT_PARSEONLY:
    3541             :                 case TDS_OPT_GETDATA:
    3542             :                 case TDS_OPT_FORCEPLAN:
    3543             :                 case TDS_OPT_FORMATONLY:
    3544             :                 case TDS_OPT_FIPSFLAG:
    3545             :                 case TDS_OPT_RESTREES:
    3546             :                 case TDS_OPT_IDENTITYON:
    3547             :                 case TDS_OPT_CURREAD:
    3548             :                 case TDS_OPT_CURWRITE:
    3549             :                 case TDS_OPT_IDENTITYOFF:
    3550             :                 case TDS_OPT_AUTHOFF:
    3551             :                         break;
    3552             :                 }
    3553          32 :                 tds_submit_query(tds, cmd);
    3554          32 :                 TDS_PROPAGATE(tds_process_simple_query(tds));
    3555             :         }
    3556          64 :         if (command == TDS_OPT_LIST) {
    3557          32 :                 int optionval = 0;
    3558             :                 TDS_INT resulttype;
    3559             : 
    3560          32 :                 switch (option) {
    3561          16 :                 case TDS_OPT_ANSINULL :
    3562             :                 case TDS_OPT_ARITHABORTON :
    3563             :                 case TDS_OPT_ARITHABORTOFF :
    3564             :                 case TDS_OPT_ARITHIGNOREON :
    3565             :                 case TDS_OPT_ARITHIGNOREOFF :
    3566             :                 case TDS_OPT_CHAINXACTS :
    3567             :                 case TDS_OPT_CURCLOSEONXACT :
    3568             :                 case TDS_OPT_NOCOUNT :
    3569             :                 case TDS_OPT_QUOTED_IDENT :
    3570             :                 case TDS_OPT_TRUNCABORT :
    3571          16 :                         tdsdump_log(TDS_DBG_FUNC, "SELECT @@options\n");
    3572          16 :                         strcpy(cmd, "SELECT @@options");
    3573          16 :                         break;
    3574           8 :                 case TDS_OPT_DATEFIRST :
    3575           8 :                         strcpy(cmd, "SELECT @@datefirst");
    3576           8 :                         break;
    3577           8 :                 case TDS_OPT_DATEFORMAT :
    3578           8 :                         strcpy(cmd, "SELECT DATEPART(dy,'01/02/03')");
    3579           8 :                         break;
    3580           0 :                 case TDS_OPT_TEXTSIZE:
    3581           0 :                         strcpy(cmd, "SELECT @@textsize");
    3582           0 :                         break;
    3583             :                 /* TODO */
    3584           0 :                 case TDS_OPT_STAT_TIME:
    3585             :                 case TDS_OPT_STAT_IO:
    3586             :                 case TDS_OPT_ROWCOUNT:
    3587             :                 case TDS_OPT_NATLANG:
    3588             :                 case TDS_OPT_ISOLATION:
    3589             :                 case TDS_OPT_AUTHON:
    3590             :                 case TDS_OPT_CHARSET:
    3591             :                 case TDS_OPT_SHOWPLAN:
    3592             :                 case TDS_OPT_NOEXEC:
    3593             :                 case TDS_OPT_PARSEONLY:
    3594             :                 case TDS_OPT_GETDATA:
    3595             :                 case TDS_OPT_FORCEPLAN:
    3596             :                 case TDS_OPT_FORMATONLY:
    3597             :                 case TDS_OPT_FIPSFLAG:
    3598             :                 case TDS_OPT_RESTREES:
    3599             :                 case TDS_OPT_IDENTITYON:
    3600             :                 case TDS_OPT_CURREAD:
    3601             :                 case TDS_OPT_CURWRITE:
    3602             :                 case TDS_OPT_IDENTITYOFF:
    3603             :                 case TDS_OPT_AUTHOFF:
    3604             :                 default:
    3605           0 :                         tdsdump_log(TDS_DBG_FUNC, "what!\n");
    3606             :                 }
    3607             :                 tds_submit_query(tds, cmd);
    3608          96 :                 while (tds_process_tokens(tds, &resulttype, NULL, TDS_TOKEN_RESULTS) == TDS_SUCCESS) {
    3609          64 :                         switch (resulttype) {
    3610             :                         case TDS_ROWFMT_RESULT:
    3611             :                                 break;
    3612             :                         case TDS_ROW_RESULT:
    3613          64 :                                 while (tds_process_tokens(tds, &resulttype, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW) == TDS_SUCCESS) {
    3614          64 :                                         if (resulttype != TDS_ROW_RESULT)
    3615             :                                                 break;
    3616             : 
    3617          32 :                                         if (!tds->current_results || tds->current_results->num_cols < 1)
    3618           0 :                                                 continue;
    3619             : 
    3620             :                                         /* keep previous value if NULL or not convertible */
    3621          32 :                                         optionval = tds_get_int_col(tds, tds->current_results->columns[0], optionval);
    3622             :                                 }
    3623             :                                 break;
    3624             :                         default:
    3625             :                                 break;
    3626             :                         }
    3627             :                 }
    3628          32 :                 tdsdump_log(TDS_DBG_FUNC, "optionval = %d\n", optionval);
    3629          32 :                 switch (option) {
    3630           8 :                 case TDS_OPT_CHAINXACTS :
    3631           8 :                         tds->option_value = (optionval & 0x02) > 0;
    3632           8 :                         break;
    3633           0 :                 case TDS_OPT_CURCLOSEONXACT :
    3634           0 :                         tds->option_value = (optionval & 0x04) > 0;
    3635           0 :                         break;
    3636           0 :                 case TDS_OPT_TRUNCABORT :
    3637           0 :                         tds->option_value = (optionval & 0x08) > 0;
    3638           0 :                         break;
    3639           8 :                 case TDS_OPT_ANSINULL :
    3640           8 :                         tds->option_value = (optionval & 0x20) > 0;
    3641           8 :                         break;
    3642           0 :                 case TDS_OPT_ARITHABORTON :
    3643           0 :                         tds->option_value = (optionval & 0x40) > 0;
    3644           0 :                         break;
    3645           0 :                 case TDS_OPT_ARITHABORTOFF :
    3646           0 :                         tds->option_value = (optionval & 0x40) > 0;
    3647           0 :                         break;
    3648           0 :                 case TDS_OPT_ARITHIGNOREON :
    3649           0 :                         tds->option_value = (optionval & 0x80) > 0;
    3650           0 :                         break;
    3651           0 :                 case TDS_OPT_ARITHIGNOREOFF :
    3652           0 :                         tds->option_value = (optionval & 0x80) > 0;
    3653           0 :                         break;
    3654           0 :                 case TDS_OPT_QUOTED_IDENT :
    3655           0 :                         tds->option_value = (optionval & 0x0100) > 0;
    3656           0 :                         break;
    3657           0 :                 case TDS_OPT_NOCOUNT :
    3658           0 :                         tds->option_value = (optionval & 0x0200) > 0;
    3659           0 :                         break;
    3660           8 :                 case TDS_OPT_TEXTSIZE:
    3661             :                 case TDS_OPT_DATEFIRST :
    3662           8 :                         tds->option_value = optionval;
    3663           8 :                         break;
    3664           8 :                 case TDS_OPT_DATEFORMAT :
    3665           8 :                         switch (optionval) {
    3666           0 :                         case 61: tds->option_value = TDS_OPT_FMTYDM; break;
    3667           0 :                         case 34: tds->option_value = TDS_OPT_FMTYMD; break;
    3668           0 :                         case 32: tds->option_value = TDS_OPT_FMTDMY; break;
    3669           0 :                         case 60: tds->option_value = TDS_OPT_FMTYDM; break;
    3670           0 :                         case 2:  tds->option_value = TDS_OPT_FMTMDY; break;
    3671           8 :                         case 3:  tds->option_value = TDS_OPT_FMTMYD; break;
    3672             :                         }
    3673             :                         break;
    3674             :                 /* TODO */
    3675             :                 case TDS_OPT_STAT_TIME:
    3676             :                 case TDS_OPT_STAT_IO:
    3677             :                 case TDS_OPT_ROWCOUNT:
    3678             :                 case TDS_OPT_NATLANG:
    3679             :                 case TDS_OPT_ISOLATION:
    3680             :                 case TDS_OPT_AUTHON:
    3681             :                 case TDS_OPT_CHARSET:
    3682             :                 case TDS_OPT_SHOWPLAN:
    3683             :                 case TDS_OPT_NOEXEC:
    3684             :                 case TDS_OPT_PARSEONLY:
    3685             :                 case TDS_OPT_GETDATA:
    3686             :                 case TDS_OPT_FORCEPLAN:
    3687             :                 case TDS_OPT_FORMATONLY:
    3688             :                 case TDS_OPT_FIPSFLAG:
    3689             :                 case TDS_OPT_RESTREES:
    3690             :                 case TDS_OPT_IDENTITYON:
    3691             :                 case TDS_OPT_CURREAD:
    3692             :                 case TDS_OPT_CURWRITE:
    3693             :                 case TDS_OPT_IDENTITYOFF:
    3694             :                 case TDS_OPT_AUTHOFF:
    3695             :                         break;
    3696             :                 }
    3697          32 :                 tdsdump_log(TDS_DBG_FUNC, "tds_submit_optioncmd: returned option_value = %d\n", tds->option_value);
    3698             :         }
    3699             :         return TDS_SUCCESS;
    3700             : }
    3701             : 
    3702             : 
    3703             : /**
    3704             :  * Send a rollback request.
    3705             :  * TDS 7.2+ need this in order to handle transactions correctly if MARS is used.
    3706             :  * \tds
    3707             :  * \sa tds_submit_commit, tds_submit_rollback
    3708             :  */
    3709             : TDSRET
    3710         132 : tds_submit_begin_tran(TDSSOCKET *tds)
    3711             : {
    3712         132 :         CHECK_TDS_EXTRA(tds);
    3713             : 
    3714         132 :         if (!IS_TDS72_PLUS(tds->conn))
    3715          78 :                 return tds_submit_query(tds, "BEGIN TRANSACTION");
    3716             : 
    3717          54 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    3718             :                 return TDS_FAIL;
    3719             : 
    3720          54 :         tds_start_query(tds, TDS7_TRANS);
    3721             : 
    3722             :         /* begin transaction */
    3723          54 :         tds_put_smallint(tds, 5);
    3724          54 :         tds_put_byte(tds, 0);   /* new transaction level TODO */
    3725          54 :         tds_put_byte(tds, 0);   /* new transaction name */
    3726             : 
    3727          54 :         return tds_query_flush_packet(tds);
    3728             : }
    3729             : 
    3730             : /**
    3731             :  * Send a rollback request.
    3732             :  * TDS 7.2+ need this in order to handle transactions correctly if MARS is used.
    3733             :  * \tds
    3734             :  * \param cont true to start a new transaction
    3735             :  * \sa tds_submit_begin_tran, tds_submit_commit
    3736             :  */
    3737             : TDSRET
    3738         516 : tds_submit_rollback(TDSSOCKET *tds, bool cont)
    3739             : {
    3740         516 :         CHECK_TDS_EXTRA(tds);
    3741             : 
    3742         516 :         if (!IS_TDS72_PLUS(tds->conn))
    3743         640 :                 return tds_submit_query(tds, cont ? "IF @@TRANCOUNT > 0 ROLLBACK BEGIN TRANSACTION" : "IF @@TRANCOUNT > 0 ROLLBACK");
    3744             : 
    3745         196 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    3746             :                 return TDS_FAIL;
    3747             : 
    3748         196 :         tds_start_query(tds, TDS7_TRANS);
    3749         196 :         tds_put_smallint(tds, 8);       /* rollback */
    3750         196 :         tds_put_byte(tds, 0);   /* name */
    3751         196 :         if (cont) {
    3752         172 :                 tds_put_byte(tds, 1);
    3753         172 :                 tds_put_byte(tds, 0);   /* new transaction level TODO */
    3754         172 :                 tds_put_byte(tds, 0);   /* new transaction name */
    3755             :         } else {
    3756          24 :                 tds_put_byte(tds, 0);   /* do not continue */
    3757             :         }
    3758         196 :         return tds_query_flush_packet(tds);
    3759             : }
    3760             : 
    3761             : /**
    3762             :  * Send a commit request.
    3763             :  * TDS 7.2+ need this in order to handle transactions correctly if MARS is used.
    3764             :  * \tds
    3765             :  * \param cont true to start a new transaction
    3766             :  * \sa tds_submit_rollback, tds_submit_begin_tran
    3767             :  */
    3768             : TDSRET
    3769         282 : tds_submit_commit(TDSSOCKET *tds, bool cont)
    3770             : {
    3771         282 :         CHECK_TDS_EXTRA(tds);
    3772             : 
    3773         282 :         if (!IS_TDS72_PLUS(tds->conn))
    3774         328 :                 return tds_submit_query(tds, cont ? "IF @@TRANCOUNT > 0 COMMIT BEGIN TRANSACTION" : "IF @@TRANCOUNT > 0 COMMIT");
    3775             : 
    3776         118 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
    3777             :                 return TDS_FAIL;
    3778             : 
    3779         118 :         tds_start_query(tds, TDS7_TRANS);
    3780         118 :         tds_put_smallint(tds, 7);       /* commit */
    3781         118 :         tds_put_byte(tds, 0);   /* name */
    3782         118 :         if (cont) {
    3783         118 :                 tds_put_byte(tds, 1);
    3784         118 :                 tds_put_byte(tds, 0);   /* new transaction level TODO */
    3785         118 :                 tds_put_byte(tds, 0);   /* new transaction name */
    3786             :         } else {
    3787           0 :                 tds_put_byte(tds, 0);   /* do not continue */
    3788             :         }
    3789         118 :         return tds_query_flush_packet(tds);
    3790             : }
    3791             : 
    3792             : static const TDSCONTEXT empty_ctx = {0};
    3793             : 
    3794             : TDSRET
    3795        3905 : tds_disconnect(TDSSOCKET * tds)
    3796             : {
    3797             :         TDS_INT old_timeout;
    3798             :         const TDSCONTEXT *old_ctx;
    3799             : 
    3800        3905 :         CHECK_TDS_EXTRA(tds);
    3801             :  
    3802        3905 :         tdsdump_log(TDS_DBG_FUNC, "tds_disconnect()\n");
    3803             :  
    3804        3905 :         if (!IS_TDS50(tds->conn))
    3805             :                 return TDS_SUCCESS;
    3806             : 
    3807             :         /* Socket should not be busy, otherwise this can create an infinite
    3808             :          * loop. */
    3809         766 :         if (tds_mutex_trylock(&tds->wire_mtx))
    3810             :                 return TDS_FAIL;
    3811         756 :         tds_mutex_unlock(&tds->wire_mtx);
    3812             : 
    3813         756 :         old_timeout = tds->query_timeout;
    3814         756 :         old_ctx = tds_get_ctx(tds);
    3815             : 
    3816             :         /* avoid to stall forever */
    3817         756 :         tds->query_timeout = 5;
    3818             : 
    3819             :         /* do not report errors to upper libraries */
    3820         756 :         tds_set_ctx(tds, &empty_ctx);
    3821             : 
    3822         756 :         if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING) {
    3823          16 :                 tds->query_timeout = old_timeout;
    3824          16 :                 tds_set_ctx(tds, old_ctx);
    3825          16 :                 return TDS_FAIL;
    3826             :         }
    3827             :  
    3828         740 :         tds->out_flag = TDS_NORMAL;
    3829         740 :         tds_put_byte(tds, TDS_LOGOUT_TOKEN);
    3830         740 :         tds_put_byte(tds, 0);
    3831             :  
    3832         740 :         tds_query_flush_packet(tds);
    3833             :  
    3834         740 :         return tds_process_simple_query(tds);
    3835             : }
    3836             :  
    3837             : /*
    3838             :  * TODO add function to return type suitable for param
    3839             :  * ie:
    3840             :  * sybvarchar -> sybvarchar / xsybvarchar
    3841             :  * sybint4 -> sybintn
    3842             :  */
    3843             : 
    3844             : /** @} */

Generated by: LCOV version 1.13