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

Generated by: LCOV version 1.13