LCOV - code coverage report
Current view: top level - src/tds - query.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 1464 1741 84.1 %
Date: 2025-04-13 14:39:30 Functions: 62 65 95.4 %

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

Generated by: LCOV version 1.13