LCOV - code coverage report
Current view: top level - src/tds - query.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 1466 1741 84.2 %
Date: 2025-01-18 12:13:41 Functions: 62 65 95.4 %

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

Generated by: LCOV version 1.13