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

Generated by: LCOV version 1.13