LCOV - code coverage report
Current view: top level - src/dblib - dblib.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 1411 2612 54.0 %
Date: 2026-02-12 22:26:45 Functions: 98 161 60.9 %

          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-2015  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             : 
      25             : #include <freetds/time.h>
      26             : 
      27             : #include <assert.h>
      28             : #include <stdio.h>
      29             : 
      30             : #if HAVE_STDLIB_H
      31             : #include <stdlib.h>
      32             : #endif /* HAVE_STDLIB_H */
      33             : 
      34             : #if HAVE_STRING_H
      35             : #include <string.h>
      36             : #endif /* HAVE_STRING_H */
      37             : 
      38             : #if HAVE_UNISTD_H
      39             : #include <unistd.h>
      40             : #endif /* HAVE_UNISTD_H */
      41             : 
      42             : #if HAVE_ERRNO_H
      43             : # include <errno.h>
      44             : #endif /* HAVE_ERRNO_H */
      45             : 
      46             : /** 
      47             :  * \ingroup dblib_core
      48             :  * \remarks Either SYBDBLIB or MSDBLIB (not both) must be defined. 
      49             :  *      This affects how certain application-addressable 
      50             :  *      strucures are defined.  
      51             :  */
      52             : #include <freetds/tds.h>
      53             : #include <freetds/thread.h>
      54             : #include <freetds/convert.h>
      55             : #include <freetds/utils/string.h>
      56             : #include <freetds/data.h>
      57             : #include <freetds/replacements.h>
      58             : #include <sybfront.h>
      59             : #include <sybdb.h>
      60             : #include <syberror.h>
      61             : #include <dblib.h>
      62             : 
      63             : static RETCODE _dbresults(DBPROCESS * dbproc);
      64             : static BYTE *_dbcoldata(TDSCOLUMN *colinfo);
      65             : static int _get_printable_size(TDSCOLUMN * colinfo);
      66             : static char *_dbprdate(char *timestr);
      67             : static int _dbnullable(DBPROCESS * dbproc, int column);
      68             : static const char *tds_prdatatype(int datatype_token);
      69             : 
      70             : static int default_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr);
      71             : 
      72             : void copy_data_to_host_var(DBPROCESS *, TDS_SERVER_TYPE, const BYTE *, int, BYTE *, DBINT, int, DBINT *);
      73             : RETCODE dbgetnull(DBPROCESS *dbproc, int bindtype, int varlen, BYTE* varaddr);
      74             : 
      75             : /**
      76             :  * \file dblib.c
      77             :  * Main implementation file for \c db-lib.
      78             :  */
      79             : /**
      80             :  * \file bcp.c
      81             :  * Implementation of \c db-lib bulk copy functions.
      82             :  */
      83             : /**
      84             :  * \defgroup dblib_api The db-lib API
      85             :  * Functions callable by \c db-lib client programs
      86             :  *
      87             :  * The \c db_lib interface is implemented by both Sybase and Microsoft.  FreeTDS seeks to implement 
      88             :  * first the intersection of the functions defined by the vendors.  
      89             :  */
      90             :  
      91             : /**
      92             :  * \ingroup dblib_api
      93             :  * \defgroup dblib_core Primary functions
      94             :  * Core functions needed by most db-lib programs.  
      95             : */
      96             : /**
      97             :  * \ingroup dblib_api
      98             :  * \defgroup dblib_rpc Remote Procedure functions
      99             :  * Functions used with stored procedures.  
     100             :  * Especially useful for OUTPUT parameters, because modern Microsoft servers do not 
     101             :  * return output parameter data to the client unless the procedure was invoked
     102             :  * with dbrpcsend().  
     103             :  */
     104             : /**
     105             :  * \ingroup dblib_api
     106             :  * \defgroup dblib_bcp Bulk copy functions
     107             :  * Functions to bulk-copy (a/k/a \em bcp) data to/from the database.  
     108             :  */
     109             : /**
     110             :  * \ingroup dblib_bcp
     111             :  * \defgroup dblib_bcp_internal Internal bcp functions
     112             :  * Static functions internal to the bcp library.  
     113             :  */
     114             : /**
     115             :  * \ingroup dblib_api
     116             :  * \defgroup dblib_money Money functions 
     117             :  * Functions to manipulate the MONEY datatype.  
     118             :  */
     119             : /**
     120             :  * \ingroup dblib_api
     121             :  * \defgroup dblib_datetime Datetime functions 
     122             :  * Functions to manipulate DBDATETIME structures.  Defined by Sybase only.  
     123             :  * These are not implemented:
     124             :  *      - dbdate4cmp()
     125             :  *      - dbdate4zero()
     126             :  *      - dbdatechar()
     127             :  *      - dbdatename()
     128             :  *      - dbdateorder()
     129             :  *      - dbdatepart()
     130             :  *      - dbdatezero()
     131             :  *      - dbdayname()
     132             :  */
     133             : /**
     134             :  * \ingroup dblib_api
     135             :  * \defgroup dblib_internal Internals
     136             :  * Functions called within \c db-lib for self-help.  
     137             :  * These functions are of interest only to people hacking on the FreeTDS db-lib implementation.  
     138             :  */
     139             : /**
     140             :  * \ingroup dblib_api
     141             :  * \defgroup dblib_unimplemented Unimplemented
     142             :  * Functions thus far not implemented in the FreeTDS db-lib implementation.  
     143             :  * While some of these are simply awaiting someone with time and skill (and inclination)
     144             :  * it might be noted here that the old browse functions (e.g. dbcolbrowse()) 
     145             :  * are on the never-to-do list.  
     146             :  * They were defined by Sybase and were superseded long ago, although they're still
     147             :  * present in Microsoft's implementation.  
     148             :  * They were never popular and today better alternatives are available.  
     149             :  * For completeness, they are:
     150             :  *      - dbcolbrowse()
     151             :  *      - dbcolsource()
     152             :  *      - dbfreequal()
     153             :  *      - dbqual()
     154             :  *      - dbtabbrowse()
     155             :  *      - dbtabcount()
     156             :  *      - dbtabname()
     157             :  *      - dbtabsource()
     158             :  *      - dbtsnewlen()
     159             :  *      - dbtsnewval()
     160             :  *      - dbtsput()
     161             :  */
     162             : 
     163             : /* info/err message handler functions (or rather pointers to them) */
     164             : MHANDLEFUNC _dblib_msg_handler = NULL;
     165             : EHANDLEFUNC _dblib_err_handler = default_err_handler;
     166             : 
     167             : /** \internal
     168             :  * \dblib_internal
     169             :  * \remarks A db-lib connection has an implicit TDS context. 
     170             :  */
     171             : typedef struct dblib_context
     172             : {
     173             :         /** reference count, time dbinit called */
     174             :         int ref_count;
     175             : 
     176             :         /** libTDS context */
     177             :         TDSCONTEXT *tds_ctx;
     178             :         /** libTDS context reference counter */
     179             :         int tds_ctx_ref_count;
     180             : 
     181             :         /* save all connection in a list */
     182             :         TDSSOCKET **connection_list;
     183             :         int connection_list_size;
     184             :         int connection_list_size_represented;
     185             :         char *recftos_filename;
     186             :         int recftos_filenum;
     187             :         int login_timeout;      /**< not used unless positive */
     188             :         int query_timeout;      /**< not used unless positive */
     189             : }
     190             : DBLIBCONTEXT;
     191             : 
     192             : static DBLIBCONTEXT g_dblib_ctx;
     193             : static tds_mutex dblib_mutex = TDS_MUTEX_INITIALIZER;
     194             : 
     195             : static int g_dblib_version =
     196             : #if TDS50
     197             :         DBVERSION_100;
     198             : #elif TDS71
     199             :         DBVERSION_71;
     200             : #elif TDS72
     201             :         DBVERSION_72;
     202             : #elif TDS73
     203             :         DBVERSION_73;
     204             : #elif TDS74
     205             :         DBVERSION_74;
     206             : #else
     207             :         DBVERSION_UNKNOWN;
     208             : #endif
     209             : 
     210             : 
     211             : static int
     212         864 : dblib_add_connection(DBLIBCONTEXT * ctx, TDSSOCKET * tds)
     213             : {
     214         864 :         int i = 0;
     215         864 :         const int list_size = ctx->connection_list_size_represented;
     216             : 
     217         864 :         tdsdump_log(TDS_DBG_FUNC, "dblib_add_connection(%p, %p)\n", ctx, tds);
     218             : 
     219        1466 :         while (i < list_size && ctx->connection_list[i])
     220         602 :                 i++;
     221         864 :         if (i == list_size) {
     222             :                 return 1;
     223             :         } else {
     224         854 :                 ctx->connection_list[i] = tds;
     225         854 :                 return 0;
     226             :         }
     227             : }
     228             : 
     229             : static void
     230         456 : dblib_del_connection(DBLIBCONTEXT * ctx, TDSSOCKET * tds)
     231             : {
     232         456 :         int i = 0;
     233         456 :         const int list_size = ctx->connection_list_size;
     234             : 
     235         456 :         tdsdump_log(TDS_DBG_FUNC, "dblib_del_connection(%p, %p)\n", ctx, tds);
     236             : 
     237       41966 :         while (i < list_size && ctx->connection_list[i] != tds)
     238       41510 :                 i++;
     239         456 :         if (i == list_size) {
     240             :                 /* connection wasn't on the free list...now what */
     241             :         } else {
     242             :                 /* remove it */
     243         446 :                 ctx->connection_list[i] = NULL;
     244             :         }
     245         456 : }
     246             : 
     247             : /**
     248             :  * Get global dblib TDS context.
     249             :  * dblib_mutex should be locked.
     250             :  */
     251             : static TDSCONTEXT *
     252        1366 : dblib_get_tds_ctx(void)
     253             : {
     254        1366 :         tdsdump_log(TDS_DBG_FUNC, "dblib_get_tds_ctx(void)\n");
     255             : 
     256        1366 :         ++g_dblib_ctx.tds_ctx_ref_count;
     257        1366 :         if (g_dblib_ctx.tds_ctx == NULL) {
     258         502 :                 TDSCONTEXT *tds_ctx = tds_alloc_context(&g_dblib_ctx);
     259             : 
     260         502 :                 if (!tds_ctx)
     261             :                         return NULL;
     262             : 
     263             :                 /*
     264             :                  * Set the functions in the TDS layer to point to the correct handler functions
     265             :                  */
     266         502 :                 tds_ctx->msg_handler = _dblib_handle_info_message;
     267         502 :                 tds_ctx->err_handler = _dblib_handle_err_message;
     268         502 :                 tds_ctx->int_handler = _dblib_check_and_handle_interrupt;
     269             : 
     270         502 :                 if (tds_ctx->locale && !tds_ctx->locale->datetime_fmt) {
     271             :                         /* set default in case there's no locale file */
     272             :                         static const char datetime_format[] = "%b %e %Y %l:%M:%S:%z%p";
     273           0 :                         tds_ctx->locale->datetime_fmt = strdup(datetime_format);
     274           0 :                         if (!tds_ctx->locale->datetime_fmt) {
     275           0 :                                 tds_free_context(tds_ctx);
     276           0 :                                 return NULL;
     277             :                         }
     278             :                 }
     279         502 :                 g_dblib_ctx.tds_ctx = tds_ctx;
     280             :         }
     281        1366 :         return g_dblib_ctx.tds_ctx;
     282             : }
     283             : 
     284             : /**
     285             :  * Decrease global dblib TDS context.
     286             :  * dblib_mutex should be locked.
     287             :  *
     288             :  * @param count number of references to decrease.
     289             :  */
     290             : static void
     291         928 : dblib_release_tds_ctx(int count)
     292             : {
     293         928 :         tdsdump_log(TDS_DBG_FUNC, "dblib_release_tds_ctx(%d)\n", count);
     294             : 
     295         928 :         g_dblib_ctx.tds_ctx_ref_count -= count;
     296         928 :         if (g_dblib_ctx.tds_ctx_ref_count <= 0) {
     297         472 :                 tds_free_context(g_dblib_ctx.tds_ctx);
     298         472 :                 g_dblib_ctx.tds_ctx = NULL;
     299             :         }
     300         928 : }
     301             : 
     302             : #include "buffering.h"
     303             : 
     304             : static void
     305        3198 : db_env_chg(TDSSOCKET * tds, int type, char *oldval, char *newval)
     306             : {
     307             :         DBPROCESS *dbproc;
     308             : 
     309        3198 :         assert(oldval != NULL && newval != NULL);
     310        3198 :         if (strlen(oldval) == 1 && *oldval == 1)
     311           0 :                 oldval = "(0x1)";
     312             :                 
     313        3198 :         tdsdump_log(TDS_DBG_FUNC, "db_env_chg(%p, %d, %s, %s)\n", tds, type, oldval, newval);
     314             : 
     315        3198 :         if (!tds || !tds_get_parent(tds))
     316             :                 return;
     317        3198 :         dbproc = (DBPROCESS *) tds_get_parent(tds);
     318             : 
     319        3198 :         dbproc->envchange_rcv |= (1 << (type - 1));
     320        3198 :         switch (type) {
     321        1664 :         case TDS_ENV_DATABASE:
     322        1664 :                 strlcpy(dbproc->dbcurdb, newval, sizeof(dbproc->dbcurdb));
     323        1664 :                 break;
     324           0 :         case TDS_ENV_CHARSET:
     325           0 :                 strlcpy(dbproc->servcharset, newval, sizeof(dbproc->servcharset));
     326           0 :                 break;
     327             :         default:
     328             :                 break;
     329             :         }
     330             :         return;
     331             : }
     332             : 
     333             : /** \internal
     334             :  * \ingroup dblib_internal
     335             :  * \brief Sanity checks for column-oriented functions.  
     336             :  * 
     337             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
     338             :  * \param pcolinfo address of pointer to a TDSCOLUMN structure.
     339             :  * \remarks Makes sure dbproc and the requested column are valid.  
     340             :  *      Calls dbperror() if not.  
     341             :  * \returns appropriate error or SUCCEED
     342             :  */
     343             : static TDSCOLUMN*
     344       11728 : dbcolptr(DBPROCESS* dbproc, int column)
     345             : {
     346             :         TDSSOCKET *tds;
     347             :         TDSRESULTINFO *info;
     348             : 
     349       11728 :         if (!dbproc) {
     350           0 :                 dbperror(dbproc, SYBENULL, 0);
     351           0 :                 return NULL;
     352             :         }
     353       11728 :         tds = dbproc->tds_socket;
     354       11728 :         if (IS_TDSDEAD(tds)) {
     355           0 :                 dbperror(dbproc, SYBEDDNE, 0);
     356           0 :                 return NULL;
     357             :         }
     358       11728 :         info = tds->res_info;
     359       11728 :         if (!info)
     360             :                 return NULL;
     361       11468 :         if (column < 1 || column > info->num_cols) {
     362           0 :                 dbperror(dbproc, SYBECNOR, 0);
     363           0 :                 return NULL;
     364             :         } 
     365             :         
     366       11468 :         return info->columns[column - 1];
     367             : }
     368             : 
     369             : static TDSCOLUMN*
     370          64 : dbacolptr(DBPROCESS* dbproc, int computeid, int column, bool is_bind)
     371             : {
     372             :         unsigned int i;
     373             :         TDSSOCKET *tds;
     374             :         TDSCOMPUTEINFO *info;
     375             : 
     376          64 :         if (!dbproc) {
     377           0 :                 dbperror(dbproc, SYBENULL, 0);
     378           0 :                 return NULL;
     379             :         }
     380          64 :         tds = dbproc->tds_socket;
     381          64 :         if (IS_TDSDEAD(tds)) {
     382           0 :                 dbperror(dbproc, SYBEDDNE, 0);
     383           0 :                 return NULL;
     384             :         }
     385          32 :         for (i = 0;; ++i) {
     386         128 :                 if (i >= tds->num_comp_info) {
     387             :                         /* Attempt to bind user variable to a non-existent compute row */
     388           0 :                         if (is_bind)
     389           0 :                                 dbperror(dbproc, SYBEBNCR, 0);
     390             :                         return NULL;
     391             :                 }
     392          96 :                 info = tds->comp_info[i];
     393          96 :                 if (info->computeid == computeid)
     394             :                         break;
     395             :         }
     396             :         /* Fail if either the compute id or the column number is invalid. */
     397          64 :         if (column < 1 || column > info->num_cols) {
     398           0 :                 dbperror(dbproc, is_bind ? SYBEABNC : SYBECNOR, 0);
     399           0 :                 return NULL;
     400             :         }
     401             : 
     402          64 :         return info->columns[column - 1];
     403             : }
     404             : 
     405             : /*
     406             :  * Default null substitution values
     407             :  * Binding Type         Null Substitution Value
     408             :  * TINYBIND             0
     409             :  * SMALLBIND            0
     410             :  * INTBIND              0
     411             :  * CHARBIND             Empty string (padded with blanks)
     412             :  * STRINGBIND           Empty string (padded with blanks, null-terminated)
     413             :  * NTBSTRINGBIND        Empty string (null-terminated)
     414             :  * VARYCHARBIND         Empty string
     415             :  * BINARYBIND           Empty array (padded with zeros)
     416             :  * VARYBINBIND          Empty array
     417             :  * DATETIMEBIND         8 bytes of zeros
     418             :  * SMALLDATETIMEBIND    8 bytes of zeros
     419             :  * MONEYBIND            $0.00
     420             :  * SMALLMONEYBIND       $0.00
     421             :  * FLT8BIND             0.0
     422             :  * REALBIND             0.0
     423             :  * DECIMALBIND          0.0 (with default scale and precision)
     424             :  * NUMERICBIND          0.0 (with default scale and precision)
     425             :  * BOUNDARYBIND         Empty string (null-terminated)
     426             :  * SENSITIVITYBIND      Empty string (null-terminated)
     427             :  */
     428             : 
     429             : static const DBBIT              null_BIT = 0;
     430             : static const DBTINYINT          null_TINYINT = 0;
     431             : static const DBSMALLINT         null_SMALLINT = 0;
     432             : static const DBINT              null_INT = 0;
     433             : static const DBBIGINT           null_BIGINT = 0;
     434             : static const DBFLT8             null_FLT8 = 0;
     435             : static const DBREAL             null_REAL = 0;
     436             : 
     437             : static const DBCHAR             null_CHAR = '\0';
     438             : static const DBVARYCHAR         null_VARYCHAR = { 0, {0} };
     439             : 
     440             : static const DBDATETIME         null_DATETIME = { 0, 0 };
     441             : static const DBDATETIME4        null_SMALLDATETIME = { 0, 0 };
     442             : static const DBMONEY            null_MONEY = { 0, 0 };
     443             : static const DBMONEY4           null_SMALLMONEY = {0};
     444             : static const DBNUMERIC          null_NUMERIC = { 0, 0, {0} };
     445             : static const TDS_DATETIMEALL    null_DATETIMEALL = { 0, 0, 0, 0 };
     446             : 
     447             : static NULLREP default_null_representations[MAXBINDTYPES] = {
     448             :         /* CHARBIND          0  */        {         NULL, 0 }
     449             :         /* STRINGBIND        1  */      , {         NULL, 0 }
     450             :         /* NTBSTRINGBIND     2  */      , { (BYTE*) &null_CHAR, sizeof(null_CHAR) }
     451             :         /* VARYCHARBIND      3  */      , { (BYTE*) &null_VARYCHAR, sizeof(null_VARYCHAR) }
     452             :         /* VARYBINBIND       4  */      , { (BYTE*) &null_VARYCHAR, sizeof(null_VARYCHAR) }
     453             :         /* no such bind      5  */      , {         NULL, 0 }                   
     454             :         /* TINYBIND          6  */      , {         &null_TINYINT, sizeof(null_TINYINT) }
     455             :         /* SMALLBIND         7  */      , { (BYTE*) &null_SMALLINT, sizeof(null_SMALLINT) }
     456             :         /* INTBIND           8  */      , { (BYTE*) &null_INT, sizeof(null_INT) }
     457             :         /* FLT8BIND          9  */      , { (BYTE*) &null_FLT8, sizeof(null_FLT8) }
     458             :         /* REALBIND          10 */      , { (BYTE*) &null_REAL, sizeof(null_REAL) }
     459             :         /* DATETIMEBIND      11 */      , { (BYTE*) &null_DATETIME, sizeof(null_DATETIME) }
     460             :         /* SMALLDATETIMEBIND 12 */      , { (BYTE*) &null_SMALLDATETIME, sizeof(null_SMALLDATETIME) }
     461             :         /* MONEYBIND         13 */      , { (BYTE*) &null_MONEY, sizeof(null_MONEY) }
     462             :         /* SMALLMONEYBIND    14 */      , { (BYTE*) &null_SMALLMONEY, sizeof(null_SMALLMONEY) }
     463             :         /* BINARYBIND        15 */      , {         NULL, 0 }               
     464             :         /* BITBIND           16 */      , {         &null_BIT, sizeof(null_BIT) }
     465             :         /* NUMERICBIND       17 */      , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
     466             :         /* DECIMALBIND       18 */      , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
     467             :         /* SRCNUMERICBIND    19 */      , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
     468             :         /* SRCDECIMALBIND    20 */      , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
     469             :         /* DATEBIND          21 */      , { (BYTE*) &null_INT, sizeof(null_INT) }
     470             :         /* TIMEBIND          22 */      , { (BYTE*) &null_INT, sizeof(null_INT) }
     471             :         /* BIGDATETIMEBIND   23 */      , { (BYTE*) &null_BIGINT, sizeof(null_BIGINT) }
     472             :         /* BIGTIMEBIND       24 */      , { (BYTE*) &null_BIGINT, sizeof(null_BIGINT) }
     473             :         /*                   25 */      , {         NULL, 0 }
     474             :         /*                   26 */      , {         NULL, 0 }
     475             :         /*                   27 */      , {         NULL, 0 }
     476             :         /*                   28 */      , {         NULL, 0 }
     477             :         /*                   29 */      , {         NULL, 0 }
     478             :         /* BIGINTBIND        30 */      , { (BYTE*) &null_BIGINT, sizeof(null_BIGINT) }
     479             :         /* DATETIME2BIND     31 */      , { (BYTE*) &null_DATETIMEALL, sizeof(null_DATETIMEALL) }
     480             :         /* MAXBINDTYPES      32 */
     481             : };
     482             : 
     483             : static int
     484         122 : dbbindtype(int datatype)
     485             : {
     486         122 :         switch (datatype) {
     487             :         case SYBIMAGE:
     488             :         case SYBVARBINARY:
     489             :         case SYBBINARY:         return BINARYBIND;
     490             :         
     491           0 :         case SYBBIT:            return BITBIND;
     492             : 
     493          22 :         case SYBLONGCHAR:
     494             :         case SYBTEXT:
     495             :         case SYBVARCHAR:
     496          22 :         case SYBCHAR:           return NTBSTRINGBIND;
     497             :         
     498           0 :         case SYBDATETIME:       return DATETIMEBIND;
     499           0 :         case SYBDATETIME4:      return SMALLDATETIMEBIND;
     500             : 
     501           0 :         case SYBDATE:           return DATEBIND;
     502           0 :         case SYBTIME:           return TIMEBIND;
     503             : 
     504           0 :         case SYB5BIGDATETIME:   return BIGDATETIMEBIND;
     505           0 :         case SYB5BIGTIME:       return BIGTIMEBIND;
     506             : 
     507           0 :         case SYBDECIMAL:        return DECIMALBIND;
     508           0 :         case SYBNUMERIC:        return NUMERICBIND;
     509             :         
     510           0 :         case SYBFLT8:           return FLT8BIND;
     511           0 :         case SYBREAL:           return REALBIND;
     512             : 
     513           0 :         case SYBINT1:           return TINYBIND;
     514           0 :         case SYBINT2:           return SMALLBIND;
     515           0 :         case SYBINT4:           return INTBIND;
     516           0 :         case SYBINT8:           return BIGINTBIND;
     517             : 
     518           0 :         case SYBMONEY:          return MONEYBIND;
     519           0 :         case SYBMONEY4:         return SMALLMONEYBIND;
     520             : 
     521           0 :         case SYBMSDATE:
     522             :         case SYBMSTIME:
     523             :         case SYBMSDATETIME2:
     524             :         case SYBMSDATETIMEOFFSET:
     525           0 :                                 return DATETIME2BIND;
     526             : 
     527             :         default:
     528           0 :                 assert(0 == "no such datatype");
     529             :         }
     530             :         
     531             :         return 0;
     532             : }
     533             : 
     534             : /** \internal
     535             :  * dbbind() says: "Note that if varlen is 0, no padding takes place"
     536             :  * dbgetnull() will not pad varaddr unless varlen is positive.  
     537             :  * Vartype              Program Type    Padding         Terminator
     538             :  * -------------------  --------------  --------------  ----------
     539             :  * CHARBIND             DBCHAR          blanks          none
     540             :  * STRINGBIND           DBCHAR          blanks          \0
     541             :  * NTBSTRINGBIND        DBCHAR          none            \0
     542             :  * VARYCHARBIND         DBVARYCHAR      none            none
     543             :  * BOUNDARYBIND         DBCHAR          none            \0
     544             :  * SENSITIVITYBIND      DBCHAR          none            \0
     545             :  */
     546             : RETCODE
     547         420 : dbgetnull(DBPROCESS *dbproc, int bindtype, int varlen, BYTE* varaddr)
     548             : {
     549         420 :         NULLREP *pnullrep = default_null_representations + bindtype;
     550             : 
     551         420 :         tdsdump_log(TDS_DBG_FUNC, "dbgetnull(%p, %d, %d, %p)\n", dbproc, bindtype, varlen, varaddr);
     552             : 
     553         420 :         CHECK_PARAMETER(varaddr, SYBENULL, FAIL);
     554         420 :         CHECK_PARAMETER(0 <= bindtype && bindtype < MAXBINDTYPES, SYBEBTYP, FAIL);
     555             : 
     556             :         /* dbproc can be NULL */
     557         420 :         if (NULL != dbproc) {
     558         320 :                 assert(dbproc->nullreps);
     559         320 :                 pnullrep = dbproc->nullreps + bindtype;
     560             :         }
     561             :         
     562             :         /* 
     563             :          * Fixed types: ignore varlen
     564             :          * Other types: ignore varlen if <= 0, else varlen must be >= pnullrep->len.
     565             :          */
     566             :         switch (bindtype) {
     567          10 :         case DATETIMEBIND:
     568             :         case DATETIME2BIND:
     569             :         case DECIMALBIND:
     570             :         case SRCDECIMALBIND:
     571             :         case FLT8BIND:
     572             :         case INTBIND:
     573             :         case MONEYBIND:
     574             :         case NUMERICBIND:
     575             :         case SRCNUMERICBIND:
     576             :         case REALBIND:
     577             :         case SMALLBIND:
     578             :         case SMALLDATETIMEBIND:
     579             :         case SMALLMONEYBIND:
     580             :         case TINYBIND:
     581             :         case BIGINTBIND:
     582             :         case BITBIND:
     583             :         case TIMEBIND:
     584             :         case DATEBIND:
     585             :         case BIGDATETIMEBIND:
     586             :         case BIGTIMEBIND:
     587          10 :                 memcpy(varaddr, pnullrep->bindval, pnullrep->len);
     588          10 :                 return SUCCEED;
     589         410 :         case CHARBIND:
     590             :         case STRINGBIND:
     591             :         case NTBSTRINGBIND:
     592             :         case BINARYBIND:
     593             :         case VARYCHARBIND:
     594             :         case VARYBINBIND:
     595         410 :                 if (pnullrep->bindval && (varlen <= 0 || (size_t)varlen >= pnullrep->len)) {
     596         232 :                         memcpy(varaddr, pnullrep->bindval, pnullrep->len);
     597             :                 }
     598             :                 break;
     599           0 :         default:
     600           0 :                 dbperror(dbproc, SYBEBTYP, 0);
     601           0 :                 return FAIL;
     602             :         }
     603             : 
     604             :         /* 
     605             :          * For variable-length types, nonpositive varlen indicates 
     606             :          * buffer is "big enough" but also not to pad.
     607             :          * Apply terminator (if applicable) and go home.  
     608             :          */
     609         410 :         if (varlen <= 0) {
     610         258 :                 varlen = pnullrep->len;
     611         258 :                 switch (bindtype) {
     612         188 :                 case STRINGBIND:
     613             :                 case NTBSTRINGBIND:
     614         188 :                         ++varlen;
     615         188 :                         break;
     616             : #if 0
     617             :                 case BOUNDARYBIND:
     618             :                 case SENSITIVITYBIND:
     619             : #endif
     620             :                 }
     621             :         }
     622             :         
     623         410 :         if (varlen < (long)pnullrep->len) {
     624           0 :                 tdsdump_log(TDS_DBG_FUNC, "dbgetnull: error: not setting varaddr(%p) because %d < %lu\n",
     625             :                                         varaddr, varlen, (unsigned long int) pnullrep->len);
     626             :                 return FAIL;
     627             :         } 
     628             :                 
     629         410 :         tdsdump_log(TDS_DBG_FUNC, "varaddr(%p) varlen %d < %lu?\n",
     630             :                                 varaddr, varlen, (unsigned long int) pnullrep->len);
     631             : 
     632         410 :         assert(varlen >= 0);
     633             : 
     634             :         /*
     635             :          * CHARBIND             Empty string (padded with blanks)
     636             :          * STRINGBIND           Empty string (padded with blanks, null-terminated)
     637             :          * NTBSTRINGBIND        Empty string (unpadded, null-terminated)
     638             :          * BINARYBIND           Empty array (padded with zeros)
     639             :          */
     640         410 :         varaddr += pnullrep->len;
     641         410 :         varlen  -= (int)pnullrep->len;
     642         410 :         if (varlen > 0) {
     643         326 :                 switch (bindtype) {
     644          48 :                 case CHARBIND:
     645          48 :                         memset(varaddr, ' ', varlen);
     646          48 :                         break;
     647          40 :                 case STRINGBIND:
     648          40 :                         memset(varaddr, ' ', varlen);
     649          40 :                         varaddr[varlen-1] = '\0';
     650          40 :                         break;
     651         148 :                 case NTBSTRINGBIND:
     652         148 :                         varaddr[0] = '\0';
     653         148 :                         break;
     654          90 :                 case BINARYBIND:
     655          90 :                         memset(varaddr, 0, varlen);
     656          90 :                         break;
     657             :                 case VARYCHARBIND:
     658             :                 case VARYBINBIND:
     659             :                         break;
     660             :                 default:
     661           0 :                         assert(!"unknown bindtype");
     662             :                 }
     663             :         }       
     664             :         return SUCCEED;
     665             : }
     666             : 
     667             : /**
     668             :  * \ingroup dblib_core
     669             :  * \brief Initialize db-lib.  
     670             :  *
     671             :  * \remarks Call this function before trying to use db-lib in any way.  
     672             :  * Allocates various internal structures and reads \c locales.conf (if any) to determine the default
     673             :  * date format.  
     674             :  * \retval SUCCEED normal.  
     675             :  * \retval FAIL cannot allocate an array of \c TDS_MAX_CONN \c TDSSOCKET pointers.  
     676             :  */
     677             : RETCODE
     678         502 : dbinit(void)
     679             : {
     680         502 :         _dblib_err_handler = default_err_handler;
     681             : 
     682         502 :         tds_mutex_lock(&dblib_mutex);
     683             : 
     684         502 :         tdsdump_log(TDS_DBG_FUNC, "dbinit(void)\n");
     685             : 
     686         502 :         if (++g_dblib_ctx.ref_count != 1) {
     687           0 :                 tds_mutex_unlock(&dblib_mutex);
     688           0 :                 return SUCCEED;
     689             :         }
     690             : 
     691         502 :         if (dblib_get_tds_ctx() == NULL) {
     692           0 :                 tdsdump_log(TDS_DBG_FUNC, "dbinit: out of memory\n");
     693           0 :                 tds_mutex_unlock(&dblib_mutex);
     694           0 :                 return FAIL;
     695             :         }
     696             : 
     697             :         /* 
     698             :          * DBLIBCONTEXT stores a list of current connections so they may be closed with dbexit() 
     699             :          */
     700             : 
     701         502 :         g_dblib_ctx.connection_list = tds_new0(TDSSOCKET *, TDS_MAX_CONN);
     702         502 :         if (g_dblib_ctx.connection_list == NULL) {
     703           0 :                 tdsdump_log(TDS_DBG_FUNC, "dbinit: out of memory\n");
     704           0 :                 dblib_release_tds_ctx(1);
     705           0 :                 tds_mutex_unlock(&dblib_mutex);
     706           0 :                 return FAIL;
     707             :         }
     708         502 :         g_dblib_ctx.connection_list_size = TDS_MAX_CONN;
     709         502 :         g_dblib_ctx.connection_list_size_represented = TDS_MAX_CONN;
     710             : 
     711         502 :         g_dblib_ctx.login_timeout = -1;
     712         502 :         g_dblib_ctx.query_timeout = -1;
     713             : 
     714         502 :         tds_mutex_unlock(&dblib_mutex);
     715             : 
     716         502 :         return SUCCEED;
     717             : }
     718             : 
     719             : /**
     720             :  * \ingroup dblib_core
     721             :  * \brief Allocate a \c LOGINREC structure.  
     722             :  *
     723             :  * \remarks A \c LOGINREC structure is passed to \c dbopen() to create a connection to the database. 
     724             :  *      Does not communicate to the server; interacts strictly with library.  
     725             :  * \retval NULL the \c LOGINREC cannot be allocated.
     726             :  * \retval LOGINREC* to valid memory, otherwise.  
     727             :  */
     728             : LOGINREC *
     729         832 : dblogin(void)
     730             : {
     731             :         LOGINREC *loginrec;
     732             : 
     733         832 :         tdsdump_log(TDS_DBG_FUNC, "dblogin(void)\n");
     734             : 
     735         832 :         if ((loginrec = tds_new0(LOGINREC, 1)) == NULL) {
     736           0 :                 dbperror(NULL, SYBEMEM, errno);
     737           0 :                 return NULL;
     738             :         }
     739         832 :         if ((loginrec->tds_login = tds_alloc_login(true)) == NULL) {
     740           0 :                 dbperror(NULL, SYBEMEM, errno);
     741           0 :                 free(loginrec);
     742           0 :                 return NULL;
     743             :         }
     744             : 
     745             :         /* set default values for loginrec */
     746         832 :         if (!tds_set_library(loginrec->tds_login, "DB-Library")) {
     747           0 :                 dbperror(NULL, SYBEMEM, errno);
     748           0 :                 tds_free_login(loginrec->tds_login);
     749           0 :                 free(loginrec);
     750           0 :                 return NULL;
     751             :         }
     752             : 
     753             :         return loginrec;
     754             : }
     755             : 
     756             : /**
     757             :  * \ingroup dblib_core
     758             :  * \brief free the \c LOGINREC
     759             :  *
     760             :  */
     761             : void
     762         802 : dbloginfree(LOGINREC * login)
     763             : {
     764         802 :         tdsdump_log(TDS_DBG_FUNC, "dbloginfree(%p)\n", login);
     765             : 
     766         802 :         if (login) {
     767         802 :                 tds_free_login(login->tds_login);
     768         802 :                 free(login);
     769             :         }
     770         802 : }
     771             : 
     772             : /** \internal
     773             :  * \ingroup dblib_internal 
     774             :  * \brief Set the value of a string in a \c LOGINREC structure.  
     775             :  *
     776             :  * Called by various macros to populate \a login.  
     777             :  * \param login the \c LOGINREC* to modify.
     778             :  * \param value the value to set it to.  
     779             :  * \param which the field to set.  
     780             :  * \retval SUCCEED the value was set.
     781             :  * \retval FAIL \c DBSETHID or other invalid \a which was tried.  
     782             :  */
     783             : RETCODE
     784        2836 : dbsetlname(LOGINREC * login, const char *value, int which)
     785             : {
     786             :         bool copy_ret;
     787        2836 :         const char *value_nonull = value ? value : "";
     788             : 
     789        2836 :         tdsdump_log(TDS_DBG_FUNC, "dbsetlname(%p, %s, %d)\n", login, value, which);
     790             : 
     791        2836 :         if (login == NULL) {
     792           0 :                 dbperror(NULL, SYBEASNL, 0);
     793           0 :                 return FAIL;
     794             :         }
     795             : 
     796        2836 :         if (TDS_MAX_LOGIN_STR_SZ < strlen(value_nonull)) {
     797           0 :                 dbperror(NULL, SYBENTLL, 0);
     798           0 :                 return FAIL;
     799             :         }
     800             : 
     801        2836 :         switch (which) {
     802         330 :         case DBSETHOST:
     803         330 :                 copy_ret = tds_set_host(login->tds_login, value_nonull);
     804         330 :                 break;
     805         822 :         case DBSETUSER:
     806         822 :                 copy_ret = tds_set_user(login->tds_login, value_nonull);
     807         822 :                 break;
     808         822 :         case DBSETPWD:
     809         822 :                 copy_ret = tds_set_passwd(login->tds_login, value_nonull);
     810         822 :                 break;
     811         822 :         case DBSETAPP:
     812         822 :                 copy_ret = tds_set_app(login->tds_login, value_nonull);
     813         822 :                 break;
     814          10 :         case DBSETCHARSET:
     815             :                 /* TODO NULL == no conversion desired */
     816          10 :                 copy_ret = tds_set_client_charset(login->tds_login, value_nonull);
     817          10 :                 break;
     818           0 :         case DBSETNATLANG:
     819           0 :                 copy_ret = tds_set_language(login->tds_login, value_nonull);
     820           0 :                 break;
     821          30 :         case DBSETDBNAME:
     822          30 :                 copy_ret = !!tds_dstr_copy(&login->tds_login->database, value_nonull);
     823          30 :                 break;
     824           0 :         case DBSETSERVERPRINCIPAL:
     825           0 :                 copy_ret = !!tds_dstr_copy(&login->tds_login->server_spn, value_nonull);
     826           0 :                 break;
     827           0 :         case DBSETENCRYPTION:
     828           0 :                 copy_ret = tds_parse_conf_section(TDS_STR_ENCRYPTION, value_nonull, login->tds_login);
     829           0 :                 break;
     830           0 :         default:
     831           0 :                 dbperror(NULL, SYBEASUL, 0); /* Attempt to set unknown LOGINREC field */
     832           0 :                 return FAIL;
     833             :                 break;
     834             :         }
     835             : 
     836        2836 :         if (!copy_ret)
     837             :                 return FAIL;
     838        2836 :         return SUCCEED;
     839             : }
     840             : 
     841             : /** \internal
     842             :  * \ingroup dblib_internal
     843             :  * \brief Set an integer value in a \c LOGINREC structure.  
     844             :  *
     845             :  * Called by various macros to populate \a login.  
     846             :  * \param login the \c LOGINREC* to modify.
     847             :  * \param value the value to set it to.  
     848             :  * \param which the field to set.  
     849             :  * \retval SUCCEED the value was set.
     850             :  * \retval FAIL anything other than \c DBSETPACKET was passed for \a which.  
     851             :  */
     852             : RETCODE
     853          10 : dbsetllong(LOGINREC * login, long value, int which)
     854             : {
     855          10 :         tdsdump_log(TDS_DBG_FUNC, "dbsetllong(%p, %ld, %d)\n", login, value, which);
     856             : 
     857          10 :         if( login == NULL ) {
     858           0 :                 dbperror(NULL, SYBEASNL, 0);
     859           0 :                 return FAIL;
     860             :         }
     861             : 
     862          10 :         switch (which) {
     863          10 :         case DBSETPACKET:
     864          10 :                 if (0 <= value && value <= 999999) { 
     865           0 :                         tds_set_packet(login->tds_login, (int) value);
     866           0 :                         return SUCCEED;
     867             :                 }
     868          10 :                 dbperror(0, SYBEBADPK, 0, (int) value, (int) login->tds_login->block_size);
     869          10 :                 return FAIL;
     870             :                 break;
     871           0 :         default:
     872           0 :                 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetllong() which = %d\n", which);
     873             :                 return FAIL;
     874             :                 break;
     875             :         }
     876             : }
     877             : 
     878             : /** \internal
     879             :  * \ingroup dblib_internal
     880             :  * \brief Set an integer value in a \c LOGINREC structure.  
     881             :  *
     882             :  * Called by various macros to populate \a login.  
     883             :  * \param login the \c LOGINREC* to modify.
     884             :  * \param value the value to set it to.  
     885             :  * \param which the field to set.  
     886             :  * \retval SUCCEED the value was set.
     887             :  * \retval FAIL if invalid field in \a which or invalid \a value.  
     888             :  */
     889             : RETCODE
     890           0 : dbsetlshort(LOGINREC * login, int value, int which)
     891             : {
     892           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsetlshort(%p, %d, %d)\n", login, value, which);
     893             : 
     894           0 :         if( login == NULL ) {
     895           0 :                 dbperror(NULL, SYBEASNL, 0);
     896           0 :                 return FAIL;
     897             :         }
     898             : 
     899           0 :         switch (which) {
     900           0 :         case DBSETPORT:
     901           0 :                 tds_set_port(login->tds_login, value);
     902           0 :                 return SUCCEED;
     903             :         /* case DBSETHIER: */
     904           0 :         default:
     905           0 :                 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetlshort() which = %d\n", which);
     906             :                 return FAIL;
     907             :                 break;
     908             :         }
     909             : }
     910             : 
     911             : /** \internal
     912             :  * \ingroup dblib_internal
     913             :  * \brief Set a boolean value in a \c LOGINREC structure.  
     914             :  *
     915             :  * Called by various macros to populate \a login.  
     916             :  * \param login the \c LOGINREC* to modify.
     917             :  * \param value the value to set it to.  
     918             :  * \param which the field to set. 
     919             :  * \remark Only DBSETBCP is implemented.  
     920             :  * \retval SUCCEED the value was set.
     921             :  * \retval FAIL invalid value passed for \a which.  
     922             :  * \todo DBSETNOSHORT, DBSETENCRYPT, DBSETLABELED
     923             :  */
     924             : RETCODE
     925          50 : dbsetlbool(LOGINREC * login, int value, int which)
     926             : {
     927             :         bool b_value;
     928             : 
     929          50 :         tdsdump_log(TDS_DBG_FUNC, "dbsetlbool(%p, %d, %d)\n", login, value, which);
     930             : 
     931          50 :         if (login == NULL) {
     932           0 :                 dbperror(NULL, SYBEASNL, 0);
     933           0 :                 return FAIL;
     934             :         }
     935             : 
     936          50 :         b_value = (value != 0);
     937             : 
     938          50 :         switch (which) {
     939          50 :         case DBSETBCP:
     940          50 :                 tds_set_bulk(login->tds_login, b_value);
     941          50 :                 return SUCCEED;
     942           0 :         case DBSETUTF16:
     943           0 :                 login->tds_login->use_utf16 = b_value;
     944           0 :                 return SUCCEED;
     945           0 :         case DBSETNTLMV2:
     946           0 :                 login->tds_login->use_ntlmv2 = b_value;
     947           0 :                 login->tds_login->use_ntlmv2_specified = 1;
     948           0 :                 return SUCCEED;
     949           0 :         case DBSETREADONLY:
     950           0 :                 login->tds_login->readonly_intent = b_value;
     951           0 :                 return SUCCEED;
     952           0 :         case DBSETNETWORKAUTH:
     953           0 :                 login->network_auth = b_value;
     954           0 :                 return SUCCEED;
     955           0 :         case DBSETMUTUALAUTH:
     956           0 :                 login->tds_login->mutual_authentication = b_value;
     957           0 :                 return SUCCEED;
     958           0 :         case DBSETDELEGATION:
     959           0 :                 login->tds_login->gssapi_use_delegation = b_value;
     960           0 :                 return SUCCEED;
     961           0 :         case DBSETENCRYPT:
     962             :         case DBSETLABELED:
     963             :         default:
     964           0 :                 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetlbool() which = %d\n", which);
     965             :                 return FAIL;
     966             :                 break;
     967             :         }
     968             : }
     969             : 
     970             : /**
     971             :  * \ingroup dblib_core
     972             :  * \brief Set TDS version for future connections
     973             :  *
     974             :  */
     975             : RETCODE 
     976           0 : dbsetlversion(LOGINREC * login, BYTE version)
     977             : {
     978           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsetlversion(%p, %x)\n", login, version);
     979             : 
     980           0 :         if( login == NULL ) {
     981           0 :                 dbperror(NULL, SYBEASNL, 0);
     982           0 :                 return FAIL;
     983             :         }
     984             : 
     985           0 :         assert(login->tds_login != NULL);
     986             : 
     987           0 :         switch (version) {
     988           0 :         case DBVERSION_UNKNOWN:
     989           0 :                 tds_set_version(login->tds_login, 0, 0);
     990           0 :                 return SUCCEED;
     991           0 :         case DBVERSION_42:
     992           0 :                 tds_set_version(login->tds_login, 4, 2);
     993           0 :                 return SUCCEED;
     994           0 :         case DBVERSION_100:
     995           0 :                 tds_set_version(login->tds_login, 5, 0);
     996           0 :                 return SUCCEED;
     997           0 :         case DBVERSION_70:
     998           0 :                 tds_set_version(login->tds_login, 7, 0);
     999           0 :                 return SUCCEED;
    1000           0 :         case DBVERSION_71:
    1001           0 :                 tds_set_version(login->tds_login, 7, 1);
    1002           0 :                 return SUCCEED;
    1003           0 :         case DBVERSION_72:
    1004           0 :                 tds_set_version(login->tds_login, 7, 2);
    1005           0 :                 return SUCCEED;
    1006           0 :         case DBVERSION_73:
    1007           0 :                 tds_set_version(login->tds_login, 7, 3);
    1008           0 :                 return SUCCEED;
    1009           0 :         case DBVERSION_74:
    1010           0 :                 tds_set_version(login->tds_login, 7, 4);
    1011           0 :                 return SUCCEED;
    1012             :         }
    1013             : 
    1014             :         return FAIL;
    1015             : }
    1016             : 
    1017             : static void
    1018       36072 : dbstring_free(DBSTRING ** dbstrp)
    1019             : {
    1020             :         DBSTRING *curr, *next;
    1021             :         /* tdsdump_log(TDS_DBG_FUNC, "dbstring_free(%p)\n", dbstrp); */
    1022             : 
    1023       36072 :         if (!dbstrp)
    1024             :                 return;
    1025             : 
    1026       36072 :         curr = *dbstrp;
    1027       36072 :         *dbstrp = NULL;
    1028       77178 :         for (; curr; ) {
    1029        5034 :                 next = curr->strnext;
    1030        5034 :                 free(curr->strtext);
    1031        5034 :                 free(curr);
    1032        5034 :                 curr = next;
    1033             :         }
    1034             : }
    1035             : 
    1036             : static RETCODE
    1037        5214 : dbstring_concat(DBSTRING ** dbstrp, const char *p)
    1038             : {
    1039        5214 :         DBSTRING **strp = dbstrp;
    1040             : 
    1041             :         /* tdsdump_log(TDS_DBG_FUNC, "dbstring_concat(%p, %s)\n", *dbstrp, p); */
    1042             : 
    1043       10428 :         while (*strp != NULL) {
    1044           0 :                 strp = &((*strp)->strnext);
    1045             :         }
    1046        5214 :         if ((*strp = tds_new(DBSTRING, 1)) == NULL) {
    1047           0 :                 dbperror(NULL, SYBEMEM, errno);
    1048           0 :                 return FAIL;
    1049             :         }
    1050        5214 :         (*strp)->strtotlen = (DBINT)strlen(p);
    1051        5214 :         if (((*strp)->strtext = tds_new(BYTE, (*strp)->strtotlen)) == NULL) {
    1052           0 :                 TDS_ZERO_FREE(*strp);
    1053           0 :                 dbperror(NULL, SYBEMEM, errno);
    1054           0 :                 return FAIL;
    1055             :         }
    1056        5214 :         memcpy((*strp)->strtext, p, (*strp)->strtotlen);
    1057        5214 :         (*strp)->strnext = NULL;
    1058        5214 :         return SUCCEED;
    1059             : }
    1060             : 
    1061             : static RETCODE
    1062             : dbstring_assign(DBSTRING ** dbstrp, const char *p)
    1063             : {
    1064             :         /* tdsdump_log(TDS_DBG_FUNC, "dbstring_assign(%p, %s)\n", *dbstrp, p); */
    1065             : 
    1066        5214 :         dbstring_free(dbstrp);
    1067        5214 :         return dbstring_concat(dbstrp, p);
    1068             : }
    1069             : 
    1070             : static DBINT
    1071             : dbstring_length(DBSTRING * dbstr)
    1072             : {
    1073           0 :         DBINT len = 0;
    1074             :         DBSTRING *next;
    1075             : 
    1076             :         /* tdsdump_log(TDS_DBG_FUNC, "dbstring_length(%p)\n", dbstr); */
    1077             : 
    1078           0 :         for (next = dbstr; next != NULL; next = next->strnext) {
    1079           0 :                 len += next->strtotlen;
    1080             :         }
    1081             :         return len;
    1082             : }
    1083             : 
    1084             : static int
    1085             : dbstring_getchar(DBSTRING * dbstr, ptrdiff_t i)
    1086             : {
    1087             : 
    1088             :         /* tdsdump_log(TDS_DBG_FUNC, "dbstring_getchar(%p, %d)\n", dbstr, i); */
    1089             : 
    1090        6160 :         if (dbstr == NULL) {
    1091             :                 return -1;
    1092             :         }
    1093        4600 :         if (i < 0) {
    1094             :                 return -1;
    1095             :         }
    1096        4600 :         if (i < dbstr->strtotlen) {
    1097        3040 :                 return dbstr->strtext[i];
    1098             :         }
    1099        1560 :         return dbstring_getchar(dbstr->strnext, i - dbstr->strtotlen);
    1100             : }
    1101             : 
    1102             : static char *
    1103           0 : dbstring_get(DBSTRING * dbstr)
    1104             : {
    1105             :         DBSTRING *next;
    1106             :         int len;
    1107             :         char *ret;
    1108             :         char *cp;
    1109             : 
    1110             :         /* tdsdump_log(TDS_DBG_FUNC, "dbstring_get(%p)\n", dbstr); */
    1111             : 
    1112           0 :         if (dbstr == NULL) {
    1113             :                 return NULL;
    1114             :         }
    1115           0 :         len = dbstring_length(dbstr);
    1116           0 :         if ((ret = tds_new(char, len + 1)) == NULL) {
    1117           0 :                 dbperror(NULL, SYBEMEM, errno);
    1118           0 :                 return NULL;
    1119             :         }
    1120             :         cp = ret;
    1121           0 :         for (next = dbstr; next != NULL; next = next->strnext) {
    1122           0 :                 memcpy(cp, next->strtext, next->strtotlen);
    1123           0 :                 cp += next->strtotlen;
    1124             :         }
    1125           0 :         *cp = '\0';
    1126           0 :         return ret;
    1127             : }
    1128             : 
    1129             : static const char *const opttext[DBNUMOPTIONS] = {
    1130             :         "parseonly",
    1131             :         "estimate",
    1132             :         "showplan",
    1133             :         "noexec",
    1134             :         "arithignore",
    1135             :         "nocount",
    1136             :         "arithabort",
    1137             :         "textlimit",
    1138             :         "browse",
    1139             :         "offsets",
    1140             :         "statistics",
    1141             :         "errlvl",
    1142             :         "confirm",
    1143             :         "spid",
    1144             :         "buffer",
    1145             :         "noautofree",
    1146             :         "rowcount",
    1147             :         "textsize",
    1148             :         "language",
    1149             :         "dateformat",
    1150             :         "prpad",
    1151             :         "prcolsep",
    1152             :         "prlinelen",
    1153             :         "prlinesep",
    1154             :         "lfconvert",
    1155             :         "datefirst",
    1156             :         "chained",
    1157             :         "fipsflagger",
    1158             :         "transaction isolation level",
    1159             :         "auth",
    1160             :         "identity_insert",
    1161             :         "no_identity_column",
    1162             :         "cnv_date2char_short",
    1163             :         "client cursors",
    1164             :         "set time",
    1165             :         "quoted_identifier"
    1166             : };
    1167             : 
    1168             : static DBOPTION *
    1169         864 : init_dboptions(void)
    1170             : {
    1171             :         DBOPTION *dbopts;
    1172             :         int i;
    1173             : 
    1174         864 :         if ((dbopts = tds_new0(DBOPTION, DBNUMOPTIONS)) == NULL) {
    1175           0 :                 dbperror(NULL, SYBEMEM, errno);
    1176           0 :                 return NULL;
    1177             :         }
    1178       31104 :         for (i = 0; i < DBNUMOPTIONS; i++) {
    1179       31104 :                 dbopts[i].text = opttext[i];
    1180       31104 :                 dbopts[i].param = NULL;
    1181       31104 :                 dbopts[i].factive = FALSE;
    1182             :         }
    1183        1728 :         dbstring_assign(&(dbopts[DBPRPAD].param), " ");
    1184        1728 :         dbstring_assign(&(dbopts[DBPRCOLSEP].param), " ");
    1185        1728 :         dbstring_assign(&(dbopts[DBPRLINELEN].param), "80");
    1186        1728 :         dbstring_assign(&(dbopts[DBPRLINESEP].param), "\n");
    1187        1728 :         dbstring_assign(&(dbopts[DBCLIENTCURSORS].param), " ");
    1188        1728 :         dbstring_assign(&(dbopts[DBSETTIME].param), " ");
    1189         864 :         return dbopts;
    1190             : }
    1191             : 
    1192             : /** \internal
    1193             :  * \ingroup dblib_internal
    1194             :  * \brief Form a connection with the server.
    1195             :  *   
    1196             :  * Called by the \c dbopen() macro, normally.  If FreeTDS was configured with \c --enable-msdblib, this
    1197             :  * function is called by (exported) \c dbopen() function.  \c tdsdbopen is so-named to avoid
    1198             :  * namespace conflicts with other database libraries that use the same function name.  
    1199             :  * \param login \c LOGINREC* carrying the account information.
    1200             :  * \param server name of the dataserver to connect to.  
    1201             :  * \return valid pointer on successful login.  
    1202             :  * \retval NULL insufficient memory, unable to connect for any reason.
    1203             :  * \sa dbopen()
    1204             :  * \todo use \c asprintf() to avoid buffer overflow.
    1205             :  * \todo separate error messages for \em no-such-server and \em no-such-user. 
    1206             :  */
    1207             : DBPROCESS *
    1208         864 : tdsdbopen(LOGINREC * login, const char *server, int msdblib)
    1209             : {
    1210         864 :         DBPROCESS *dbproc = NULL;
    1211         864 :         TDSCONTEXT *tds_ctx = NULL;
    1212             :         TDSLOGIN *connection;
    1213             :         int add_connection_res;
    1214             : 
    1215         864 :         tds_dir_char *tdsdump = tds_dir_getenv(TDS_DIR("TDSDUMP"));
    1216         864 :         if (tdsdump && *tdsdump) {
    1217           0 :                 tdsdump_topen(tdsdump);
    1218           0 :                 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen(%p, %s, [%s])\n", login, server? server : "0x0", msdblib? "microsoft" : "sybase");
    1219             :         }
    1220             : 
    1221             :         /*
    1222             :          * Sybase supports the DSQUERY environment variable and falls back to "SYBASE" if server is NULL. 
    1223             :          * Microsoft uses a NULL or "" server to indicate a local server.  
    1224             :          * FIXME: support local server for win32.  
    1225             :          */
    1226         864 :         if (!server && !msdblib) {
    1227           0 :                 if ((server = getenv("TDSQUERY")) == NULL)
    1228           0 :                         if ((server = getenv("DSQUERY")) == NULL)
    1229           0 :                                 server = "SYBASE";
    1230           0 :                 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: servername set to %s\n", server);
    1231             :         }
    1232             : 
    1233         864 :         if ((dbproc = tds_new0(DBPROCESS, 1)) == NULL)
    1234             :                 goto memory_error;
    1235         864 :         dbproc->msdblib = msdblib;
    1236             : 
    1237         864 :         dbproc->dbopts = init_dboptions();
    1238         864 :         if (dbproc->dbopts == NULL)
    1239             :                 goto cleanup;
    1240         864 :         tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: dbproc->dbopts = %p\n", dbproc->dbopts);
    1241             : 
    1242         864 :         dbproc->dboptcmd = NULL;
    1243         864 :         dbproc->avail_flag = TRUE;
    1244         864 :         dbproc->command_state = DBCMDNONE;
    1245             : 
    1246         864 :         if (!tds_set_server(login->tds_login, server))
    1247             :                 goto memory_error;
    1248         864 :         tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: tds_set_server(%p, \"%s\")\n", login->tds_login, server);
    1249             : 
    1250         864 :         tds_mutex_lock(&dblib_mutex);
    1251         864 :         tds_ctx = dblib_get_tds_ctx();
    1252         864 :         tds_mutex_unlock(&dblib_mutex);
    1253         864 :         if (!tds_ctx)
    1254             :                 goto memory_error;
    1255             : 
    1256         864 :         dbproc->tds_socket = tds_alloc_socket(tds_ctx, 512);
    1257         864 :         if (dbproc->tds_socket == NULL)
    1258             :                 goto memory_error;
    1259         864 :         tds_ctx = NULL;
    1260             : 
    1261         864 :         tds_set_parent(dbproc->tds_socket, dbproc);
    1262             : 
    1263         864 :         dbproc->tds_socket->env_chg_func = db_env_chg;
    1264         864 :         dbproc->envchange_rcv = 0;
    1265             : 
    1266         864 :         dbproc->dbcurdb[0] = '\0';
    1267         864 :         dbproc->servcharset[0] = '\0';
    1268             : 
    1269         864 :         tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: About to call tds_read_config_info...\n");
    1270             : 
    1271         864 :         tds_mutex_lock(&dblib_mutex);
    1272         864 :         add_connection_res = dblib_add_connection(&g_dblib_ctx, dbproc->tds_socket);
    1273         864 :         tds_mutex_unlock(&dblib_mutex);
    1274         864 :         if (add_connection_res) {
    1275          10 :                 dbperror(dbproc, SYBEDBPS, 0);
    1276          10 :                 dbclose(dbproc);
    1277          10 :                 return NULL;
    1278             :         }
    1279             : 
    1280         854 :         connection = tds_read_config_info(dbproc->tds_socket, login->tds_login, g_dblib_ctx.tds_ctx->locale);
    1281         854 :         if (!connection) {
    1282           0 :                 dbclose(dbproc);
    1283           0 :                 return NULL;
    1284             :         }
    1285         854 :         connection->option_flag2 &= ~TDS_ODBC_ON;        /* we're not an ODBC driver */
    1286         854 :         tds_fix_login(connection);              /* initialize from Environment variables */
    1287             : 
    1288         854 :         dbproc->chkintr = NULL;
    1289         854 :         dbproc->hndlintr = NULL;
    1290             : 
    1291         854 :         tds_mutex_lock(&dblib_mutex);
    1292             : 
    1293             :         /* override connection timeout if dbsetlogintime() was called */
    1294         854 :         if (g_dblib_ctx.login_timeout > 0) {
    1295          20 :                 connection->connect_timeout = g_dblib_ctx.login_timeout;
    1296             :         }
    1297             : 
    1298             :         /* override query timeout if dbsettime() was called */
    1299         854 :         if (g_dblib_ctx.query_timeout > 0) {
    1300          10 :                 connection->query_timeout = g_dblib_ctx.query_timeout;
    1301             :         }
    1302             : 
    1303         854 :         tds_mutex_unlock(&dblib_mutex);
    1304             : 
    1305         854 :         tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: Calling tds_connect_and_login(%p, %p)\n",
    1306             :                 dbproc->tds_socket, connection);
    1307             : 
    1308         854 :         if (login->network_auth) {
    1309           0 :                 tds_dstr_empty(&connection->user_name);
    1310           0 :                 tds_dstr_empty(&connection->password);
    1311             :         }
    1312             : 
    1313         854 :         if (TDS_FAILED(tds_connect_and_login(dbproc->tds_socket, connection))) {
    1314           0 :                 tdsdump_log(TDS_DBG_ERROR, "tdsdbopen: tds_connect_and_login failed for \"%s\"!\n",
    1315             :                         tds_dstr_cstr(&connection->server_name));
    1316           0 :                 tds_free_login(connection);
    1317           0 :                 dbclose(dbproc);
    1318           0 :                 return NULL;
    1319             :         }
    1320         854 :         tds_free_login(connection);
    1321             : 
    1322         854 :         dbproc->dbbuf = NULL;
    1323         854 :         dbproc->dbbufsz = 0;
    1324             : 
    1325             :         /* set the DBBUFFER capacity to nil */
    1326         854 :         buffer_set_capacity(dbproc, 0);
    1327             : 
    1328         854 :         memcpy(dbproc->nullreps, default_null_representations, sizeof(default_null_representations));
    1329             : 
    1330         854 :         tds_mutex_lock(&dblib_mutex);
    1331             : 
    1332         854 :         if (g_dblib_ctx.recftos_filename != NULL) {
    1333           0 :                 char *temp_filename = NULL;
    1334           0 :                 const int len = asprintf(&temp_filename, "%s.%d", 
    1335             :                                          g_dblib_ctx.recftos_filename, g_dblib_ctx.recftos_filenum);
    1336           0 :                 if (len >= 0) {
    1337           0 :                         dbproc->ftos = fopen(temp_filename, "w");
    1338           0 :                         free(temp_filename);
    1339           0 :                         if (dbproc->ftos != NULL) {
    1340             :                                 char timestr[256];
    1341           0 :                                 fprintf(dbproc->ftos, "/* dbopen() at %s */\n", _dbprdate(timestr));
    1342           0 :                                 fflush(dbproc->ftos);
    1343           0 :                                 g_dblib_ctx.recftos_filenum++;
    1344             :                         }
    1345             :                 }
    1346             :         }
    1347             :         
    1348         854 :         tds_mutex_unlock(&dblib_mutex);
    1349             : 
    1350         854 :         tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: Returning dbproc = %p\n", dbproc);
    1351             : 
    1352             :         return dbproc;
    1353             : 
    1354           0 :       memory_error:
    1355           0 :         dbperror(NULL, SYBEMEM, errno);
    1356             : 
    1357           0 :       cleanup:
    1358           0 :         if (tds_ctx) {
    1359           0 :                 tds_mutex_lock(&dblib_mutex);
    1360           0 :                 dblib_release_tds_ctx(1);
    1361           0 :                 tds_mutex_unlock(&dblib_mutex);
    1362             :         }
    1363           0 :         if (dbproc) {
    1364           0 :                 free(dbproc->dbopts);
    1365           0 :                 free(dbproc);
    1366             :         }
    1367             :         return NULL;
    1368             : }
    1369             : 
    1370             : /**
    1371             :  * \ingroup dblib_core
    1372             :  * \brief \c printf-like way to form SQL to send to the server.  
    1373             :  *
    1374             :  * Forms a command string and writes to the command buffer with dbcmd().  
    1375             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1376             :  * \param fmt <tt> man vasprintf</tt> for details.  
    1377             :  * \retval SUCCEED success.
    1378             :  * \retval FAIL insufficient memory, or dbcmd() failed.
    1379             :  * \sa dbcmd(), dbfreebuf(), dbgetchar(), dbopen(), dbstrcpy(), dbstrlen().
    1380             :  */
    1381             : RETCODE
    1382         860 : dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
    1383             : {
    1384             :         va_list ap;
    1385             :         char *s;
    1386             :         int len;
    1387             :         RETCODE ret;
    1388             : 
    1389         860 :         tdsdump_log(TDS_DBG_FUNC, "dbfcmd(%p, %s, ...)\n", dbproc, fmt);
    1390         860 :         CHECK_CONN(FAIL);
    1391         860 :         CHECK_NULP(fmt, "dbfcmd", 2, FAIL);
    1392             :         
    1393         860 :         va_start(ap, fmt);
    1394         860 :         len = vasprintf(&s, fmt, ap);
    1395         860 :         va_end(ap);
    1396             : 
    1397         860 :         if (len < 0) {
    1398           0 :                 dbperror(dbproc, SYBEMEM, errno);
    1399           0 :                 return FAIL;
    1400             :         }
    1401             : 
    1402         860 :         ret = dbcmd(dbproc, s);
    1403         860 :         free(s);
    1404             : 
    1405         860 :         return ret;
    1406             : }
    1407             : 
    1408             : /**
    1409             :  * \ingroup dblib_core
    1410             :  * \brief \c Append SQL to the command buffer.  
    1411             :  *
    1412             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1413             :  * \param cmdstring SQL to append to the command buffer.  
    1414             :  * \retval SUCCEED success.
    1415             :  * \retval FAIL insufficient memory.  
    1416             :  * \remarks set command state to \c  DBCMDPEND unless the command state is DBCMDSENT, in which case 
    1417             :  * it frees the command buffer.  This latter may or may not be the Right Thing to do.  
    1418             :  * \sa dbfcmd(), dbfreebuf(), dbgetchar(), dbopen(), dbstrcpy(), dbstrlen().
    1419             :  */
    1420             : RETCODE
    1421       25248 : dbcmd(DBPROCESS * dbproc, const char cmdstring[])
    1422             : {
    1423             :         size_t cmd_len, buf_len, newsz;
    1424             : 
    1425       25248 :         tdsdump_log(TDS_DBG_FUNC, "dbcmd(%p, %s)\n", dbproc, cmdstring);
    1426       25248 :         CHECK_CONN(FAIL);
    1427       25248 :         CHECK_NULP(cmdstring, "dbcmd", 2, FAIL);
    1428             : 
    1429       25248 :         dbproc->avail_flag = FALSE;
    1430             : 
    1431       25248 :         tdsdump_log(TDS_DBG_FUNC, "dbcmd() bufsz = %d\n", dbproc->dbbufsz);
    1432             : 
    1433       25248 :         if (dbproc->command_state == DBCMDSENT) {
    1434       19890 :                 if (!dbproc->noautofree) {
    1435       19890 :                         dbfreebuf(dbproc);
    1436             :                 }
    1437             :         }
    1438             : 
    1439       25248 :         buf_len = (dbproc->dbbufsz == 0) ? 0 : dbproc->dbbufsz - 1;
    1440       25248 :         cmd_len = strlen(cmdstring);
    1441       25248 :         newsz = buf_len + cmd_len + 1;
    1442       25248 :         if (newsz > 0x7fffffffu || !TDS_RESIZE(dbproc->dbbuf, newsz)) {
    1443           0 :                 dbperror(dbproc, SYBEMEM, errno);
    1444           0 :                 return FAIL;
    1445             :         }
    1446       25248 :         memcpy(dbproc->dbbuf + buf_len, cmdstring, cmd_len);
    1447       25248 :         dbproc->dbbuf[newsz - 1] = 0;
    1448       25248 :         dbproc->dbbufsz = (int) newsz;
    1449             : 
    1450       25248 :         dbproc->command_state = DBCMDPEND;
    1451             : 
    1452       25248 :         return SUCCEED;
    1453             : }
    1454             : 
    1455             : /**
    1456             :  * \ingroup dblib_core
    1457             :  * \brief send the SQL command to the server and wait for an answer.  
    1458             :  * 
    1459             :  * Please be patient.  This function waits for the server to respond.   \c dbsqlexec is equivalent
    1460             :  * to dbsqlsend() followed by dbsqlok(). 
    1461             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1462             :  * \retval SUCCEED query was processed without errors.
    1463             :  * \retval FAIL was returned by dbsqlsend() or dbsqlok().
    1464             :  * \sa dbcmd(), dbfcmd(), dbnextrow(), dbresults(), dbretstatus(), dbsettime(), dbsqlok(), dbsqlsend()
    1465             :  */
    1466             : RETCODE
    1467       20434 : dbsqlexec(DBPROCESS * dbproc)
    1468             : {
    1469       20434 :         RETCODE rc = FAIL;
    1470             : 
    1471       20434 :         tdsdump_log(TDS_DBG_FUNC, "dbsqlexec(%p)\n", dbproc);
    1472       20434 :         CHECK_CONN(FAIL);
    1473             : 
    1474       20434 :         if (SUCCEED == (rc = dbsqlsend(dbproc))) {
    1475       20354 :                 rc = dbsqlok(dbproc);
    1476             :         }
    1477             :         return rc;
    1478             : }
    1479             : 
    1480             : /**
    1481             :  * \ingroup dblib_core
    1482             :  * \brief Change current database. 
    1483             :  * 
    1484             :  * Analagous to the unix command \c cd, dbuse() makes \a name the default database.  Waits for an answer
    1485             :  * from the server.  
    1486             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1487             :  * \param name database to use.
    1488             :  * \retval SUCCEED query was processed without errors.
    1489             :  * \retval FAIL query was not processed
    1490             :  * \sa dbchange(), dbname().
    1491             :  */
    1492             : RETCODE
    1493         804 : dbuse(DBPROCESS * dbproc, const char *name)
    1494             : {
    1495             :         RETCODE rc;
    1496             :         char *query;
    1497             : 
    1498         804 :         tdsdump_log(TDS_DBG_FUNC, "dbuse(%p, %s)\n", dbproc, name);
    1499         804 :         CHECK_CONN(FAIL);
    1500         804 :         CHECK_NULP(name, "dbuse", 2, FAIL);
    1501             : 
    1502             :         /* quote name */
    1503         804 :         query = tds_new(char, tds_quote_id(dbproc->tds_socket, NULL, name, -1) + 6);
    1504         804 :         if (!query) {
    1505           0 :                 dbperror(dbproc, SYBEMEM, errno);
    1506           0 :                 return FAIL;
    1507             :         }
    1508         804 :         strcpy(query, "use ");
    1509             :         /* TODO PHP suggest to quote by yourself with []... what should I do ?? quote or not ?? */
    1510         804 :         if (name[0] == '[' && name[strlen(name)-1] == ']')
    1511           0 :                 strcat(query, name);
    1512             :         else
    1513         804 :                 tds_quote_id(dbproc->tds_socket, query + 4, name, -1);
    1514             : 
    1515         804 :         rc = SUCCEED;
    1516         804 :         if ((dbcmd(dbproc, query) == FAIL)
    1517         804 :             || (dbsqlexec(dbproc) == FAIL)
    1518         804 :             || (dbresults(dbproc) == FAIL)
    1519         804 :             || (dbcanquery(dbproc) == FAIL))
    1520             :                 rc = FAIL;
    1521         804 :         free(query);
    1522         804 :         return rc;
    1523             : }
    1524             : 
    1525             : /**
    1526             :  * \ingroup dblib_core
    1527             :  * \brief Close a connection to the server and free associated resources.  
    1528             :  * 
    1529             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1530             :  * \sa dbexit(), dbopen().
    1531             :  */
    1532             : void
    1533         834 : dbclose(DBPROCESS * dbproc)
    1534             : {
    1535             :         TDSSOCKET *tds;
    1536             :         int i;
    1537             :         char timestr[256];
    1538             : 
    1539         834 :         tdsdump_log(TDS_DBG_FUNC, "dbclose(%p)\n", dbproc);   
    1540         834 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    1541             : 
    1542         834 :         tds = dbproc->tds_socket;
    1543         834 :         if (tds) {
    1544             :                 /* 
    1545             :                  * this MUST be done before socket destruction
    1546             :                  * it is possible that a TDSSOCKET is allocated on same position
    1547             :                  */
    1548         456 :                 tds_mutex_lock(&dblib_mutex);
    1549         456 :                 dblib_del_connection(&g_dblib_ctx, dbproc->tds_socket);
    1550         456 :                 tds_mutex_unlock(&dblib_mutex);
    1551             : 
    1552         456 :                 tds_close_socket(tds);
    1553         456 :                 tds_free_socket(tds);
    1554             : 
    1555         456 :                 tds_mutex_lock(&dblib_mutex);
    1556         456 :                 dblib_release_tds_ctx(1);
    1557         456 :                 tds_mutex_unlock(&dblib_mutex);
    1558             :         }
    1559         834 :         buffer_free(&(dbproc->row_buf));
    1560             : 
    1561         834 :         if (dbproc->ftos != NULL) {
    1562           0 :                 fprintf(dbproc->ftos, "/* dbclose() at %s */\n", _dbprdate(timestr));
    1563           0 :                 fclose(dbproc->ftos);
    1564             :         }
    1565             : 
    1566         834 :         tds_free_bcpinfo(dbproc->bcpinfo);
    1567         834 :         if (dbproc->hostfileinfo) {
    1568           0 :                 free(dbproc->hostfileinfo->hostfile);
    1569           0 :                 free(dbproc->hostfileinfo->errorfile);
    1570           0 :                 if (dbproc->hostfileinfo->host_columns) {
    1571           0 :                         for (i = 0; i < dbproc->hostfileinfo->host_colcount; i++) {
    1572           0 :                                 free(dbproc->hostfileinfo->host_columns[i]->terminator);
    1573           0 :                                 free(dbproc->hostfileinfo->host_columns[i]);
    1574             :                         }
    1575           0 :                         free(dbproc->hostfileinfo->host_columns);
    1576             :                 }
    1577             :         }
    1578             : 
    1579       30024 :         for (i = 0; i < DBNUMOPTIONS; i++) {
    1580       30024 :                 dbstring_free(&(dbproc->dbopts[i].param));
    1581             :         }
    1582         834 :         free(dbproc->dbopts);
    1583             : 
    1584         834 :         dbstring_free(&(dbproc->dboptcmd));
    1585             : 
    1586       27522 :         for (i=0; i < MAXBINDTYPES; i++) {
    1587       26688 :                 if (dbproc->nullreps[i].bindval != default_null_representations[i].bindval)
    1588         250 :                         free((BYTE*)dbproc->nullreps[i].bindval);
    1589             :         }
    1590             : 
    1591         834 :         dbfreebuf(dbproc);
    1592         834 :         free(dbproc);
    1593             : }
    1594             : 
    1595             : /**
    1596             :  * \ingroup dblib_core
    1597             :  * \brief Close server connections and free all related structures.  
    1598             :  * 
    1599             :  * \sa dbclose(), dbinit(), dbopen().
    1600             :  * \todo breaks if ctlib/dblib used in same process.
    1601             :  */
    1602             : void
    1603         472 : dbexit(void)
    1604             : {
    1605             :         TDSSOCKET *tds;
    1606             :         DBPROCESS *dbproc;
    1607         472 :         int i, list_size, count = 1;
    1608             : 
    1609         472 :         tdsdump_log(TDS_DBG_FUNC, "dbexit(void)\n");
    1610             : 
    1611         472 :         tds_mutex_lock(&dblib_mutex);
    1612             : 
    1613         472 :         if (--g_dblib_ctx.ref_count != 0) {
    1614           0 :                 tds_mutex_unlock(&dblib_mutex);
    1615           0 :                 return;
    1616             :         }
    1617             : 
    1618         472 :         list_size = g_dblib_ctx.connection_list_size;
    1619             : 
    1620     1933784 :         for (i = 0; i < list_size; i++) {
    1621     1933312 :                 tds = g_dblib_ctx.connection_list[i];
    1622     1933312 :                 g_dblib_ctx.connection_list[i] = NULL;
    1623     1933312 :                 if (tds) {
    1624         378 :                         ++count;
    1625         378 :                         dbproc = (DBPROCESS *) tds_get_parent(tds);
    1626         378 :                         tds_close_socket(tds);
    1627         378 :                         tds_free_socket(tds);
    1628         378 :                         if (dbproc) {
    1629             :                                 /* avoid locking in dbclose */
    1630         378 :                                 dbproc->tds_socket = NULL;
    1631         378 :                                 dbclose(dbproc);
    1632             :                         }
    1633             :                 }
    1634             :         }
    1635         472 :         if (g_dblib_ctx.connection_list) {
    1636         472 :                 TDS_ZERO_FREE(g_dblib_ctx.connection_list);
    1637         472 :                 g_dblib_ctx.connection_list_size = 0;
    1638         472 :                 g_dblib_ctx.connection_list_size_represented = 0;
    1639             :         }
    1640             : 
    1641         472 :         dblib_release_tds_ctx(count);
    1642             : 
    1643         472 :         tds_mutex_unlock(&dblib_mutex);
    1644             : }
    1645             : 
    1646             : static const char *
    1647             : prdbresults_state(int retcode)
    1648             : {
    1649           0 :         switch(retcode) {
    1650             :         case _DB_RES_INIT:              return "_DB_RES_INIT";
    1651           0 :         case _DB_RES_RESULTSET_EMPTY:   return "_DB_RES_RESULTSET_EMPTY";
    1652           0 :         case _DB_RES_RESULTSET_ROWS:    return "_DB_RES_RESULTSET_ROWS";
    1653           0 :         case _DB_RES_NEXT_RESULT:       return "_DB_RES_NEXT_RESULT";
    1654           0 :         case _DB_RES_NO_MORE_RESULTS:   return "_DB_RES_NO_MORE_RESULTS";
    1655           0 :         case _DB_RES_SUCCEED:           return "_DB_RES_SUCCEED";
    1656             :         default: break;
    1657             :         }
    1658             :         return "??";
    1659             : }
    1660             : 
    1661             : static const char *
    1662             : prdbretcode(RETCODE retcode)
    1663             : {
    1664           0 :         switch(retcode) {
    1665             :         case REG_ROW:           return "REG_ROW/MORE_ROWS";
    1666           0 :         case NO_MORE_ROWS:      return "NO_MORE_ROWS";
    1667           0 :         case BUF_FULL:          return "BUF_FULL";
    1668           0 :         case NO_MORE_RESULTS:   return "NO_MORE_RESULTS";
    1669           0 :         case SUCCEED:           return "SUCCEED";
    1670           0 :         case FAIL:              return "FAIL";
    1671             :         default: break;
    1672             :         }
    1673             :         return "??";
    1674             : }
    1675             : 
    1676             : static const char *
    1677             : prretcode(int retcode)
    1678             : {
    1679           0 :         switch(retcode) {
    1680             :         case TDS_SUCCESS:               return "TDS_SUCCESS";
    1681           0 :         case TDS_FAIL:                  return "TDS_FAIL";
    1682           0 :         case TDS_NO_MORE_RESULTS:       return "TDS_NO_MORE_RESULTS";
    1683           0 :         case TDS_CANCELLED:             return "TDS_CANCELLED";
    1684             :         default: break;
    1685             :         }
    1686             :         return "??";
    1687             : }
    1688             : 
    1689             : static const char *
    1690           0 : prresult_type(int result_type)
    1691             : {
    1692           0 :         switch(result_type) {
    1693             :         case TDS_ROW_RESULT:            return "TDS_ROW_RESULT";
    1694           0 :         case TDS_PARAM_RESULT:          return "TDS_PARAM_RESULT";
    1695           0 :         case TDS_STATUS_RESULT:         return "TDS_STATUS_RESULT";
    1696           0 :         case TDS_MSG_RESULT:            return "TDS_MSG_RESULT";
    1697           0 :         case TDS_COMPUTE_RESULT:        return "TDS_COMPUTE_RESULT";
    1698           0 :         case TDS_CMD_DONE:              return "TDS_CMD_DONE";
    1699           0 :         case TDS_CMD_SUCCEED:           return "TDS_CMD_SUCCEED";
    1700           0 :         case TDS_CMD_FAIL:              return "TDS_CMD_FAIL";
    1701           0 :         case TDS_ROWFMT_RESULT:         return "TDS_ROWFMT_RESULT";
    1702           0 :         case TDS_COMPUTEFMT_RESULT:     return "TDS_COMPUTEFMT_RESULT";
    1703           0 :         case TDS_DESCRIBE_RESULT:       return "TDS_DESCRIBE_RESULT";
    1704           0 :         case TDS_DONE_RESULT:           return "TDS_DONE_RESULT";
    1705           0 :         case TDS_DONEPROC_RESULT:       return "TDS_DONEPROC_RESULT";
    1706           0 :         case TDS_DONEINPROC_RESULT:     return "TDS_DONEINPROC_RESULT";
    1707           0 :         case TDS_OTHERS_RESULT:         return "TDS_OTHERS_RESULT";
    1708             :         default: break;
    1709             :         }
    1710           0 :         return "??";
    1711             : }
    1712             : 
    1713             : /**
    1714             :  * \ingroup dblib_core
    1715             :  * \brief Set up query results.  
    1716             :  *
    1717             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1718             :  * \retval SUCCEED Some results are available.
    1719             :  * \retval FAIL query was not processed successfully by the server
    1720             :  * \retval NO_MORE_RESULTS query produced no results. 
    1721             :  *
    1722             :  * \remarks Call dbresults() after calling dbsqlexec() or dbsqlok(), or dbrpcsend() returns SUCCEED.  Unless
    1723             :  *      one of them fails, dbresults will return either SUCCEED or NO_MORE_RESULTS.  
    1724             :  *
    1725             :  *      The meaning of \em results is very specific and not very intuitive.  Results are created by either
    1726             :  *      - a SELECT statement
    1727             :  *      - a stored procedure
    1728             :  *
    1729             :  *      When dbresults returns SUCCEED, therefore, it indicates the server processed the query successfully and 
    1730             :  *      that one or more of these is present:
    1731             :  *      - metadata -- dbnumcols() returns 1 or more
    1732             :  *      - data -- dbnextrow() returns SUCCEED
    1733             :  *      - return status -- dbhasretstat() returns TRUE
    1734             :  *      - output parameters -- dbnumrets() returns 1 or more
    1735             :  *
    1736             :  *      If none of the above are present, dbresults() returns NO_MORE_RESULTS.  
    1737             :  *      
    1738             :  *      SUCCEED does not imply that DBROWS() will return TRUE or even that dbnumcols() will return nonzero.  
    1739             :  *      A general algorithm for reading results will call dbresults() until it return NO_MORE_RESULTS (or FAIL).  
    1740             :  *      An application should check for all the above kinds of results within the dbresults() loop.  
    1741             :  * 
    1742             :  * \sa dbsqlexec(), dbsqlok(), dbrpcsend(), dbcancel(), DBROWS(), dbnextrow(), dbnumcols(), dbhasretstat(), dbretstatus(), dbnumrets()
    1743             :  */
    1744             : RETCODE
    1745       29835 : dbresults(DBPROCESS * dbproc)
    1746             : {
    1747       29835 :         RETCODE erc = _dbresults(dbproc);
    1748             : 
    1749       29835 :         tdsdump_log(TDS_DBG_FUNC, "dbresults returning %d (%s)\n", erc, prdbretcode(erc));
    1750       29835 :         return erc;
    1751             : }
    1752             : 
    1753             : static RETCODE
    1754       29835 : _dbresults(DBPROCESS * dbproc)
    1755             : {
    1756             :         TDSSOCKET *tds;
    1757       29835 :         int result_type = 0, done_flags;
    1758             : 
    1759       29835 :         tdsdump_log(TDS_DBG_FUNC, "dbresults(%p)\n", dbproc);
    1760       29835 :         CHECK_CONN(FAIL);
    1761             : 
    1762       29835 :         tds = dbproc->tds_socket;
    1763             : 
    1764       29835 :         tdsdump_log(TDS_DBG_FUNC, "dbresults: dbresults_state is %d (%s)\n", 
    1765             :                                         dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
    1766       29835 :         switch ( dbproc->dbresults_state ) {
    1767        8170 :         case _DB_RES_SUCCEED:
    1768        8170 :                 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    1769        8170 :                 return SUCCEED;
    1770             :                 break;
    1771           0 :         case _DB_RES_RESULTSET_ROWS:
    1772           0 :                 dbperror(dbproc, SYBERPND, 0); /* dbresults called while rows outstanding.... */
    1773           0 :                 return FAIL;
    1774             :                 break;
    1775             :         case _DB_RES_NO_MORE_RESULTS:
    1776             :                 return NO_MORE_RESULTS;
    1777             :                 break;
    1778             :         default:
    1779             :                 break;
    1780             :         }
    1781             : 
    1782             :         for (;;) {
    1783       31271 :                 TDSRET retcode = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS);
    1784             : 
    1785       31271 :                 tdsdump_log(TDS_DBG_FUNC, "dbresults() tds_process_tokens returned %d (%s),\n\t\t\tresult_type %d (%s)\n",
    1786             :                                                 retcode, prretcode(retcode), result_type, prresult_type(result_type));
    1787             : 
    1788       31271 :                 switch (retcode) {
    1789             : 
    1790       22863 :                 case TDS_SUCCESS:
    1791             : 
    1792       22863 :                         switch (result_type) {
    1793             :         
    1794         298 :                         case TDS_ROWFMT_RESULT:
    1795         298 :                                 buffer_free(&dbproc->row_buf);
    1796         298 :                                 buffer_alloc(dbproc);
    1797         298 :                                 dbproc->dbresults_state = _DB_RES_RESULTSET_EMPTY;
    1798         298 :                                 break;
    1799             :         
    1800             :                         case TDS_COMPUTEFMT_RESULT:
    1801             :                                 break;
    1802             :         
    1803       12080 :                         case TDS_ROW_RESULT:
    1804             :                         case TDS_COMPUTE_RESULT:
    1805             :         
    1806       12080 :                                 dbproc->dbresults_state = _DB_RES_RESULTSET_ROWS;
    1807       12080 :                                 return SUCCEED;
    1808             :                                 break;
    1809             :         
    1810        1177 :                         case TDS_DONE_RESULT:
    1811             :                         case TDS_DONEPROC_RESULT:
    1812        1177 :                                 tdsdump_log(TDS_DBG_FUNC, "dbresults(): dbresults_state is %d (%s)\n", 
    1813             :                                                 dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
    1814             : 
    1815             :                                 /* A done token signifies the end of a logical command.
    1816             :                                  * There are three possibilities:
    1817             :                                  * 1. Simple command with no result set, i.e. update, delete, insert
    1818             :                                  * 2. Command with result set but no rows
    1819             :                                  * 3. Command with result set and rows
    1820             :                                  */
    1821        1177 :                                 switch (dbproc->dbresults_state) {
    1822             : 
    1823         807 :                                 case _DB_RES_INIT:
    1824             :                                 case _DB_RES_NEXT_RESULT:
    1825         807 :                                         dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    1826         807 :                                         if (done_flags & TDS_DONE_ERROR)
    1827             :                                                 return FAIL;
    1828         791 :                                         if (result_type == TDS_DONE_RESULT) {
    1829         685 :                                                 tds_free_all_results(tds);
    1830         685 :                                                 return SUCCEED;
    1831             :                                         }
    1832             :                                         break;
    1833             : 
    1834         370 :                                 case _DB_RES_RESULTSET_EMPTY:
    1835             :                                 case _DB_RES_RESULTSET_ROWS:
    1836         370 :                                         dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    1837         370 :                                         return SUCCEED;
    1838             :                                         break;
    1839             :                                 default:
    1840           0 :                                         assert(0);
    1841             :                                         break;
    1842             :                                 }
    1843             :                                 break;
    1844             : 
    1845        9253 :                         case TDS_DONEINPROC_RESULT:
    1846             :                                 /* 
    1847             :                                  * Return SUCCEED on a command within a stored procedure
    1848             :                                  * only if the command returned a result set. 
    1849             :                                  */
    1850        9253 :                                 switch (dbproc->dbresults_state) {
    1851        9241 :                                 case _DB_RES_INIT:  
    1852             :                                 case _DB_RES_NEXT_RESULT: 
    1853        9241 :                                         dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    1854        9241 :                                         break;
    1855          12 :                                 case _DB_RES_RESULTSET_EMPTY :
    1856             :                                 case _DB_RES_RESULTSET_ROWS : 
    1857          12 :                                         dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    1858          12 :                                         return SUCCEED;
    1859             :                                         break;
    1860             :                                 case _DB_RES_NO_MORE_RESULTS:
    1861             :                                 case _DB_RES_SUCCEED:
    1862             :                                         break;
    1863             :                                 }
    1864             :                                 break;
    1865             : 
    1866             :                         case TDS_STATUS_RESULT:
    1867             :                         case TDS_MSG_RESULT:
    1868             :                         case TDS_DESCRIBE_RESULT:
    1869             :                         case TDS_PARAM_RESULT:
    1870             :                         default:
    1871             :                                 break;
    1872             :                         }
    1873             : 
    1874             :                         break;
    1875             : 
    1876        8408 :                 case TDS_NO_MORE_RESULTS:
    1877        8408 :                         dbproc->dbresults_state = _DB_RES_NO_MORE_RESULTS;
    1878        8408 :                         return NO_MORE_RESULTS;
    1879             :                         break;
    1880             : 
    1881           0 :                 default:
    1882           0 :                         assert(TDS_FAILED(retcode));
    1883           0 :                         dbproc->dbresults_state = _DB_RES_INIT;
    1884           0 :                         return FAIL;
    1885             :                         break;
    1886             :                 }
    1887             :         }
    1888             : }
    1889             : 
    1890             : 
    1891             : /**
    1892             :  * \ingroup dblib_core
    1893             :  * \brief Return number of regular columns in a result set.  
    1894             :  * 
    1895             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1896             :  * \sa dbcollen(), dbcolname(), dbnumalts().
    1897             :  */
    1898             : int
    1899        4014 : dbnumcols(DBPROCESS * dbproc)
    1900             : {
    1901        4014 :         tdsdump_log(TDS_DBG_FUNC, "dbnumcols(%p)\n", dbproc);
    1902        4014 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    1903             : 
    1904        4014 :         if (dbproc && dbproc->tds_socket && dbproc->tds_socket->res_info)
    1905        3754 :                 return dbproc->tds_socket->res_info->num_cols;
    1906             :         return 0;
    1907             : }
    1908             : 
    1909             : /**
    1910             :  * \ingroup dblib_core
    1911             :  * \brief Return name of a regular result column.
    1912             :  * 
    1913             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1914             :  * \param column Nth in the result set, starting with 1.  
    1915             :  * \return pointer to ASCII null-terminated string, the name of the column. 
    1916             :  * \retval NULL \a column is not in range.
    1917             :  * \sa dbcollen(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
    1918             :  * \bug Relies on ASCII column names, post iconv conversion.  
    1919             :  *      Will not work as described for UTF-8 or UCS-2 clients.  
    1920             :  *      But maybe it shouldn't.  
    1921             :  */
    1922             : char *
    1923        2858 : dbcolname(DBPROCESS * dbproc, int column)
    1924             : {
    1925             :         TDSCOLUMN *colinfo;
    1926             :         
    1927        2858 :         tdsdump_log(TDS_DBG_FUNC, "dbcolname(%p, %d)\n", dbproc, column);
    1928             : 
    1929        2858 :         colinfo = dbcolptr(dbproc, column);
    1930        2858 :         if (!colinfo)
    1931             :                 return NULL;
    1932             :                 
    1933        5716 :         return tds_dstr_buf(&colinfo->column_name);
    1934             : }
    1935             : 
    1936             : /**
    1937             :  * \ingroup dblib_core
    1938             :  * \brief Return name of a computed result column.
    1939             :  *
    1940             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1941             :  * \param computeid identifies which one of potientially many compute rows is meant. The first compute
    1942             :  * clause has \a computeid == 1.
    1943             :  * \param column Nth in the result set, starting with 1.
    1944             :  * \return pointer to ASCII null-terminated string, the name of the column.
    1945             :  * \retval NULL \a column is not in range.
    1946             :  * \sa dbcollen(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
    1947             :  */
    1948             : const char *
    1949           0 : dbacolname(DBPROCESS * dbproc, int computeid, int column)
    1950             : {
    1951             :         TDSCOLUMN *colinfo;
    1952             : 
    1953           0 :         tdsdump_log(TDS_DBG_FUNC, "dbacolname(%p, %d, %d)\n", dbproc, computeid, column);
    1954             : 
    1955           0 :         colinfo = dbacolptr(dbproc, computeid, column, true);
    1956           0 :         if (!colinfo)
    1957             :                 return NULL;
    1958             : 
    1959           0 :         return tds_dstr_cstr(&colinfo->column_name);
    1960             : }
    1961             : 
    1962             : /**
    1963             :  * \ingroup dblib_core
    1964             :  * \brief Read a row from the row buffer.
    1965             :  * 
    1966             :  * When row buffering is enabled (DBBUFFER option is on), the client can use dbgetrow() to re-read a row previously fetched 
    1967             :  * with dbnextrow().  The effect is to move the row pointer -- analogous to fseek() -- back to \a row.  
    1968             :  * Calls to dbnextrow() read from \a row + 1 until the buffer is exhausted, at which point it resumes
    1969             :  * its normal behavior, except that as each row is fetched from the server, it is added to the row
    1970             :  * buffer (in addition to being returned to the client).  When the buffer is filled, dbnextrow()  returns 
    1971             :  * \c FAIL until the buffer is at least partially emptied with dbclrbuf().
    1972             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    1973             :  * \param row Nth row to read, starting with 1.
    1974             :  * \retval REG_ROW returned row is a regular row.
    1975             :  * \returns computeid when returned row is a compute row.
    1976             :  * \retval NO_MORE_ROWS no such row in the row buffer.  Current row is unchanged.
    1977             :  * \retval FAIL unsuccessful; row buffer may be full.  
    1978             :  * \sa dbaltbind(), dbbind(), dbclrbuf(), DBCURROW(), DBFIRSTROW(), DBLASTROW(), dbnextrow(), dbsetrow().
    1979             :  */
    1980             : RETCODE
    1981          60 : dbgetrow(DBPROCESS * dbproc, DBINT row)
    1982             : {
    1983          60 :         RETCODE result = FAIL;
    1984          60 :         const int idx = buffer_row2idx(&dbproc->row_buf, row);
    1985             : 
    1986          60 :         tdsdump_log(TDS_DBG_FUNC, "dbgetrow(%p, %d)\n", dbproc, row);
    1987          60 :         CHECK_CONN(FAIL);
    1988             : 
    1989          60 :         if (-1 == idx)
    1990             :                 return NO_MORE_ROWS;
    1991             : 
    1992          40 :         dbproc->row_buf.current = idx;
    1993          40 :         buffer_transfer_bound_data(&dbproc->row_buf, TDS_ROW_RESULT, 0, dbproc, idx);
    1994          40 :         result = REG_ROW;
    1995             : 
    1996          40 :         return result;
    1997             : }
    1998             : 
    1999             : /**
    2000             :  * \ingroup dblib_core
    2001             :  * \brief Define substitution values to be used when binding null values.
    2002             :  * 
    2003             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2004             :  * \param bindtype      type of binding to which the substitute value will apply. 
    2005             :  * \param bindlen       size of the substitute value you are supplying, in bytes. 
    2006             :  *                      Ignored except for CHARBIND and BINARYBIND. 
    2007             :  * \param bindval       pointer to a buffer containing the substitute value.
    2008             :  * \retval SUCCEED query was processed without errors.
    2009             :  * \retval FAIL query was not processed
    2010             :  * \sa dbaltbind(), dbbind(), dbconvert(), dbnullbind().
    2011             :  */
    2012             : RETCODE
    2013         400 : dbsetnull(DBPROCESS * dbproc, int bindtype, int bindlen, BYTE *bindval)
    2014             : {
    2015             :         BYTE *pval;
    2016             :         
    2017         400 :         tdsdump_log(TDS_DBG_FUNC, "dbsetnull(%p, %d, %d, %p)\n", dbproc, bindtype, bindlen, bindval);
    2018             :         
    2019         400 :         CHECK_CONN(FAIL);
    2020         400 :         CHECK_PARAMETER(bindval, SYBENBVP, FAIL);
    2021             :         
    2022         400 :         switch (bindtype) {
    2023         330 :         case DATETIMEBIND:
    2024             :         case DECIMALBIND:
    2025             :         case SRCDECIMALBIND:
    2026             :         case FLT8BIND:
    2027             :         case INTBIND:
    2028             :         case MONEYBIND:
    2029             :         case NUMERICBIND:
    2030             :         case SRCNUMERICBIND:
    2031             :         case REALBIND:
    2032             :         case SMALLBIND:
    2033             :         case SMALLDATETIMEBIND:
    2034             :         case SMALLMONEYBIND:
    2035             :         case TINYBIND:
    2036             :         case BIGINTBIND:
    2037             :         case DATEBIND:
    2038             :         case TIMEBIND:
    2039             :         case BIGDATETIMEBIND:
    2040             :         case BIGTIMEBIND:
    2041         330 :                 bindlen = (int)default_null_representations[bindtype].len;
    2042         330 :                 break;
    2043             : 
    2044          70 :         case CHARBIND:
    2045             :         case BINARYBIND:
    2046          70 :                 CHECK_PARAMETER(bindlen >= 0, SYBEBBL, FAIL);
    2047             :                 break;
    2048             :                 
    2049           0 :         case NTBSTRINGBIND:     bindlen = (int)strlen((char *) bindval);
    2050           0 :                 break;
    2051           0 :         case STRINGBIND:        bindlen = (int)strlen((char *) bindval);
    2052           0 :                 break;
    2053           0 :         case VARYBINBIND:       bindlen = ((DBVARYBIN*) bindval)->len;
    2054           0 :                 break;
    2055           0 :         case VARYCHARBIND:      bindlen = ((DBVARYCHAR*) bindval)->len;
    2056           0 :                 break;
    2057             : 
    2058             : #if 0
    2059             :         case SENSITIVITYBIND:
    2060             :         case BOUNDARYBIND:
    2061             : #endif
    2062           0 :         default:
    2063           0 :                 dbperror(dbproc, SYBEBTYP, 0);
    2064           0 :                 return FAIL;
    2065             :         }
    2066             : 
    2067         400 :         if ((pval = tds_new(BYTE, bindlen)) == NULL) {
    2068           0 :                 dbperror(dbproc, SYBEMEM, errno);
    2069           0 :                 return FAIL;
    2070             :         }
    2071             :         
    2072             :         /* free any prior allocation */
    2073         400 :         if (dbproc->nullreps[bindtype].bindval != default_null_representations[bindtype].bindval)
    2074         380 :                 free((BYTE*)dbproc->nullreps[bindtype].bindval);
    2075             : 
    2076         400 :         memcpy(pval, bindval, bindlen);
    2077             :         
    2078         400 :         dbproc->nullreps[bindtype].bindval = pval;
    2079         400 :         dbproc->nullreps[bindtype].len = bindlen;
    2080             : 
    2081         400 :         tdsdump_dump_buf(TDS_DBG_NETWORK, "null representation set ", pval,  bindlen);
    2082             :         return SUCCEED;
    2083             : }
    2084             : 
    2085             : /**
    2086             :  * \ingroup dblib_core
    2087             :  * \brief Make a buffered row "current" without fetching it into bound variables.  
    2088             :  * 
    2089             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2090             :  * \retval MORE_ROWS row found
    2091             :  * \retval NO_MORE_ROWS row not found
    2092             :  * \retval FAIL \a dbproc is dead or not enabled
    2093             :  * \sa dbaltbind(), dbbind(), dbcanquery(), dbclrbuf(), dbgetrow(), dbnextrow(), dbprrow().
    2094             :  */
    2095             : STATUS
    2096           0 : dbsetrow(DBPROCESS * dbproc, DBINT row)
    2097             : {
    2098           0 :         const int idx = buffer_row2idx(&dbproc->row_buf, row);
    2099             : 
    2100           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsetrow(%p, %d)\n", dbproc, row);
    2101           0 :         CHECK_CONN(FAIL);
    2102             : 
    2103           0 :         if (-1 == idx)
    2104             :                 return NO_MORE_ROWS;
    2105             :                 
    2106           0 :         dbproc->row_buf.current = idx;
    2107             :                 
    2108             :         /* FIXME: should determine REG_ROW or compute_id; */
    2109           0 :         return REG_ROW; 
    2110             : }
    2111             : 
    2112             : /**
    2113             :  * \ingroup dblib_core
    2114             :  * \brief Read result row into the row buffer and into any bound host variables.
    2115             :  * 
    2116             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2117             :  * \retval REG_ROW regular row has been read.
    2118             :  * \returns computeid when a compute row is read. 
    2119             :  * \retval BUF_FULL reading next row would cause the buffer to be exceeded (and buffering is turned on).
    2120             :  * No row was read from the server
    2121             :  * \sa dbaltbind(), dbbind(), dbcanquery(), dbclrbuf(), dbgetrow(), dbprrow(), dbsetrow().
    2122             :  */
    2123             : struct pivot_t;
    2124             : STATUS
    2125      220548 : dbnextrow(DBPROCESS * dbproc)
    2126             : {
    2127             :         TDSRESULTINFO *resinfo;
    2128             :         TDSSOCKET *tds;
    2129      220548 :         STATUS result = FAIL;
    2130             :         TDS_INT res_type;
    2131             :         TDS_INT computeid;
    2132             :         int idx; /* row buffer index.  Unless DBUFFER is on, idx will always be 0. */
    2133             :         struct pivot_t *pivot;
    2134             : 
    2135      220548 :         tdsdump_log(TDS_DBG_FUNC, "dbnextrow(%p)\n", dbproc);
    2136      220548 :         CHECK_CONN(FAIL);
    2137             : 
    2138      220548 :         tds = dbproc->tds_socket;
    2139      220548 :         resinfo = tds->res_info;
    2140             : 
    2141      220548 :         tdsdump_log(TDS_DBG_FUNC, "dbnextrow() dbresults_state = %d (%s)\n", 
    2142             :                                         dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
    2143             : 
    2144      220548 :         if (!resinfo || dbproc->dbresults_state != _DB_RES_RESULTSET_ROWS) {
    2145             :                 /* no result set or result set empty (no rows) */
    2146         820 :                 tdsdump_log(TDS_DBG_FUNC, "leaving dbnextrow() returning %d (NO_MORE_ROWS)\n", NO_MORE_ROWS);
    2147         820 :                 return dbproc->row_type = NO_MORE_ROWS;
    2148             :         }
    2149             : 
    2150             :         /*
    2151             :          * Try to get the dbproc->row_buf.current item from the buffered rows, if any.  
    2152             :          * Else read from the stream, unless the buffer is exhausted.  
    2153             :          * If no rows are read, DBROWTYPE() will report NO_MORE_ROWS. 
    2154             :          */
    2155      219728 :         dbproc->row_type = NO_MORE_ROWS; 
    2156      219728 :         computeid = REG_ROW;
    2157      219728 :         if (-1 != (idx = buffer_current_index(dbproc))) {                       
    2158             :                 /*
    2159             :                  * Cool, the item we want is already there
    2160             :                  */
    2161          10 :                 result = dbproc->row_type = REG_ROW;
    2162          10 :                 res_type = TDS_ROW_RESULT;
    2163             :                 
    2164      219718 :         } else if (buffer_is_full(&dbproc->row_buf)) {
    2165             :                 
    2166         120 :                 result = BUF_FULL;
    2167         120 :                 res_type = TDS_ROWFMT_RESULT;
    2168             :         
    2169      219598 :         } else if ((pivot = dbrows_pivoted(dbproc)) != NULL) {
    2170             :         
    2171           0 :                 tdsdump_log(TDS_DBG_FUNC, "returning pivoted row\n");
    2172           0 :                 return dbnextrow_pivoted(dbproc, pivot);
    2173             : 
    2174             :         } else {
    2175      219598 :                 const int mask = TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE;
    2176      219598 :                 TDS_INT8 row_count = TDS_NO_COUNT;
    2177      219598 :                 bool rows_set = false;
    2178      219598 :                 buffer_save_row(dbproc);
    2179             : 
    2180             :                 /* Get the row from the TDS stream.  */
    2181         718 : again:
    2182      220316 :                 switch (tds_process_tokens(tds, &res_type, NULL, mask)) {
    2183      220286 :                 case TDS_SUCCESS:
    2184      220286 :                         if (res_type == TDS_ROW_RESULT || res_type == TDS_COMPUTE_RESULT) {
    2185      207698 :                                 if (res_type == TDS_COMPUTE_RESULT)
    2186          32 :                                         computeid = tds->current_results->computeid;
    2187             :                                 /* Add the row to the row buffer, whose capacity is always at least 1 */
    2188      207698 :                                 resinfo = tds->current_results;
    2189      207698 :                                 idx = buffer_add_row(dbproc, resinfo);
    2190      207698 :                                 assert(idx != -1);
    2191      207698 :                                 result = dbproc->row_type = (res_type == TDS_ROW_RESULT)? REG_ROW : computeid;
    2192             : #if 0 /* TODO */
    2193             :                                 tds_process_tokens(tds, &res_type, NULL, TDS_TOKEN_TRAILING);
    2194             : #endif
    2195      207698 :                                 break;
    2196             :                         }
    2197             :                         /* allows to process trailing tokens */
    2198       12588 :                         if (res_type == TDS_DONEINPROC_RESULT) {
    2199         718 :                                 if (!rows_set)
    2200         462 :                                         row_count = tds->rows_affected;
    2201             :                                 rows_set = true;
    2202             :                                 goto again;
    2203             :                         }
    2204             :                         /* fall through */
    2205             :                 case TDS_NO_MORE_RESULTS:
    2206       11900 :                         dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    2207       11900 :                         result = NO_MORE_ROWS;
    2208       11900 :                         break;
    2209           0 :                 default:
    2210           0 :                         tdsdump_log(TDS_DBG_FUNC, "unexpected: leaving dbnextrow() returning FAIL\n");
    2211             :                         return FAIL;
    2212             :                         break;
    2213             :                 }
    2214      219598 :                 if (rows_set)
    2215         462 :                         tds->rows_affected = row_count;
    2216             :         }
    2217             : 
    2218      219728 :         if (res_type == TDS_ROW_RESULT || res_type == TDS_COMPUTE_RESULT) {
    2219             :                 /*
    2220             :                  * Transfer the data from the row buffer to the bound variables.  
    2221             :                  */
    2222      207708 :                 buffer_transfer_bound_data(&dbproc->row_buf, res_type, computeid, dbproc, idx);
    2223             :         }
    2224             :         
    2225      219728 :         if (res_type == TDS_COMPUTE_RESULT) {
    2226          32 :                 tdsdump_log(TDS_DBG_FUNC, "leaving dbnextrow() returning compute_id %d\n", result);
    2227             :         } else {
    2228      219696 :                 tdsdump_log(TDS_DBG_FUNC, "leaving dbnextrow() returning %d (%s)\n", result, prdbretcode(result));
    2229             :         }
    2230             :         return result;
    2231             : } /* dbnextrow()  */
    2232             : 
    2233             : static TDS_SERVER_TYPE
    2234             : dblib_bound_type(int bindtype)
    2235             : {
    2236             :         switch (bindtype) {
    2237             :         case CHARBIND:
    2238             :         case STRINGBIND:
    2239             :         case NTBSTRINGBIND:
    2240             :                 return SYBCHAR;
    2241             :                 break;
    2242             :         case FLT8BIND:
    2243             :                 return SYBFLT8;
    2244             :                 break;
    2245             :         case REALBIND:
    2246             :                 return SYBREAL;
    2247             :                 break;
    2248             :         case INTBIND:
    2249             :                 return SYBINT4;
    2250             :                 break;
    2251             :         case SMALLBIND:
    2252             :                 return SYBINT2;
    2253             :                 break;
    2254             :         case TINYBIND:
    2255             :                 return SYBINT1;
    2256             :                 break;
    2257             :         case BIGINTBIND:
    2258             :                 return SYBINT8;
    2259             :                 break;
    2260             :         case DATETIMEBIND:
    2261             :                 return SYBDATETIME;
    2262             :                 break;
    2263             :         case SMALLDATETIMEBIND:
    2264             :                 return SYBDATETIME4;
    2265             :                 break;
    2266             :         case DATEBIND:
    2267             :                 return SYBDATE;
    2268             :                 break;
    2269             :         case TIMEBIND:
    2270             :                 return SYBTIME;
    2271             :                 break;
    2272             :         case BIGDATETIMEBIND:
    2273             :                 return SYB5BIGDATETIME;
    2274             :                 break;
    2275             :         case BIGTIMEBIND:
    2276             :                 return SYB5BIGTIME;
    2277             :                 break;
    2278             :         case MONEYBIND:
    2279             :                 return SYBMONEY;
    2280             :                 break;
    2281             :         case SMALLMONEYBIND:
    2282             :                 return SYBMONEY4;
    2283             :                 break;
    2284             :         case BINARYBIND:
    2285             :                 return SYBBINARY;
    2286             :                 break;
    2287             :         case VARYBINBIND:
    2288             :                 return SYBVARBINARY;
    2289             :                 break;
    2290             :         case VARYCHARBIND:
    2291             :                 return SYBVARCHAR;
    2292             :                 break;
    2293             :         case BITBIND:
    2294             :                 return SYBBIT;
    2295             :                 break;
    2296             :         case NUMERICBIND:
    2297             :         case SRCNUMERICBIND:
    2298             :         case DECIMALBIND:
    2299             :         case SRCDECIMALBIND:
    2300             :                 return SYBNUMERIC;
    2301             :                 break;
    2302             :         case DATETIME2BIND:
    2303             :                 return SYBMSDATETIMEOFFSET;
    2304             :                 break;
    2305             :         default:
    2306             :                 return TDS_INVALID_TYPE;
    2307             :         }
    2308             : }
    2309             : 
    2310             : /**
    2311             :  * \ingroup dblib_core
    2312             :  * \brief Convert one datatype to another.
    2313             :  *
    2314             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2315             :  * \param srctype datatype of the data to convert. 
    2316             :  * \param src buffer to convert
    2317             :  * \param srclen length of \a src
    2318             :  * \param desttype target datatype
    2319             :  * \param dest output buffer
    2320             :  * \param destlen size of \a dest
    2321             :  * \returns     On success, the count of output bytes in \a dest, else -1. On failure, it will call any user-supplied error handler. 
    2322             :  * \remarks      Causes of failure: 
    2323             :  *              - No such conversion unavailable. 
    2324             :  *              - Character data output was truncated, or numerical data overflowed or lost precision. 
    2325             :  *              - In converting character data to one of the numeric types, the string could not be interpreted as a number.  
    2326             :  *              
    2327             :  * Conversion functions are handled in the TDS layer.
    2328             :  * 
    2329             :  * The main reason for this is that \c ct-lib and \c ODBC (and presumably \c DBI) need
    2330             :  * to be able to do conversions between datatypes. This is possible because
    2331             :  * the format of complex data (dates, money, numeric, decimal) is defined by
    2332             :  * its representation on the wire; thus what we call \c DBMONEY is exactly its
    2333             :  * format on the wire. CLIs that need a different representation (ODBC?) 
    2334             :  * need to convert from this format anyway, so the code would already be in
    2335             :  * place.
    2336             :  * 
    2337             :  * Each datatype is also defined by its Server-type so all CLIs should be 
    2338             :  * able to map native types to server types as well.
    2339             :  *
    2340             :  * tds_convert() copies from src to dest and returns the output data length,
    2341             :  * period.  All padding and termination is the responsibility of the API library
    2342             :  * and is done post-conversion.  The peculiar rule in dbconvert() is that
    2343             :  * a \a destlen of -1 and a \a desttype of \c SYBCHAR means the output buffer
    2344             :  * should be null-terminated.
    2345             :  *  
    2346             :  * \sa dbaltbind(), dbaltbind_ps(), dbbind(), dbbind_ps(), dbconvert_ps(), dberrhandle(), dbsetnull(), dbsetversion(), dbwillconvert().
    2347             :  * \todo What happens if client does not reset values? 
    2348             :  * \todo Microsoft and Sybase define this function differently.  
    2349             :  */
    2350             : DBINT
    2351        2288 : dbconvert_ps(DBPROCESS * dbproc, int db_srctype, const BYTE * src, DBINT srclen,
    2352             :              int db_desttype, BYTE * dest, DBINT destlen, DBTYPEINFO * typeinfo)
    2353             : {
    2354             :         CONV_RESULT dres;
    2355             :         DBINT ret;
    2356             :         int i;
    2357             :         int len;
    2358             :         TDS_SERVER_TYPE srctype, desttype;
    2359             : 
    2360        2288 :         tdsdump_log(TDS_DBG_FUNC, "dbconvert_ps(%p, %s, %p, %d, %s, %p, %d, %p)\n",
    2361             :                     dbproc, tds_prdatatype(db_srctype), src, srclen,
    2362             :                     tds_prdatatype(db_desttype), dest, destlen, typeinfo);
    2363             :         /* dbproc and src can be NULLs */
    2364        2288 :         CHECK_PARAMETER(dest, SYBEACNV, -1);
    2365             : 
    2366        2288 :         DBPERROR_RETURN(!is_tds_type_valid(db_srctype), SYBEUDTY);
    2367        2288 :         srctype = (TDS_SERVER_TYPE) db_srctype;
    2368        2288 :         DBPERROR_RETURN(!is_tds_type_valid(db_desttype), SYBEUDTY);
    2369        2288 :         desttype = (TDS_SERVER_TYPE) db_desttype;
    2370             : 
    2371        2288 :         if (is_numeric_type(desttype)) {
    2372         480 :                 TDS_NUMERIC *d = &dres.n;
    2373             : 
    2374         480 :                 if (typeinfo == NULL) {
    2375           0 :                         if (is_numeric_type(srctype)) {
    2376           0 :                                 DBNUMERIC *s = (DBNUMERIC *) src;
    2377           0 :                                 d->precision = s->precision;
    2378           0 :                                 d->scale = s->scale;
    2379             :                         } else {
    2380           0 :                                 d->precision = 18;
    2381           0 :                                 d->scale = 0;
    2382             :                         }
    2383             :                 } else {
    2384         480 :                         d->precision = typeinfo->precision;
    2385         480 :                         d->scale = typeinfo->scale;
    2386             :                 }
    2387             :         }
    2388             : 
    2389        2288 :         if (0 == destlen) 
    2390             :                 return 0; 
    2391             : 
    2392        2288 :         if (src == NULL || srclen == 0) {
    2393         122 :                 int bind = dbbindtype(desttype);
    2394         122 :                 int size = tds_get_size_by_type(desttype);
    2395             : 
    2396         122 :                 if (bind == NTBSTRINGBIND) {
    2397          22 :                         if (destlen > 0) {
    2398             :                                 size = destlen;
    2399             :                                 bind = CHARBIND;
    2400             :                         } else { 
    2401          14 :                                 size = 1;
    2402          14 :                                 bind = NTBSTRINGBIND;
    2403             :                         }
    2404         100 :                 } else if (bind == BINARYBIND) {
    2405         100 :                         if (destlen > 0)
    2406             :                                 size = destlen;
    2407             :                         else
    2408          10 :                                 size = 0;
    2409             :                 }
    2410             : 
    2411         122 :                 dbgetnull(dbproc, bind, size, dest);
    2412         122 :                 return size;
    2413             :         }
    2414             : 
    2415             :         /* srclen of -1 means the source data is definitely NULL terminated */
    2416        2166 :         if (srclen == -1)
    2417         480 :                 srclen = (int)strlen((const char *) src);
    2418             : 
    2419             :         /* often times we are asked to convert a data type to itself */
    2420        2166 :         if (srctype == desttype && !is_numeric_type(desttype)) {
    2421         150 :                 ret = -2;  /* to make sure we always set it */
    2422         150 :                 tdsdump_log(TDS_DBG_INFO1, "dbconvert_ps() srctype == desttype\n");
    2423         150 :                 switch (desttype) {
    2424             : 
    2425          10 :                 case SYBBINARY:
    2426             :                 case SYBVARBINARY:
    2427             :                 case SYBIMAGE:
    2428          10 :                         if (srclen > destlen && destlen >= 0) {
    2429           0 :                                 dbperror(dbproc, SYBECOFL, 0);
    2430           0 :                                 ret = -1;
    2431             :                         } else {
    2432          10 :                                 memcpy(dest, src, srclen);
    2433          10 :                                 if (srclen < destlen)
    2434           0 :                                         memset(dest + srclen, 0, destlen - srclen);
    2435             :                                 ret = srclen;
    2436             :                         }
    2437             :                         break;
    2438             : 
    2439         140 :                 case SYBCHAR:
    2440             :                 case SYBVARCHAR:
    2441             :                 case SYBTEXT:
    2442             :                         /* srclen of -1 means the source data is definitely NULL terminated */
    2443         140 :                         if (srclen == -1)
    2444           0 :                                 srclen = (int)strlen((const char *) src);
    2445             : 
    2446         140 :                         switch (destlen) {
    2447             :                         case  0:        /* nothing to copy */
    2448             :                                 ret = 0;
    2449             :                                 break;
    2450             :                         case -1:        /* rtrim and null terminate */
    2451          80 :                                 while (srclen && src[srclen - 1] == ' ') {
    2452          20 :                                         --srclen;
    2453             :                                 }
    2454             :                                 /* fall thru */
    2455             :                         case -2:        /* just null terminate */
    2456          80 :                                 memcpy(dest, src, srclen);
    2457          80 :                                 dest[srclen] = '\0';
    2458          80 :                                 ret = srclen;
    2459          80 :                                 break;
    2460          60 :                         default:
    2461          60 :                                 assert(destlen > 0);
    2462          60 :                                 if (destlen < 0 || srclen > destlen) {
    2463          20 :                                         dbperror(dbproc, SYBECOFL, 0);
    2464          20 :                                         ret = -1;
    2465             :                                 } else {
    2466          40 :                                         memcpy(dest, src, srclen);
    2467          40 :                                         if (srclen < destlen)
    2468          20 :                                                 memset(dest + srclen, ' ', destlen - srclen);
    2469             :                                         ret = srclen;
    2470             :                                 }
    2471             :                                 break;
    2472             :                         }
    2473             :                         break;
    2474           0 :                 case SYBINT1:
    2475             :                 case SYBINT2:
    2476             :                 case SYBINT4:
    2477             :                 case SYBINT8:
    2478             :                 case SYBFLT8:
    2479             :                 case SYBREAL:
    2480             :                 case SYBBIT:
    2481             :                 case SYBBITN:
    2482             :                 case SYBMONEY:
    2483             :                 case SYBMONEY4:
    2484             :                 case SYBDATETIME:
    2485             :                 case SYBDATETIME4:
    2486             :                 case SYBDATE:
    2487             :                 case SYBTIME:
    2488             :                 case SYB5BIGDATETIME:
    2489             :                 case SYB5BIGTIME:
    2490             :                 case SYBUNIQUE:
    2491           0 :                         ret = tds_get_size_by_type(desttype);
    2492           0 :                         memcpy(dest, src, ret);
    2493           0 :                         break;
    2494             : 
    2495           0 :                 case SYBMSDATE:
    2496             :                 case SYBMSTIME:
    2497             :                 case SYBMSDATETIME2:
    2498             :                 case SYBMSDATETIMEOFFSET:
    2499           0 :                         ret = sizeof(TDS_DATETIMEALL);
    2500           0 :                         memcpy(dest, src, ret);
    2501             :                         break;
    2502             : 
    2503             :                 default:
    2504             :                         ret = -1;
    2505             :                         break;
    2506             :                 }
    2507         130 :                 assert(ret > -2);
    2508             :                 return ret;
    2509             :         }
    2510             :         /* end srctype == desttype */
    2511             : 
    2512             :         /*
    2513             :          * Character types need no conversion.  Just move the data.
    2514             :          */
    2515        2016 :         if (is_similar_type(srctype, desttype)) {
    2516         662 :                 if (src && dest && srclen > 0 && destlen >= srclen) {
    2517         662 :                         memcpy(dest, src, srclen);
    2518         662 :                         return srclen;
    2519             :                 }
    2520             :         }
    2521             : 
    2522        1354 :         tdsdump_log(TDS_DBG_INFO1, "dbconvert_ps() calling tds_convert\n");
    2523             : 
    2524        1354 :         len = tds_convert(g_dblib_ctx.tds_ctx, srctype, src, srclen, desttype, &dres);
    2525        1354 :         tdsdump_log(TDS_DBG_INFO1, "dbconvert_ps() called tds_convert returned %d\n", len);
    2526             : 
    2527        1354 :         if (len < 0) {
    2528           0 :                 _dblib_convert_err(dbproc, len);
    2529           0 :                 return -1;
    2530             :         }
    2531             : 
    2532        1354 :         switch (desttype) {
    2533           0 :         case SYBBINARY:
    2534             :         case SYBVARBINARY:
    2535             :         case SYBIMAGE:
    2536           0 :                 if (len > destlen && destlen >= 0) {
    2537           0 :                         dbperror(dbproc, SYBECOFL, 0);
    2538           0 :                         ret = -1;
    2539             :                 } else {
    2540           0 :                         memcpy(dest, dres.ib, len);
    2541           0 :                         if (len < destlen)
    2542           0 :                                 memset(dest + len, 0, destlen - len);
    2543             :                         ret = len;
    2544             :                 }
    2545           0 :                 free(dres.ib);
    2546           0 :                 break;
    2547         480 :         case SYBINT1:
    2548             :         case SYBINT2:
    2549             :         case SYBINT4:
    2550             :         case SYBINT8:
    2551             :         case SYBFLT8:
    2552             :         case SYBREAL:
    2553             :         case SYBBIT:
    2554             :         case SYBBITN:
    2555             :         case SYBMONEY:
    2556             :         case SYBMONEY4:
    2557             :         case SYBDATETIME:
    2558             :         case SYBDATETIME4:
    2559             :         case SYBTIME:
    2560             :         case SYBDATE:
    2561             :         case SYB5BIGDATETIME:
    2562             :         case SYB5BIGTIME:
    2563             :         case SYBUNIQUE:
    2564             :         case SYBMSDATE:
    2565             :         case SYBMSTIME:
    2566             :         case SYBMSDATETIME2:
    2567             :         case SYBMSDATETIMEOFFSET:
    2568             :         case SYBNUMERIC:
    2569             :         case SYBDECIMAL:
    2570         480 :                 memcpy(dest, &(dres.ti), len);
    2571         480 :                 ret = len;
    2572         480 :                 break;
    2573         874 :         case SYBCHAR:
    2574             :         case SYBVARCHAR:
    2575             :         case SYBTEXT:
    2576         874 :                 tdsdump_log(TDS_DBG_INFO1, "dbconvert_ps() outputting %d bytes character data destlen = %d \n", len, destlen);
    2577             : 
    2578         874 :                 if (destlen < -2)
    2579             :                         destlen = 0;    /* failure condition */
    2580             : 
    2581         874 :                 switch (destlen) {
    2582             :                 case 0:
    2583             :                         ret = -1;
    2584             :                         break;
    2585         204 :                 case -1:        /* rtrim and null terminate */
    2586         204 :                         for (i = len - 1; i >= 0 && dres.c[i] == ' '; --i) {
    2587           0 :                                 len = i;
    2588             :                         }
    2589         204 :                         memcpy(dest, dres.c, len);
    2590         204 :                         dest[len] = '\0';
    2591         204 :                         ret = len;
    2592         204 :                         break;
    2593           0 :                 case -2:        /* just null terminate */
    2594           0 :                         memcpy(dest, dres.c, len);
    2595           0 :                         dest[len] = 0;
    2596           0 :                         ret = len;
    2597           0 :                         break;
    2598         670 :                 default:
    2599         670 :                         assert(destlen > 0);
    2600         670 :                         if (destlen < 0 || len > destlen) {
    2601           0 :                                 dbperror(dbproc, SYBECOFL, 0);
    2602           0 :                                 ret = -1;
    2603           0 :                                 tdsdump_log(TDS_DBG_INFO1, "%d bytes type %d -> %d, destlen %d < %d required\n",
    2604             :                                             srclen, srctype, desttype, destlen, len);
    2605             :                                 break;
    2606             :                         }
    2607             :                         /* else pad with blanks */
    2608         670 :                         memcpy(dest, dres.c, len);
    2609         670 :                         if (len < destlen)
    2610         670 :                                 memset(dest + len, ' ', destlen - len);
    2611             :                         ret = len;
    2612             : 
    2613             :                         break;
    2614             :                 }
    2615             : 
    2616         874 :                 free(dres.c);
    2617             : 
    2618         874 :                 break;
    2619           0 :         default:
    2620           0 :                 tdsdump_log(TDS_DBG_INFO1, "error: dbconvert_ps(): unrecognized desttype %d \n", desttype);
    2621             :                 ret = -1;
    2622             :                 break;
    2623             : 
    2624             :         }
    2625             :         return (ret);
    2626             : }
    2627             : 
    2628             : /**
    2629             :  * \ingroup dblib_core
    2630             :  * \brief cf. dbconvert_ps(), above
    2631             :  * 
    2632             :  * \em Sybase: Convert numeric types.
    2633             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2634             :  * \param srctype datatype of the data to convert. 
    2635             :  * \param src buffer to convert
    2636             :  * \param srclen length of \a src
    2637             :  * \param desttype target datatype
    2638             :  * \param dest output buffer
    2639             :  * \param destlen size of \a dest
    2640             :  * \param typeinfo address of a \c DBTYPEINFO structure that governs the precision & scale of the output, may be \c NULL.
    2641             :  * \sa dbaltbind(), dbaltbind_ps(), dbbind(), dbbind_ps(), dbconvert_ps(), dberrhandle(), dbsetnull(), dbsetversion(), dbwillconvert().
    2642             :  */
    2643             : DBINT
    2644        1808 : dbconvert(DBPROCESS * dbproc,
    2645             :           int srctype, const BYTE * src, DBINT srclen, int desttype, BYTE * dest, DBINT destlen)
    2646             : {
    2647        1808 :         DBTYPEINFO ti, *pti = NULL;
    2648             : 
    2649        1808 :         tdsdump_log(TDS_DBG_FUNC, "dbconvert(%p)\n", dbproc);
    2650             :         /* dbproc can be NULL*/
    2651             : 
    2652        1808 :         DBPERROR_RETURN(!is_tds_type_valid(desttype), SYBEUDTY);
    2653             : 
    2654        1808 :         if (is_numeric_type(desttype)) {
    2655             :                 DBNUMERIC *num;
    2656             : 
    2657             :                 /* FIXME what happen if client do not reset values ??? */
    2658           0 :                 if (dbproc->msdblib) {
    2659           0 :                         num = (DBNUMERIC *) dest;
    2660           0 :                         ti.precision = num->precision;
    2661           0 :                         ti.scale = num->scale;
    2662           0 :                         pti = &ti;
    2663             :                 } else {
    2664             :                         /* for Sybase passing NULL as DBTYPEINFO is fine */
    2665             :                 }
    2666             :         }
    2667             : 
    2668        1808 :         return dbconvert_ps(dbproc, srctype, src, srclen, desttype, dest, destlen, pti);
    2669             : }
    2670             : 
    2671             : /**
    2672             :  * \ingroup dblib_core
    2673             :  * \brief Tie a host variable to a resultset column.
    2674             :  * 
    2675             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2676             :  * \param column Nth column, starting at 1.
    2677             :  * \param vartype datatype of the host variable that will receive the data
    2678             :  * \param varlen size of host variable pointed to \a varaddr
    2679             :  * \param varaddr address of host variable
    2680             :  * \retval SUCCEED everything worked.
    2681             :  * \retval FAIL no such \a column or no such conversion possible, or target buffer too small.
    2682             :  * \sa 
    2683             :  */
    2684             : RETCODE
    2685       21718 : dbbind(DBPROCESS * dbproc, int column, int vartype, DBINT varlen, BYTE * varaddr)
    2686             : {
    2687       21718 :         TDSCOLUMN *colinfo = NULL;
    2688             :         TDSRESULTINFO* results;
    2689             :         TDS_SERVER_TYPE srctype, desttype;
    2690             : 
    2691       21718 :         tdsdump_log(TDS_DBG_FUNC, "dbbind(%p, %d, %d, %d, %p)\n", dbproc, column, vartype, varlen, varaddr);
    2692       21718 :         CHECK_CONN(FAIL);
    2693       21718 :         CHECK_PARAMETER(varaddr, SYBEABNV, FAIL);
    2694             :         
    2695       21718 :         results = dbproc->tds_socket->res_info;
    2696             :         
    2697       21718 :         if (results == NULL || results->num_cols < column || column < 1) {
    2698           0 :                 dbperror(dbproc, SYBEABNC, 0);
    2699           0 :                 return FAIL;
    2700             :         }
    2701             :         
    2702       21718 :         if (varlen < 0) {
    2703         154 :                 switch (vartype) {
    2704          90 :                 case CHARBIND:
    2705             :                 case STRINGBIND:
    2706             :                 case NTBSTRINGBIND:
    2707             :                 case VARYCHARBIND:
    2708             :                 case VARYBINBIND:
    2709             :                         /* 
    2710             :                          * No message for this error.  Documentation doesn't define varlen < 0, but 
    2711             :                          * experimentation with Sybase db-lib shows it's accepted as if zero. 
    2712             :                          */
    2713          90 :                         tdsdump_log(TDS_DBG_FUNC, "dbbind: setting varlen (%d) to 0\n", varlen);
    2714             :                         varlen = 0;
    2715             :                         break;
    2716             :                 }
    2717             :         }
    2718             : 
    2719       21628 :         if (0 == varlen) {              /* "Note that if varlen is 0, no padding takes place." */
    2720       21354 :                 switch (vartype) {
    2721       10906 :                 case CHARBIND:
    2722             :                 case STRINGBIND:
    2723             :                 case NTBSTRINGBIND:
    2724       10906 :                         varlen = -1;
    2725       10906 :                         break;
    2726             :                 default:
    2727             :                         break;          /* dbconvert: "The destlen is ignored for all fixed-length, non-NULL data types." */
    2728             :                 }
    2729         364 :         }
    2730             : 
    2731       21718 :         dbproc->avail_flag = FALSE;
    2732             : 
    2733       21718 :         colinfo = dbproc->tds_socket->res_info->columns[column - 1];
    2734       21718 :         srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    2735       21718 :         desttype = dblib_bound_type(vartype);
    2736       21718 :         if (desttype == TDS_INVALID_TYPE) {
    2737           0 :                 dbperror(dbproc, SYBEBTYP, 0);
    2738           0 :                 return FAIL;
    2739             :         }
    2740             : 
    2741       21718 :         if (!dbwillconvert(srctype, desttype)) {
    2742           0 :                 dbperror(dbproc, SYBEABMT, 0);
    2743           0 :                 return FAIL;
    2744             :         }
    2745             : 
    2746       21718 :         colinfo->column_varaddr = (char *) varaddr;
    2747       21718 :         colinfo->column_bindtype = vartype;
    2748       21718 :         colinfo->column_bindlen = varlen;
    2749             : 
    2750       21718 :         return SUCCEED;
    2751             : }                               /* dbbind()  */
    2752             : 
    2753             : /**
    2754             :  * \ingroup dblib_core
    2755             :  * \brief set name and location of the \c interfaces file FreeTDS should use to look up a servername.
    2756             :  * 
    2757             :  * Does not affect lookups or location of \c freetds.conf.  
    2758             :  * \param filename name of \c interfaces. It can be NULL (reset to default)
    2759             :  * \sa dbopen()
    2760             :  */
    2761             : void
    2762           0 : dbsetifile(char *filename)
    2763             : {
    2764           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsetifile(%s)\n", filename? filename : "0x00");
    2765             : 
    2766           0 :         tds_set_interfaces_file_loc(filename);
    2767           0 : }
    2768             : 
    2769             : /**
    2770             :  * \ingroup dblib_core
    2771             :  * \brief Tie a null-indicator to a regular result column.
    2772             :  * 
    2773             :  * 
    2774             :  * When a row is fetched, the indicator variable tells the state of the column's data.  
    2775             :  * 
    2776             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2777             :  * \param column Nth column in the result set, starting with 1.
    2778             :  * \param indicator address of host variable.
    2779             :  * \retval SUCCEED variable accepted.
    2780             :  * \retval FAIL \a indicator is NULL or \a column is out of range. 
    2781             :  * \remarks Contents of \a indicator are set with \c dbnextrow().  Possible values are:
    2782             :  * -  0 \a column bound successfully
    2783             :  * - -1 \a column is NULL.
    2784             :  * - >0 true length of data, had \a column not been truncated due to insufficient space in the columns bound host variable .  
    2785             :  * \sa dbanullbind(), dbbind(), dbdata(), dbdatlen(), dbnextrow().
    2786             :  */
    2787             : RETCODE
    2788         240 : dbnullbind(DBPROCESS * dbproc, int column, DBINT * indicator)
    2789             : {
    2790             :         TDSCOLUMN *colinfo;
    2791             : 
    2792         240 :         tdsdump_log(TDS_DBG_FUNC, "dbnullbind(%p, %d, %p)\n", dbproc, column, indicator);
    2793             : 
    2794         240 :         colinfo = dbcolptr(dbproc, column);
    2795         240 :         if (!colinfo)
    2796             :                 return FAIL; /* dbcolptr sent SYBECNOR, Column number out of range */
    2797             : 
    2798         240 :         colinfo->column_nullbind = (TDS_SMALLINT *)indicator;
    2799         240 :         return SUCCEED;
    2800             : }
    2801             : 
    2802             : /**
    2803             :  * \ingroup dblib_core
    2804             :  * \brief Tie a null-indicator to a compute result column.
    2805             :  * 
    2806             :  * 
    2807             :  * When a row is fetched, the indicator variable tells the state of the column's data.  
    2808             :  * 
    2809             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2810             :  * \param computeid identifies which one of potientially many compute rows is meant. The first compute
    2811             :  * clause has \a computeid == 1.  
    2812             :  * \param column Nth column in the result set, starting with 1.
    2813             :  * \param indicator address of host variable.
    2814             :  * \retval SUCCEED variable accepted.
    2815             :  * \retval FAIL \a indicator is NULL or \a column is out of range. 
    2816             :  * \remarks Contents of \a indicator are set with \c dbnextrow().  Possible values are:
    2817             :  * -  0 \a column bound successfully
    2818             :  * - -1 \a column is NULL.
    2819             :  * - >0 true length of data, had \a column not been truncated due to insufficient space in the columns bound host variable .  
    2820             :  * \sa dbadata(), dbadlen(), dbaltbind(), dbnextrow(), dbnullbind().
    2821             :  * \todo Never fails, but only because failure conditions aren't checked.  
    2822             :  */
    2823             : RETCODE
    2824           0 : dbanullbind(DBPROCESS * dbproc, int computeid, int column, DBINT * indicator)
    2825             : {
    2826             :         TDSCOLUMN *curcol;
    2827             : 
    2828           0 :         tdsdump_log(TDS_DBG_FUNC, "dbanullbind(%p, %d, %d, %p)\n", dbproc, computeid, column, indicator);
    2829             : 
    2830           0 :         curcol = dbacolptr(dbproc, computeid, column, true);
    2831           0 :         if (!curcol)
    2832             :                 return FAIL;
    2833             : 
    2834             :         /*
    2835             :          *  XXX Need to check for possibly problems before assuming
    2836             :          *  everything is okay
    2837             :          */
    2838           0 :         curcol->column_nullbind = (TDS_SMALLINT *)indicator;
    2839             : 
    2840           0 :         return SUCCEED;
    2841             : }
    2842             : 
    2843             : /**
    2844             :  * \ingroup dblib_core
    2845             :  * \brief Indicates whether or not the count returned by dbcount is real (Microsoft-compatibility feature).
    2846             :  * 
    2847             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2848             :  * \returns TRUE if the count returned by dbcount is real or FALSE if the count returned by dbcount is not real.
    2849             :  * \sa DBCOUNT(), dbcount().
    2850             :  */
    2851             : BOOL
    2852           0 : dbiscount(DBPROCESS * dbproc)
    2853             : {
    2854           0 :         tdsdump_log(TDS_DBG_FUNC, "dbiscount(%p)\n", dbproc);
    2855           0 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    2856             : 
    2857           0 :         return dbproc->tds_socket && dbproc->tds_socket->rows_affected != TDS_NO_COUNT;
    2858             : }
    2859             : 
    2860             : /**
    2861             :  * \ingroup dblib_core
    2862             :  * \brief Get count of rows processed
    2863             :  *
    2864             :  *
    2865             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    2866             :  * \returns
    2867             :  *      - for insert/update/delete, count of rows affected.
    2868             :  *      - for select, count of rows returned, after all rows have been fetched.
    2869             :  * \sa DBCOUNT(), dbnextrow(), dbresults().
    2870             :  */
    2871             : DBINT
    2872        1294 : dbcount(DBPROCESS * dbproc)
    2873             : {
    2874        1294 :         tdsdump_log(TDS_DBG_FUNC, "dbcount(%p)\n", dbproc);
    2875        1294 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    2876             : 
    2877        1294 :         if (!dbproc->tds_socket || dbproc->tds_socket->rows_affected == TDS_NO_COUNT)
    2878             :                 return -1;
    2879         796 :         return (DBINT)dbproc->tds_socket->rows_affected;
    2880             : }
    2881             : 
    2882             : /**
    2883             :  * \ingroup dblib_core
    2884             :  * \brief Clear \a n rows from the row buffer.
    2885             :  * 
    2886             :  * 
    2887             :  * \param dbproc contains all information needed by db-lib to manage communications with the server. 
    2888             :  * \param n number of rows to remove, >= 0.
    2889             :  * \sa dbgetrow(), dbnextrow(), dbsetopt().
    2890             :  */
    2891             : void
    2892         200 : dbclrbuf(DBPROCESS * dbproc, DBINT n)
    2893             : {
    2894         200 :         tdsdump_log(TDS_DBG_FUNC, "dbclrbuf(%p, %d)\n", dbproc, n);
    2895         200 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    2896             : 
    2897         200 :         if (n <= 0)
    2898             :                 return;
    2899             : 
    2900         200 :         if (dbproc->dbopts[DBBUFFER].factive) {
    2901         160 :                 DBPROC_ROWBUF * buf = &(dbproc->row_buf);
    2902         160 :                 int count = buffer_count(buf);
    2903         160 :                 if (n >= count)
    2904         140 :                         n = count - 1;
    2905         160 :                 buffer_delete_rows(&(dbproc->row_buf), n);
    2906             :         }
    2907             : }
    2908             : 
    2909             : /**
    2910             :  * \ingroup dblib_core
    2911             :  * \brief Test whether or not a datatype can be converted to another datatype
    2912             :  * 
    2913             :  * \param srctype type converting from
    2914             :  * \param desttype type converting to
    2915             :  * \remarks dbwillconvert() lies sometimes.  Some datatypes \em should be convertible but aren't yet in our implementation.  
    2916             :  *          Legal unimplemented conversions return \em TRUE.  
    2917             :  * \retval TRUE convertible, or should be.
    2918             :  * \retval FAIL not convertible.  
    2919             :  * \sa dbaltbind(), dbbind(), dbconvert(), dbconvert_ps(), \c src/dblib/unittests/convert().c().
    2920             :  */
    2921             : DBBOOL
    2922       22594 : dbwillconvert(int srctype, int desttype)
    2923             : {
    2924       22594 :         tdsdump_log(TDS_DBG_FUNC, "dbwillconvert(%s, %s)\n", tds_prdatatype(srctype), tds_prdatatype(desttype));
    2925       22594 :         return tds_willconvert(srctype, desttype) ? TRUE : FALSE;
    2926             : }
    2927             : 
    2928             : static int
    2929        1448 : dblib_coltype(TDSCOLUMN *colinfo)
    2930             : {
    2931        1448 :         switch (colinfo->column_type) {
    2932             :         case SYBVARCHAR:
    2933             :                 return SYBCHAR;
    2934           0 :         case SYBVARBINARY:
    2935             :                 return SYBBINARY;
    2936           0 :         case SYBLONGCHAR:
    2937             :         case SYBUNITEXT:
    2938             :         case SYBMSXML:
    2939             :                 return SYBTEXT;
    2940             :         /* these types are handled by tds_get_conversion_type */
    2941             :         case SYBBITN:
    2942             :         case SYBDATEN:
    2943             :         case SYBDATETIMN:
    2944             :         case SYBFLTN:
    2945             :         case SYBINTN:
    2946             :         case SYBMONEYN:
    2947             :         case SYBTIMEN:
    2948             :         case SYBUINTN:
    2949             :         case SYBMSTABLE:
    2950             :                 break;
    2951             :         /* these types are supported */
    2952             :         case SYBCHAR:
    2953             :         case SYBINT1:
    2954             :         case SYBINT2:
    2955             :         case SYBINT4:
    2956             :         case SYBINT8:
    2957             :         case SYBFLT8:
    2958             :         case SYBDATETIME:
    2959             :         case SYBBIT:
    2960             :         case SYBTEXT:
    2961             :         case SYBNTEXT:
    2962             :         case SYBIMAGE:
    2963             :         case SYBMONEY4:
    2964             :         case SYBMONEY:
    2965             :         case SYBDATETIME4:
    2966             :         case SYBREAL:
    2967             :         case SYBBINARY:
    2968             :         case SYBVOID:
    2969             :         case SYBNUMERIC:
    2970             :         case SYBDECIMAL:
    2971             :         case SYBNVARCHAR:
    2972             :         case SYBDATE:
    2973             :         case SYBTIME:
    2974             :         case SYB5BIGDATETIME:
    2975             :         case SYB5BIGTIME:
    2976             :         case SYBMSDATE:
    2977             :         case SYBMSTIME:
    2978             :         case SYBMSDATETIME2:
    2979             :         case SYBMSDATETIMEOFFSET:
    2980             :                 break;
    2981             :         /* exclude cardinal types */
    2982             :         case XSYBVARBINARY:
    2983             :         case XSYBBINARY:
    2984             :         case XSYBNVARCHAR:
    2985             :         case XSYBVARCHAR:
    2986             :         case XSYBNCHAR:
    2987             :         case SYB5INT8:
    2988             :                 break;
    2989             : #if !ENABLE_EXTRA_CHECKS
    2990             :         /* TODO these should be handled in some ways */
    2991             :         case SYBUNIQUE: /* or SYBBLOB */
    2992             :         case SYBVARIANT:
    2993             :         case SYBMSUDT:
    2994             :         case SYBLONGBINARY:
    2995             :         case SYBUINT1:
    2996             :         case SYBUINT2:
    2997             :         case SYBUINT4:
    2998             :         case SYBUINT8:
    2999             :         case SYBINTERVAL:
    3000             :         case SYBSINT1:
    3001             :         case SYBXML:
    3002             :                 break;
    3003             : #endif
    3004             :         }
    3005         328 :         return tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    3006             : }
    3007             : 
    3008             : /**
    3009             :  * \ingroup dblib_core
    3010             :  * \brief Get the datatype of a regular result set column. 
    3011             :  * 
    3012             :  * 
    3013             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3014             :  * \param column Nth in the result set, starting from 1.
    3015             :  * \returns \c SYB* datetype token value, or zero if \a column out of range
    3016             :  * \sa dbcollen(), dbcolname(), dbdata(), dbdatlen(), dbnumcols(), dbprtype(), dbvarylen().
    3017             :  */
    3018             : int
    3019        1562 : dbcoltype(DBPROCESS * dbproc, int column)
    3020             : {
    3021             :         TDSCOLUMN *colinfo;
    3022             : 
    3023        1562 :         tdsdump_log(TDS_DBG_FUNC, "dbcoltype(%p, %d)\n", dbproc, column);
    3024             : 
    3025        1562 :         colinfo = dbcolptr(dbproc, column);
    3026        1562 :         if (!colinfo)
    3027             :                 return -1;
    3028             : 
    3029        1432 :         return dblib_coltype(colinfo);
    3030             : }
    3031             : 
    3032             : /**
    3033             :  * \ingroup dblib_core
    3034             :  * \brief Get user-defined datatype of a regular result column.
    3035             :  * 
    3036             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3037             :  * \param column Nth in the result set, starting from 1.
    3038             :  * \returns \c SYB* datetype token value, or -1 if \a column out of range
    3039             :  * \sa dbaltutype(), dbcoltype().
    3040             :  */
    3041             : int
    3042           0 : dbcolutype(DBPROCESS * dbproc, int column)
    3043             : {
    3044             :         TDSCOLUMN *colinfo;
    3045             : 
    3046           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcolutype(%p, %d)\n", dbproc, column);
    3047             : 
    3048           0 :         colinfo = dbcolptr(dbproc, column);
    3049           0 :         if (!colinfo)
    3050             :                 return -1;
    3051             : 
    3052           0 :         return colinfo->column_usertype;
    3053             : }
    3054             : 
    3055             : /**
    3056             :  * \ingroup dblib_core
    3057             :  * \brief Get precision and scale information for a regular result column.
    3058             :  * 
    3059             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3060             :  * \param column Nth in the result set, starting from 1.
    3061             :  * \return Pointer to a DBTYPEINFO structure .  NULL \a column is out of range.
    3062             :  * \sa dbcollen(), dbcolname(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols(), dbprtype(), dbvarylen().
    3063             :  */
    3064             : DBTYPEINFO *
    3065           0 : dbcoltypeinfo(DBPROCESS * dbproc, int column)
    3066             : {
    3067             :         /* moved typeinfo from static into dbproc structure to make thread safe.  (mlilback 11/7/01) */
    3068             :         TDSCOLUMN *colinfo;
    3069             : 
    3070           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcoltypeinfo(%p, %d)\n", dbproc, column);
    3071             : 
    3072           0 :         colinfo = dbcolptr(dbproc, column);
    3073           0 :         if (!colinfo)
    3074             :                 return NULL;
    3075             : 
    3076           0 :         dbproc->typeinfo.precision = colinfo->column_prec;
    3077           0 :         dbproc->typeinfo.scale = colinfo->column_scale;
    3078           0 :         return &dbproc->typeinfo;
    3079             : }
    3080             : 
    3081             : /**
    3082             :  * \brief Get a bunch of column attributes with a single call (Microsoft-compatibility feature).  
    3083             :  * 
    3084             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3085             :  * \param type must be CI_REGULAR or CI_ALTERNATE (CI_CURSOR is defined by the vendor, but is not yet implemented).
    3086             :  * \param column Nth in the result set, starting from 1.
    3087             :  * \param computeid (ignored)
    3088             :  * \param pdbcol address of structure to be populated by this function.  
    3089             :  * \return SUCCEED or FAIL. 
    3090             :  * \sa dbcolbrowse(), dbqual(), dbtabbrowse(), dbtabcount(), dbtabname(), dbtabsource(), dbtsnewlen(), dbtsnewval(), dbtsput().
    3091             :  * \todo Support cursor rows. 
    3092             :  */
    3093             : RETCODE
    3094           0 : dbcolinfo(DBPROCESS *dbproc, CI_TYPE type, DBINT column, DBINT computeid, DBCOL *pdbcol)
    3095             : {
    3096             :         DBTYPEINFO *ps;
    3097             :         TDSCOMPUTEINFO *info;
    3098             :         TDSCOLUMN *colinfo;
    3099             :         unsigned int i;
    3100             : 
    3101           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcolinfo(%p, %d, %d, %d, %p)\n", dbproc, type, column, computeid, pdbcol);
    3102             : 
    3103           0 :         colinfo = dbcolptr(dbproc, column);
    3104           0 :         if (!colinfo)
    3105             :                 return FAIL;
    3106             :                 
    3107           0 :         CHECK_NULP(pdbcol, "dbcolinfo", 5, FAIL);
    3108             : 
    3109           0 :         if (type == CI_REGULAR) {
    3110             : 
    3111           0 :                 strlcpy(pdbcol->Name, dbcolname(dbproc, column), sizeof(pdbcol->Name));
    3112           0 :                 strlcpy(pdbcol->ActualName, dbcolname(dbproc, column), sizeof(pdbcol->ActualName));
    3113           0 :                 strlcpy(pdbcol->TableName, tds_dstr_cstr(&colinfo->table_name), sizeof(pdbcol->TableName));
    3114             : 
    3115           0 :                 pdbcol->Type = dbcoltype(dbproc, column);
    3116           0 :                 pdbcol->UserType = dbcolutype(dbproc, column);
    3117           0 :                 pdbcol->MaxLength = dbcollen(dbproc, column);
    3118           0 :                 pdbcol->Null = _dbnullable(dbproc, column);
    3119           0 :                 pdbcol->VarLength = dbvarylen(dbproc, column);
    3120             : 
    3121           0 :                 ps = dbcoltypeinfo(dbproc, column);
    3122             : 
    3123           0 :                 if( ps ) {
    3124           0 :                         pdbcol->Precision = ps->precision;
    3125           0 :                         pdbcol->Scale = ps->scale;
    3126             :                 }
    3127             : 
    3128           0 :                 pdbcol->Updatable = colinfo->column_writeable ? TRUE : FALSE;
    3129           0 :                 pdbcol->Identity = colinfo->column_identity ? TRUE : FALSE;
    3130             : 
    3131           0 :                 return SUCCEED;
    3132             :         }
    3133             :   
    3134           0 :         if (type == CI_ALTERNATE) {
    3135             : 
    3136           0 :                 if (computeid == 0)
    3137             :                         return FAIL;
    3138             : 
    3139           0 :                 for (i = 0;; ++i) {
    3140           0 :                         if (i >= dbproc->tds_socket->num_comp_info)
    3141             :                                 return FAIL;
    3142           0 :                         info = dbproc->tds_socket->comp_info[i];
    3143           0 :                         if (info->computeid == computeid)
    3144             :                                 break;
    3145             :                 }
    3146             : 
    3147             :                 /* if either the compute id or the column number are invalid, return -1 */
    3148           0 :                 if (column < 1 || column > info->num_cols)
    3149             :                         return FAIL;
    3150             : 
    3151           0 :                 colinfo = info->columns[column - 1];
    3152             : 
    3153           0 :                 strlcpy(pdbcol->Name, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->Name));
    3154           0 :                 strlcpy(pdbcol->ActualName, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->ActualName));
    3155           0 :                 strlcpy(pdbcol->TableName, tds_dstr_cstr(&colinfo->table_name), sizeof(pdbcol->TableName));
    3156             : 
    3157           0 :                 pdbcol->Type = dbalttype(dbproc, computeid, column);
    3158           0 :                 pdbcol->UserType = dbaltutype(dbproc, computeid, column);
    3159           0 :                 pdbcol->MaxLength = dbaltlen(dbproc, computeid, column);
    3160           0 :                 if (colinfo->column_nullable) 
    3161           0 :                         pdbcol->Null = TRUE;
    3162             :                 else
    3163           0 :                         pdbcol->Null = FALSE;
    3164             : 
    3165           0 :                 pdbcol->VarLength = FALSE;
    3166             : 
    3167           0 :                 if (colinfo->column_nullable
    3168           0 :                     || is_nullable_type(colinfo->column_type))
    3169           0 :                         pdbcol->VarLength = TRUE;
    3170             : 
    3171           0 :                 pdbcol->Precision = colinfo->column_prec;
    3172           0 :                 pdbcol->Scale = colinfo->column_scale;
    3173             :   
    3174           0 :                 pdbcol->Updatable = colinfo->column_writeable ? TRUE : FALSE ;
    3175           0 :                 pdbcol->Identity = colinfo->column_identity ? TRUE : FALSE ;
    3176             : 
    3177           0 :                 return SUCCEED;
    3178             :         }
    3179             : 
    3180             :         return FAIL;
    3181             : }
    3182             :  
    3183             : /**
    3184             :  * \ingroup dblib_core
    3185             :  * \brief Get base database column name for a result set column.  
    3186             :  * 
    3187             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3188             :  * \param column Nth in the result set, starting from 1.
    3189             :  * \return pointer to ASCII null-terminated string, the name of the column. On error, NULL.
    3190             :  * \sa dbcolbrowse(), dbqual(), dbtabbrowse(), dbtabcount(), dbtabname(), dbtabsource(), dbtsnewlen(), dbtsnewval(), dbtsput().
    3191             :  */
    3192             : char *
    3193           0 : dbcolsource(DBPROCESS * dbproc, int column)
    3194             : {
    3195             :         TDSCOLUMN *colinfo;
    3196             : 
    3197           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcolsource(%p, %d)\n", dbproc, column);
    3198             : 
    3199           0 :         colinfo = dbcolptr(dbproc, column);
    3200           0 :         if (!colinfo)
    3201             :                 return NULL;
    3202             : 
    3203           0 :         return tds_dstr_buf(tds_dstr_isempty(&colinfo->table_column_name) ?
    3204             :                 &colinfo->column_name :
    3205             :                 &colinfo->table_column_name);
    3206             : }
    3207             : 
    3208             : /**
    3209             :  * \ingroup dblib_core
    3210             :  * \brief Get size of a regular result column.
    3211             :  * 
    3212             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3213             :  * \param column Nth in the result set, starting from 1.
    3214             :  * \return size of the column (not of data in any particular row).  On error, -1.
    3215             :  * \sa dbcolname(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
    3216             :  */
    3217             : DBINT
    3218           0 : dbcollen(DBPROCESS * dbproc, int column)
    3219             : {
    3220             :         TDSCOLUMN *colinfo;
    3221             : 
    3222           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcollen(%p, %d)\n", dbproc, column);
    3223             : 
    3224           0 :         colinfo = dbcolptr(dbproc, column);
    3225           0 :         if (!colinfo)
    3226             :                 return -1;
    3227             : 
    3228           0 :         return colinfo->column_size;
    3229             : }
    3230             : 
    3231             : /**
    3232             :  * \ingroup dblib_core
    3233             :  * \brief Get size of a result column needed to print column.
    3234             :  *
    3235             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3236             :  * \param column Nth in the result set, starting from 1.
    3237             :  * \return size of the column in characters (not of data in any particular row).  On error, -1.
    3238             :  * \sa dbcolname(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
    3239             :  */
    3240             : DBINT
    3241           0 : dbprcollen(DBPROCESS * dbproc, int column)
    3242             : {
    3243             :         TDSCOLUMN *colinfo;
    3244             : 
    3245           0 :         tdsdump_log(TDS_DBG_FUNC, "dbprcollen(%p, %d)\n", dbproc, column);
    3246             : 
    3247           0 :         colinfo = dbcolptr(dbproc, column);
    3248           0 :         if (!colinfo)
    3249             :                 return 0;
    3250             : 
    3251           0 :         return _get_printable_size(colinfo);
    3252             : }
    3253             : 
    3254             : 
    3255             : /* dbvarylen(), pkleef@openlinksw.com 01/21/02 */
    3256             : /**
    3257             :  * \ingroup dblib_core
    3258             :  * \brief Determine whether a column can vary in size.
    3259             :  * 
    3260             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3261             :  * \param column Nth in the result set, starting from 1.
    3262             :  * \retval TRUE datatype of column can vary in size, or is nullable. 
    3263             :  * \retval FALSE datatype of column is fixed and is not nullable. 
    3264             :  * \sa dbcollen(), dbcolname(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols(), dbprtype().
    3265             :  */
    3266             : DBINT
    3267        1580 : dbvarylen(DBPROCESS * dbproc, int column)
    3268             : {
    3269             :         TDSCOLUMN *colinfo;
    3270             : 
    3271        1580 :         tdsdump_log(TDS_DBG_FUNC, "dbvarylen(%p, %d)\n", dbproc, column);
    3272             : 
    3273        1580 :         colinfo = dbcolptr(dbproc, column);
    3274        1580 :         if (!colinfo)
    3275             :                 return FALSE;
    3276             : 
    3277        1580 :         if (colinfo->column_nullable)
    3278             :                 return TRUE;
    3279             : 
    3280        1240 :         switch (colinfo->column_type) {
    3281             :                 /* variable length fields */
    3282             :         case SYBNVARCHAR:
    3283             :         case SYBVARBINARY:
    3284             :         case SYBVARCHAR:
    3285             :         case XSYBVARCHAR:
    3286             :         case XSYBNVARCHAR:
    3287             :         case XSYBVARBINARY:
    3288             :                 return TRUE;
    3289             : 
    3290             :                 /* types that can be null */
    3291             :         case SYBBITN:
    3292             :         case SYBDATETIMN:
    3293             :         case SYBDECIMAL:
    3294             :         case SYBFLTN:
    3295             :         case SYBINTN:
    3296             :         case SYBMONEYN:
    3297             :         case SYBNUMERIC:
    3298             :         case SYBDATEN:
    3299             :         case SYBTIMEN:
    3300             :         case SYBUINTN:
    3301             :         case SYB5BIGDATETIME:
    3302             :         case SYB5BIGTIME:
    3303             :         case SYBMSDATE:
    3304             :         case SYBMSTIME:
    3305             :         case SYBMSDATETIME2:
    3306             :         case SYBMSDATETIMEOFFSET:
    3307             :         case SYBVARIANT:
    3308             :         case SYBUNIQUE:
    3309             :                 return TRUE;
    3310             : 
    3311             :                 /* blob types */
    3312             :         case SYBIMAGE:
    3313             :         case SYBNTEXT:
    3314             :         case SYBTEXT:
    3315             :         case SYBLONGBINARY:
    3316             :         /* case SYBBLOB: */ /* same as SYBUNIQUE */
    3317             :         case SYB5INT8:
    3318             :         case SYBUNITEXT:
    3319             :         case SYBXML:
    3320             :         case SYBMSUDT:
    3321             :         case SYBMSXML:
    3322             :                 return TRUE;
    3323             :         case SYBMSTABLE:
    3324             :                 return TRUE;
    3325             : 
    3326           0 :         case SYBLONGCHAR:
    3327             :         /* case XSYBCHAR: */ /* same as SYBLONGCHAR */
    3328           0 :                 if (colinfo->column_varint_size >= 4)
    3329             :                         return TRUE;
    3330             :                 break;
    3331             : 
    3332             :                 /* with defined size*/
    3333             :         case SYBCHAR:
    3334             :         case SYBBINARY:
    3335             :         case XSYBNCHAR:
    3336             :         case XSYBBINARY:
    3337             :                 break;
    3338             : 
    3339             :                 /* fixed */
    3340             :         case SYBINT1:
    3341             :         case SYBINT2:
    3342             :         case SYBINT4:
    3343             :         case SYBFLT8:
    3344             :         case SYBDATETIME:
    3345             :         case SYBBIT:
    3346             :         case SYBMONEY4:
    3347             :         case SYBMONEY:
    3348             :         case SYBDATETIME4:
    3349             :         case SYBREAL:
    3350             :         case SYBINT8:
    3351             :         case SYBUINT1:
    3352             :         case SYBUINT2:
    3353             :         case SYBUINT4:
    3354             :         case SYBUINT8:
    3355             :         case SYBSINT1:
    3356             :         case SYBTIME:
    3357             :         case SYBDATE:
    3358             :         case SYBVOID:
    3359             :         case SYBINTERVAL:
    3360             :                 break;
    3361             :         }
    3362         680 :         return FALSE;
    3363             : }
    3364             : 
    3365             : /**
    3366             :  * \ingroup dblib_core
    3367             :  * \brief   Get size of current row's data in a regular result column.  
    3368             :  * 
    3369             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3370             :  * \param column Nth in the result set, starting from 1.
    3371             :  * \return size of the data, in bytes.
    3372             :  * \sa dbcollen(), dbcolname(), dbcoltype(), dbdata(), dbnumcols().
    3373             :  */
    3374             : DBINT
    3375        2344 : dbdatlen(DBPROCESS * dbproc, int column)
    3376             : {
    3377             :         DBINT len;
    3378             :         TDSCOLUMN *colinfo;
    3379             : 
    3380        2344 :         tdsdump_log(TDS_DBG_FUNC, "dbdatlen(%p, %d)\n", dbproc, column);
    3381             : 
    3382        2344 :         colinfo = dbcolptr(dbproc, column);
    3383        2344 :         if (!colinfo)
    3384             :                 return -1;      
    3385             : 
    3386        2344 :         len = TDS_MAX(colinfo->column_cur_size, 0);
    3387             : 
    3388        2344 :         tdsdump_log(TDS_DBG_FUNC, "dbdatlen() type = %d, len= %d\n", colinfo->column_type, len);
    3389             : 
    3390             :         return len;
    3391             : }
    3392             : 
    3393             : /**
    3394             :  * \ingroup dblib_core
    3395             :  * \brief Get address of data in a regular result column.
    3396             :  * 
    3397             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3398             :  * \param column Nth in the result set, starting from 1.
    3399             :  * \return pointer the data, or NULL if data are NULL, or if \a column is out of range.  
    3400             :  * \sa dbbind(), dbcollen(), dbcolname(), dbcoltype(), dbdatlen(), dbnumcols().
    3401             :  */
    3402             : BYTE *
    3403        3002 : dbdata(DBPROCESS * dbproc, int column)
    3404             : {
    3405        3002 :         tdsdump_log(TDS_DBG_FUNC, "dbdata(%p, %d)\n", dbproc, column);
    3406             : 
    3407        6004 :         return _dbcoldata(dbcolptr(dbproc, column));
    3408             : }
    3409             : 
    3410             : /** \internal
    3411             :  * \ingroup dblib_internal
    3412             :  * \brief Return data from a column
    3413             :  *
    3414             :  * \param colinfo contains information on a result column.
    3415             :  * \return pointer to the data, or NULL if data are NULL
    3416             :  * \sa dbdata(), dbretdata()
    3417             :  */
    3418             : static BYTE *
    3419             : _dbcoldata(TDSCOLUMN *colinfo)
    3420             : {
    3421             :         BYTE *res;
    3422             :         static const BYTE empty[1] = { 0 };
    3423             : 
    3424        3072 :         if (!colinfo || colinfo->column_cur_size < 0)
    3425             :                 return NULL;
    3426             : 
    3427        2528 :         res = colinfo->column_data;
    3428        2528 :         if (is_blob_col(colinfo))
    3429         274 :                 res = (BYTE *) ((TDSBLOB *) res)->textvalue;
    3430        2528 :         if (!res)
    3431             :                 return (BYTE *) empty;
    3432             :         return res;
    3433             : }
    3434             : 
    3435             : /**
    3436             :  * \ingroup dblib_core
    3437             :  * \brief Cancel the current command batch.
    3438             :  * 
    3439             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3440             :  * \retval SUCCEED always.
    3441             :  * \sa dbcanquery(), dbnextrow(), dbresults(), dbsetinterrupt(), dbsqlexec(), dbsqlok(), dbsqlsend().
    3442             :  * \todo Check for failure and return accordingly.
    3443             :  */
    3444             : RETCODE
    3445       10320 : dbcancel(DBPROCESS * dbproc)
    3446             : {
    3447             :         TDSSOCKET *tds;
    3448             : 
    3449       10320 :         tdsdump_log(TDS_DBG_FUNC, "dbcancel(%p)\n", dbproc);
    3450       10320 :         CHECK_CONN(FAIL);
    3451             : 
    3452       10320 :         tds = dbproc->tds_socket;
    3453             : 
    3454       10320 :         tds_send_cancel(tds);
    3455       10320 :         tds_process_cancel(tds);
    3456             : 
    3457       10320 :         return SUCCEED;
    3458             : }
    3459             : 
    3460             : /**
    3461             :  * \ingroup dblib_core
    3462             :  * \brief Determine size buffer required to hold the results returned by dbsprhead(), dbsprline(), and  dbspr1row().
    3463             :  * 
    3464             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3465             :  * \return size of buffer requirement, in bytes.  
    3466             :  * \remarks An esoteric function.
    3467             :  * \sa dbprhead(), dbprrow(), dbspr1row(), dbsprhead(), dbsprline().
    3468             :  */
    3469             : DBINT
    3470           0 : dbspr1rowlen(DBPROCESS * dbproc)
    3471             : {
    3472             :         TDSSOCKET *tds;
    3473           0 :         int col, len = 0;
    3474             : 
    3475           0 :         tdsdump_log(TDS_DBG_FUNC, "dbspr1rowlen(%p)\n", dbproc);
    3476           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    3477           0 :         CHECK_PARAMETER(dbproc->tds_socket, SYBEDDNE, 0);
    3478             : 
    3479             :         tds = dbproc->tds_socket;
    3480             :         
    3481           0 :         for (col = 0; col < tds->res_info->num_cols; col++) {
    3482           0 :                 TDSCOLUMN *colinfo = tds->res_info->columns[col];
    3483           0 :                 int collen = _get_printable_size(colinfo);
    3484           0 :                 int namlen = (int) tds_dstr_len(&colinfo->column_name);
    3485             : 
    3486           0 :                 len += TDS_MAX(collen, namlen);
    3487             : 
    3488           0 :                 if (col > 0)         /* allow for the space between columns */
    3489           0 :                         len += dbstring_length(dbproc->dbopts[DBPRCOLSEP].param);
    3490             :         }
    3491             :         
    3492           0 :         return ++len;   /* allow for the nul */
    3493             : }
    3494             : 
    3495             : /**
    3496             :  * \ingroup dblib_core
    3497             :  * \brief Print a regular result row to a buffer.  
    3498             :  * 
    3499             :  * Fills a buffer with one data row, represented as a null-terminated ASCII string.  Helpful for debugging.  
    3500             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3501             :  * \param buffer \em output: Address of a buffer to hold ASCII null-terminated string.
    3502             :  * \param buf_len size of \a buffer, in bytes. 
    3503             :  * \retval SUCCEED on success.
    3504             :  * \retval FAIL trouble encountered.  
    3505             :  * \sa dbclropt(), dbisopt(), dbprhead(), dbprrow(), dbspr1rowlen(), dbsprhead(), dbsprline().
    3506             :  */
    3507             : RETCODE
    3508           0 : dbspr1row(DBPROCESS * dbproc, char *buffer, DBINT buf_len)
    3509             : {
    3510             :         TDSSOCKET *tds;
    3511             :         TDSDATEREC when;
    3512             :         int i, c, col;
    3513             :         DBINT len;
    3514             : 
    3515           0 :         tdsdump_log(TDS_DBG_FUNC, "dbspr1row(%p, %s, %d)\n", dbproc, buffer, buf_len);
    3516           0 :         CHECK_CONN(FAIL);
    3517           0 :         CHECK_NULP(buffer, "dbspr1row", 2, FAIL);
    3518             : 
    3519             :         if (!dbproc->tds_socket)
    3520             :                 return FAIL;
    3521             : 
    3522             :         tds = dbproc->tds_socket;
    3523             : 
    3524           0 :         for (col = 0; col < tds->res_info->num_cols; col++) {
    3525             :                 size_t padlen, collen, namlen;
    3526           0 :                 TDSCOLUMN *colinfo = tds->res_info->columns[col];
    3527           0 :                 if (colinfo->column_cur_size < 0) {
    3528           0 :                         len = 4;
    3529           0 :                         if (buf_len <= len) {
    3530             :                                 return FAIL;
    3531             :                         }
    3532           0 :                         strcpy(buffer, "NULL");
    3533             :                 } else {
    3534             :                         TDS_SERVER_TYPE desttype, srctype;
    3535             : 
    3536           0 :                         desttype = dblib_bound_type(STRINGBIND);
    3537           0 :                         srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    3538           0 :                         if (is_datetime_type(srctype)) {
    3539           0 :                                 tds_datecrack(srctype, dbdata(dbproc, col + 1), &when);
    3540           0 :                                 len = (int)tds_strftime(buffer, buf_len, STD_DATETIME_FMT, &when, 3);
    3541             :                         } else {
    3542           0 :                                 len = dbconvert(dbproc, srctype, dbdata(dbproc, col + 1), dbdatlen(dbproc, col + 1), 
    3543             :                                                 desttype, (BYTE *) buffer, buf_len);
    3544             :                         }
    3545           0 :                         if (len == -1) {
    3546             :                                 return FAIL;
    3547             :                         }
    3548             :                 }
    3549           0 :                 buffer += len;
    3550           0 :                 buf_len -= len;
    3551           0 :                 collen = _get_printable_size(colinfo);
    3552           0 :                 namlen = tds_dstr_len(&colinfo->column_name);
    3553           0 :                 padlen = TDS_MAX(collen, namlen) - len;
    3554           0 :                 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) == -1) {
    3555           0 :                         c = ' ';
    3556             :                 }
    3557           0 :                 for (; padlen > 0; padlen--) {
    3558           0 :                         if (buf_len < 1) {
    3559             :                                 return FAIL;
    3560             :                         }
    3561           0 :                         *buffer++ = c;
    3562           0 :                         buf_len--;
    3563             :                 }
    3564           0 :                 if ((col + 1) < tds->res_info->num_cols) {
    3565             :                         i = 0;
    3566           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    3567           0 :                                 if (buf_len < 1) {
    3568             :                                         return FAIL;
    3569             :                                 }
    3570           0 :                                 *buffer++ = c;
    3571           0 :                                 buf_len--;
    3572           0 :                                 i++;
    3573             :                         }
    3574             :                 }
    3575             :         }
    3576           0 :         if (buf_len < 1) {
    3577             :                 return FAIL;
    3578             :         }
    3579           0 :         *buffer = '\0';
    3580           0 :         return SUCCEED;
    3581             : }
    3582             : 
    3583             : /**
    3584             :  * \ingroup dblib_core
    3585             :  * \brief Print a result set to stdout. 
    3586             :  * 
    3587             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3588             :  * \sa dbbind(), dbnextrow(), dbprhead(), dbresults(), dbspr1row(), dbsprhead(), dbsprline(). 
    3589             :  */
    3590             : RETCODE
    3591          60 : dbprrow(DBPROCESS * dbproc)
    3592             : {
    3593             :         TDSCOLUMN *colinfo;
    3594             :         TDSRESULTINFO *resinfo;
    3595             :         TDSSOCKET *tds;
    3596             :         int i, col;
    3597             :         size_t collen, namlen, len;
    3598             :         char dest[8192];
    3599             :         int desttype, srctype;
    3600             :         TDSDATEREC when;
    3601             :         STATUS status;
    3602             :         ptrdiff_t padlen;
    3603             :         int c;
    3604             :         int selcol;
    3605             :         int linechar;
    3606             :         int op;
    3607             :         const char *opname, *p;
    3608             : 
    3609             :         /* these are for compute rows */
    3610             :         DBINT computeid, num_cols, colid;
    3611          60 :         TDS_SMALLINT *col_printlens = NULL;
    3612             : 
    3613          60 :         tdsdump_log(TDS_DBG_FUNC, "dbprrow(%p)\n", dbproc);
    3614          60 :         CHECK_CONN(FAIL);
    3615             : 
    3616             :         tds = dbproc->tds_socket;
    3617             : 
    3618         410 :         while ((status = dbnextrow(dbproc)) != NO_MORE_ROWS) {
    3619             : 
    3620         350 :                 if (status == FAIL) {
    3621           0 :                         free(col_printlens);
    3622           0 :                         return FAIL;
    3623             :                 }
    3624             : 
    3625         350 :                 if (status == REG_ROW) {
    3626             : 
    3627         350 :                         resinfo = tds->res_info;
    3628             : 
    3629         350 :                         if (col_printlens == NULL) {
    3630          20 :                                 if ((col_printlens = tds_new0(TDS_SMALLINT, resinfo->num_cols)) == NULL) {
    3631           0 :                                         dbperror(dbproc, SYBEMEM, errno);
    3632           0 :                                         return FAIL;
    3633             :                                 }
    3634             :                         }
    3635             : 
    3636        1750 :                         for (col = 0; col < resinfo->num_cols; col++) {
    3637        1400 :                                 colinfo = resinfo->columns[col];
    3638        1400 :                                 if (colinfo->column_cur_size < 0) {
    3639          60 :                                         len = 4;
    3640          60 :                                         strcpy(dest, "NULL");
    3641             :                                 } else {
    3642        1340 :                                         desttype = dblib_bound_type(STRINGBIND);
    3643        1340 :                                         srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    3644        1340 :                                         if (is_datetime_type(srctype)) {
    3645           0 :                                                 tds_datecrack(srctype, dbdata(dbproc, col + 1), &when);
    3646           0 :                                                 len = (int)tds_strftime(dest, sizeof(dest), STD_DATETIME_FMT, &when, 3);
    3647             :                                         } else {
    3648        1340 :                                                 len = dbconvert(dbproc, srctype, dbdata(dbproc, col + 1), dbdatlen(dbproc, col + 1),
    3649             :                                                                 desttype, (BYTE *) dest, sizeof(dest));
    3650             :                                         }
    3651             :                                 }
    3652             : 
    3653        1400 :                                 p = memchr(dest, '\0', len);
    3654        1400 :                                 fwrite(dest, 1, p == NULL ? len : (p - dest), stdout);
    3655        1400 :                                 collen = _get_printable_size(colinfo);
    3656        2800 :                                 namlen = tds_dstr_len(&colinfo->column_name);
    3657        1400 :                                 padlen = TDS_MAX(collen, namlen) - len;
    3658             : 
    3659        2800 :                                 c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0);
    3660       10618 :                                 for (; c > -1 && padlen > 0; padlen--) {
    3661        9218 :                                         putchar(c);
    3662             :                                 }
    3663             : 
    3664        1400 :                                 if ((col + 1) < resinfo->num_cols) {
    3665             :                                         i = 0;
    3666        4200 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3667             :                                                 putchar(c);
    3668             :                                         }
    3669             :                                 }
    3670        1400 :                                 col_printlens[col] = collen;
    3671             :                         }
    3672             :                         i = 0;
    3673        1400 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3674             :                                 putchar(c);
    3675             :                         }
    3676             : 
    3677           0 :                 } else if (col_printlens == NULL) {
    3678             :                         return FAIL;
    3679             :                 } else {
    3680             : 
    3681             :                         computeid = status;
    3682             : 
    3683           0 :                         for (i = 0;; ++i) {
    3684           0 :                                 if ((TDS_UINT) i >= tds->num_comp_info) {
    3685           0 :                                         free(col_printlens);
    3686           0 :                                         return FAIL;
    3687             :                                 }
    3688           0 :                                 resinfo = tds->comp_info[i];
    3689           0 :                                 if (resinfo->computeid == computeid)
    3690             :                                         break;
    3691             :                         }
    3692             : 
    3693           0 :                         num_cols = dbnumalts(dbproc, computeid);
    3694           0 :                         tdsdump_log(TDS_DBG_FUNC, "dbprrow num compute cols = %d\n", num_cols);
    3695             : 
    3696             :                         i = 0;
    3697           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3698             :                                 putchar(c);
    3699             :                         }
    3700           0 :                         for (selcol = col = 1; col <= num_cols; col++) {
    3701           0 :                                 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
    3702           0 :                                 colid = dbaltcolid(dbproc, computeid, col);
    3703             :                                 /*
    3704             :                                  * The pad character is pointed to by dbopts[DBPRPAD].param.  If that pointer 
    3705             :                                  * is NULL -- meaning padding is turned off -- dbstring_getchar returns -1. 
    3706             :                                  */
    3707           0 :                                 while (selcol < colid) {
    3708           0 :                                         for (i = 0; i < col_printlens[selcol - 1]; i++) {
    3709           0 :                                                 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) >= 0)
    3710             :                                                         putchar(c); 
    3711             :                                         }
    3712           0 :                                         selcol++;
    3713           0 :                                         i = 0;
    3714           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3715             :                                                 putchar(c);
    3716             :                                         }
    3717             :                                 }
    3718           0 :                                 op = dbaltop(dbproc, computeid, col);
    3719           0 :                                 opname = dbprtype(op);
    3720           0 :                                 printf("%s", opname);
    3721           0 :                                 for (i = 0; i < (col_printlens[selcol - 1] - (int) strlen(opname)); i++) {
    3722           0 :                                         if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) >= 0)
    3723             :                                                 putchar(c); 
    3724             :                                 }
    3725           0 :                                 selcol++;
    3726           0 :                                 if ((colid + 1) < num_cols) {
    3727             :                                         i = 0;
    3728           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3729             :                                                 putchar(c);
    3730             :                                         }
    3731             :                                 }
    3732             :                         }
    3733             :                         i = 0;
    3734           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3735             :                                 putchar(c);
    3736             :                         }
    3737             : 
    3738           0 :                         for (selcol = col = 1; col <= num_cols; col++) {
    3739           0 :                                 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
    3740           0 :                                 colid = dbaltcolid(dbproc, computeid, col);
    3741           0 :                                 while (selcol < colid) {
    3742           0 :                                         for (i = 0; i < col_printlens[selcol - 1]; i++) {
    3743           0 :                                                 putchar(' ');
    3744             :                                         }
    3745           0 :                                         selcol++;
    3746           0 :                                         i = 0;
    3747           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3748             :                                                 putchar(c);
    3749             :                                         }
    3750             :                                 }
    3751           0 :                                 if (resinfo->by_cols > 0) {
    3752             :                                         linechar = '-';
    3753             :                                 } else {
    3754           0 :                                         linechar = '=';
    3755             :                                 }
    3756           0 :                                 for (i = 0; i < col_printlens[colid - 1]; i++)
    3757           0 :                                         putchar(linechar);
    3758           0 :                                 selcol++;
    3759           0 :                                 if ((colid + 1) < num_cols) {
    3760             :                                         i = 0;
    3761           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3762             :                                                 putchar(c);
    3763             :                                         }
    3764             :                                 }
    3765             :                         }
    3766             :                         i = 0;
    3767           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3768             :                                 putchar(c);
    3769             :                         }
    3770             : 
    3771           0 :                         for (selcol = col = 1; col <= num_cols; col++) {
    3772           0 :                                 colinfo = resinfo->columns[col - 1];
    3773             : 
    3774           0 :                                 desttype = dblib_bound_type(STRINGBIND);
    3775           0 :                                 srctype = dbalttype(dbproc, computeid, col);
    3776             : 
    3777           0 :                                 if (is_datetime_type(srctype)) {
    3778           0 :                                         tds_datecrack(srctype, dbadata(dbproc, computeid, col), &when);
    3779           0 :                                         len = (int)tds_strftime(dest, sizeof(dest), STD_DATETIME_FMT, &when, 3);
    3780             :                                 } else {
    3781           0 :                                         len = dbconvert(dbproc, srctype, dbadata(dbproc, computeid, col), -1, desttype,
    3782             :                                                         (BYTE *) dest, sizeof(dest));
    3783             :                                 }
    3784             : 
    3785           0 :                                 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
    3786           0 :                                 colid = dbaltcolid(dbproc, computeid, col);
    3787           0 :                                 tdsdump_log(TDS_DBG_FUNC, "dbprrow select column = %d\n", colid);
    3788             : 
    3789           0 :                                 while (selcol < colid) {
    3790           0 :                                         for (i = 0; i < col_printlens[selcol - 1]; i++) {
    3791           0 :                                                 putchar(' ');
    3792             :                                         }
    3793           0 :                                         selcol++;
    3794           0 :                                         i = 0;
    3795           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3796             :                                                 putchar(c);
    3797             :                                         }
    3798             :                                 }
    3799           0 :                                 p = memchr(dest, '\0', len);
    3800           0 :                                 fwrite(dest, 1, p == NULL ? len : (p - dest), stdout);
    3801           0 :                                 collen = _get_printable_size(colinfo);
    3802           0 :                                 namlen = tds_dstr_len(&colinfo->column_name);
    3803           0 :                                 padlen = TDS_MAX(collen, namlen) - len;
    3804           0 :                                 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) == -1) {
    3805           0 :                                         c = ' ';
    3806             :                                 }
    3807           0 :                                 for (; padlen > 0; padlen--) {
    3808           0 :                                         putchar(c);
    3809             :                                 }
    3810           0 :                                 selcol++;
    3811           0 :                                 if ((colid + 1) < num_cols) {
    3812             :                                         i = 0;
    3813           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3814             :                                                 putchar(c);
    3815             :                                         }
    3816             :                                 }
    3817             :                         }
    3818             :                         i = 0;
    3819           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3820             :                                 putchar(c);
    3821             :                         }
    3822             :                 }
    3823             :         }
    3824             : 
    3825          60 :         free(col_printlens);
    3826             : 
    3827          60 :         return SUCCEED;
    3828             : }
    3829             : 
    3830             : static int
    3831        1560 : _get_printable_size(TDSCOLUMN * colinfo)
    3832             : {
    3833        1560 :         switch (tds_get_conversion_type(colinfo->column_type, colinfo->column_size)) {
    3834             :         case SYBUINT1:
    3835             :         case SYBINT1:
    3836             :                 return 3;
    3837           0 :         case SYBSINT1:
    3838           0 :                 return 4;
    3839           0 :         case SYBUINT2:
    3840           0 :                 return 5;
    3841         522 :         case SYBINT2:
    3842         522 :                 return 6;
    3843           0 :         case SYBUINT4:
    3844           0 :                 return 10;
    3845         258 :         case SYBINT4:
    3846         258 :                 return 11;
    3847           0 :         case SYBUINT8:
    3848           0 :                 return 20;
    3849           0 :         case SYBINT8:
    3850           0 :                 return 21;
    3851         780 :         case SYBVARCHAR:
    3852             :         case SYBCHAR:
    3853             :         case SYBTEXT:
    3854             :         case SYBNTEXT:
    3855             :         case SYBUNITEXT:
    3856             :         case SYBNVARCHAR:
    3857             :         case SYBLONGCHAR:
    3858         780 :                 return colinfo->column_size;
    3859           0 :         case SYBBINARY:
    3860             :         case SYBIMAGE:
    3861             :         case SYBLONGBINARY:
    3862             :         case SYBVARBINARY:
    3863           0 :                 return colinfo->column_size * 2u;
    3864           0 :         case SYBREAL:
    3865           0 :                 return 14;      /* FIX ME -- we do not track precision */
    3866           0 :         case SYBFLT8:
    3867           0 :                 return 24;      /* FIX ME -- we do not track precision */
    3868           0 :         case SYBMONEY4:
    3869           0 :                 return 12;
    3870           0 :         case SYBMONEY:
    3871           0 :                 return 22;
    3872           0 :         case SYB5BIGDATETIME:
    3873             :         case SYBDATETIME:
    3874             :         case SYBDATETIME4:
    3875           0 :                 return 26;
    3876           0 :         case SYBTIME:
    3877             :         case SYB5BIGTIME:
    3878           0 :                 return 15;
    3879           0 :         case SYBMSTIME:
    3880           0 :                 return 16;
    3881           0 :         case SYBDATE:
    3882             :         case SYBMSDATE:
    3883           0 :                 return 10;
    3884           0 :         case SYBMSDATETIME2:
    3885           0 :                 return 27;
    3886           0 :         case SYBMSDATETIMEOFFSET:
    3887           0 :                 return 33;
    3888           0 :         case SYBUNIQUE:
    3889           0 :                 return 36;
    3890           0 :         case SYBBIT:
    3891           0 :                 return 1;
    3892           0 :         case SYBNUMERIC:
    3893             :         case SYBDECIMAL:
    3894           0 :                 return colinfo->column_prec + 2;
    3895             :                 /* FIX ME -- not all types present */
    3896           0 :         case SYBVOID: /* or 4 bytes for "null" ? */
    3897             :         default:
    3898           0 :                 return 0;
    3899             :         }
    3900             : }
    3901             : 
    3902             : /**
    3903             :  * \ingroup dblib_core
    3904             :  * \brief Get formatted string for underlining dbsprhead() column names.  
    3905             :  * 
    3906             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3907             :  * \param buffer output buffer
    3908             :  * \param buf_len size of \a buffer
    3909             :  * \param line_char character to use to represent underlining.
    3910             :  * \retval SUCCEED \a buffer filled.
    3911             :  * \retval FAIL insufficient space in \a buffer, usually.
    3912             :  * \sa dbprhead(), dbprrow(), dbspr1row(), dbspr1rowlen(), dbsprhead(). 
    3913             :  */
    3914             : RETCODE
    3915           0 : dbsprline(DBPROCESS * dbproc, char *buffer, DBINT buf_len, DBCHAR line_char)
    3916             : {
    3917             :         TDSCOLUMN *colinfo;
    3918             :         TDSRESULTINFO *resinfo;
    3919             :         TDSSOCKET *tds;
    3920             :         size_t i, col, len, collen, namlen;
    3921             :         int c;
    3922             : 
    3923           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsprline(%p, %s, %d, '%c')\n", dbproc, buffer, buf_len, line_char);
    3924           0 :         CHECK_CONN(FAIL);
    3925           0 :         CHECK_NULP(buffer, "dbsprline", 2, FAIL);
    3926             : 
    3927           0 :         tds = dbproc->tds_socket;
    3928           0 :         resinfo = tds->res_info;
    3929             : 
    3930           0 :         for (col = 0; col < resinfo->num_cols; col++) {
    3931           0 :                 colinfo = resinfo->columns[col];
    3932           0 :                 collen = _get_printable_size(colinfo);
    3933           0 :                 namlen = tds_dstr_len(&colinfo->column_name);
    3934           0 :                 len = TDS_MAX(collen, namlen);
    3935           0 :                 for (i = 0; i < len; i++) {
    3936           0 :                         if (buf_len < 1) {
    3937             :                                 return FAIL;
    3938             :                         }
    3939           0 :                         *buffer++ = line_char;
    3940           0 :                         buf_len--;
    3941             :                 }
    3942           0 :                 if ((col + 1) < resinfo->num_cols) {
    3943             :                         i = 0;
    3944           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    3945           0 :                                 if (buf_len < 1) {
    3946             :                                         return FAIL;
    3947             :                                 }
    3948           0 :                                 *buffer++ = c;
    3949           0 :                                 buf_len--;
    3950           0 :                                 i++;
    3951             :                         }
    3952             :                 }
    3953             :         }
    3954           0 :         if (buf_len < 1) {
    3955             :                 return FAIL;
    3956             :         }
    3957           0 :         *buffer = '\0';
    3958           0 :         return SUCCEED;
    3959             : }
    3960             : 
    3961             : /**
    3962             :  * \ingroup dblib_core
    3963             :  * \brief Print result set headings to a buffer.
    3964             :  * 
    3965             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3966             :  * \param buffer output buffer
    3967             :  * \param buf_len size of \a buffer
    3968             :  * \retval SUCCEED \a buffer filled.
    3969             :  * \retval FAIL insufficient space in \a buffer, usually.
    3970             :  * \sa dbprhead(), dbprrow(), dbsetopt(), dbspr1row(), dbspr1rowlen(), dbsprline().
    3971             :  */
    3972             : RETCODE
    3973           0 : dbsprhead(DBPROCESS * dbproc, char *buffer, DBINT buf_len)
    3974             : {
    3975             :         TDSCOLUMN *colinfo;
    3976             :         TDSRESULTINFO *resinfo;
    3977             :         TDSSOCKET *tds;
    3978             :         int i, collen, namlen;
    3979             :         TDS_USMALLINT col;
    3980             :         int padlen;
    3981             :         int c;
    3982             : 
    3983           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsprhead(%p, %p, %d)\n", dbproc, buffer, buf_len);
    3984           0 :         CHECK_CONN(FAIL);
    3985           0 :         CHECK_NULP(buffer, "dbsprhead", 2, FAIL);
    3986             : 
    3987           0 :         tds = dbproc->tds_socket;
    3988           0 :         resinfo = tds->res_info;
    3989             : 
    3990           0 :         for (col = 0; col < resinfo->num_cols; col++) {
    3991           0 :                 colinfo = resinfo->columns[col];
    3992           0 :                 collen = _get_printable_size(colinfo);
    3993           0 :                 namlen = (int) tds_dstr_len(&colinfo->column_name);
    3994           0 :                 padlen = TDS_MAX(collen, namlen) - namlen;
    3995           0 :                 if (buf_len < namlen) {
    3996             :                         return FAIL;
    3997             :                 }
    3998           0 :                 memcpy(buffer, tds_dstr_cstr(&colinfo->column_name), namlen);
    3999           0 :                 buffer += namlen;
    4000           0 :                 buf_len -= namlen;
    4001           0 :                 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) == -1) {
    4002           0 :                         c = ' ';
    4003             :                 }
    4004           0 :                 for (; padlen > 0; padlen--) {
    4005           0 :                         if (buf_len < 1) {
    4006             :                                 return FAIL;
    4007             :                         }
    4008           0 :                         *buffer++ = c;
    4009           0 :                         buf_len--;
    4010             :                 }
    4011           0 :                 if ((col + 1) < resinfo->num_cols) {
    4012             :                         i = 0;
    4013           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    4014           0 :                                 if (buf_len < 1) {
    4015             :                                         return FAIL;
    4016             :                                 }
    4017           0 :                                 *buffer++ = c;
    4018           0 :                                 buf_len--;
    4019           0 :                                 i++;
    4020             :                         }
    4021             :                 }
    4022             :         }
    4023           0 :         if (buf_len < 1) {
    4024             :                 return FAIL;
    4025             :         }
    4026           0 :         *buffer = '\0';
    4027           0 :         return SUCCEED;
    4028             : }
    4029             : 
    4030             : /**
    4031             :  * \ingroup dblib_core
    4032             :  * \brief Print result set headings to stdout.
    4033             :  * 
    4034             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4035             :  * \sa 
    4036             :  */
    4037             : void
    4038          60 : dbprhead(DBPROCESS * dbproc)
    4039             : {
    4040             :         TDSCOLUMN *colinfo;
    4041             :         TDSRESULTINFO *resinfo;
    4042             :         TDSSOCKET *tds;
    4043             :         size_t i, col, len, collen, namlen;
    4044             :         size_t padlen;
    4045             :         int c;
    4046             : 
    4047          60 :         tdsdump_log(TDS_DBG_FUNC, "dbprhead(%p)\n", dbproc);
    4048          60 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    4049             : 
    4050          60 :         tds = dbproc->tds_socket;
    4051          60 :         resinfo = tds->res_info;
    4052          60 :         if (resinfo == NULL) {
    4053             :                 return;
    4054             :         }
    4055         100 :         for (col = 0; col < resinfo->num_cols; col++) {
    4056          80 :                 colinfo = resinfo->columns[col];
    4057          80 :                 collen = _get_printable_size(colinfo);
    4058         160 :                 namlen = tds_dstr_len(&colinfo->column_name);
    4059          80 :                 padlen = TDS_MAX(collen, namlen) - namlen;
    4060         160 :                 printf("%s", tds_dstr_cstr(&colinfo->column_name));
    4061             : 
    4062         160 :                 c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0);
    4063          80 :                 if (c == -1) {
    4064           0 :                         c = ' ';
    4065             :                 }
    4066         600 :                 for (; padlen > 0; padlen--) {
    4067         600 :                         putchar(c);
    4068             :                 }
    4069             : 
    4070          80 :                 if ((col + 1) < resinfo->num_cols) {
    4071             :                         i = 0;
    4072         240 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    4073          60 :                                 putchar(c);
    4074          60 :                                 i++;
    4075             :                         }
    4076             :                 }
    4077             :         }
    4078             :         i = 0;
    4079          80 :         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i)) != -1) {
    4080          20 :                 putchar(c);
    4081          20 :                 i++;
    4082             :         }
    4083         100 :         for (col = 0; col < resinfo->num_cols; col++) {
    4084          80 :                 colinfo = resinfo->columns[col];
    4085          80 :                 collen = _get_printable_size(colinfo);
    4086         160 :                 namlen = tds_dstr_len(&colinfo->column_name);
    4087          80 :                 len = TDS_MAX(collen, namlen);
    4088        1040 :                 for (i = 0; i < len; i++)
    4089         960 :                         putchar('-');
    4090          80 :                 if ((col + 1) < resinfo->num_cols) {
    4091             :                         i = 0;
    4092         240 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    4093          60 :                                 putchar(c);
    4094          60 :                                 i++;
    4095             :                         }
    4096             :                 }
    4097             :         }
    4098             :         i = 0;
    4099          80 :         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i)) != -1) {
    4100          20 :                 putchar(c);
    4101          20 :                 i++;
    4102             :         }
    4103             : }
    4104             : 
    4105             : /** \internal
    4106             :  * \ingroup dblib_internal
    4107             :  * \brief Indicate whether a query returned rows.
    4108             :  * 
    4109             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4110             :  * \sa DBROWS(), DBCMDROW(), dbnextrow(), dbresults(), DBROWTYPE().
    4111             :  */
    4112             : RETCODE
    4113         522 : dbrows(DBPROCESS * dbproc)
    4114             : {
    4115             :         TDSSOCKET *tds;
    4116             : 
    4117         522 :         tdsdump_log(TDS_DBG_FUNC, "dbrows(%p)\n", dbproc);
    4118         522 :         CHECK_CONN(FAIL);
    4119             : 
    4120         522 :         if (!(tds=dbproc->tds_socket))
    4121             :                 return FAIL;
    4122             : 
    4123         522 :         return (tds->res_info && tds->res_info->rows_exist)? SUCCEED : FAIL;
    4124             : }
    4125             : 
    4126             : #if defined(DBLIB_UNIMPLEMENTED)
    4127             : /**
    4128             :  * \ingroup dblib_core
    4129             :  * \brief Set the default character set for an application.
    4130             :  * 
    4131             :  * \param language ASCII null-terminated string.  
    4132             :  * \sa dbsetdeflang(), dbsetdefcharset(), dblogin(), dbopen().
    4133             :  * \retval SUCCEED Always.  
    4134             :  * \todo Unimplemented.
    4135             :  */
    4136             : RETCODE
    4137             : dbsetdeflang(char *language)
    4138             : {
    4139             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetdeflang(%s)\n", language);
    4140             :         CHECK_PARAMETER_NOPROC(language, SYBENULP);
    4141             :         return SUCCEED;
    4142             : }
    4143             : #endif
    4144             : 
    4145             : /**
    4146             :  * \ingroup dblib_core
    4147             :  * \brief Get TDS packet size for the connection.
    4148             :  * 
    4149             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4150             :  * \return TDS packet size, in bytes.  
    4151             :  * \sa DBSETLPACKET()
    4152             :  */
    4153             : int
    4154           0 : dbgetpacket(DBPROCESS * dbproc)
    4155             : {
    4156             :         TDSSOCKET *tds;
    4157             : 
    4158           0 :         tdsdump_log(TDS_DBG_FUNC, "dbgetpacket(%p)\n", dbproc);
    4159           0 :         CHECK_PARAMETER(dbproc, SYBENULL, TDS_DEF_BLKSZ);
    4160             : 
    4161           0 :         tds = dbproc->tds_socket;
    4162           0 :         if (!tds) {
    4163             :                 return TDS_DEF_BLKSZ;
    4164             :         } else {
    4165           0 :                 return tds->conn->env.block_size;
    4166             :         }
    4167             : }
    4168             : 
    4169             : /**
    4170             :  * \ingroup dblib_core
    4171             :  * \brief Set maximum simultaneous connections db-lib will open to the server.
    4172             :  * 
    4173             :  * \param maxprocs Limit for process.
    4174             :  * \retval SUCCEED Always.  
    4175             :  * \sa dbgetmaxprocs(), dbopen()
    4176             :  */
    4177             : RETCODE
    4178         250 : dbsetmaxprocs(int maxprocs)
    4179             : {
    4180             :         int i, j;
    4181             :         TDSSOCKET **old_list;
    4182             : 
    4183         250 :         tdsdump_log(TDS_DBG_FUNC, "UNTESTED dbsetmaxprocs(%d)\n", maxprocs);
    4184             : 
    4185             :         /* not too few elements */
    4186         250 :         if (maxprocs <= 0)
    4187             :                 return FAIL;
    4188             : 
    4189         250 :         tds_mutex_lock(&dblib_mutex);
    4190             : 
    4191         250 :         old_list = g_dblib_ctx.connection_list;
    4192             : 
    4193             :         /* "compress" array */
    4194     1024250 :         for (i = 0, j = 0; i < g_dblib_ctx.connection_list_size; ++i) {
    4195     1024000 :                 if (!old_list[i])
    4196     1024000 :                         continue;
    4197           0 :                 if (i != j) {
    4198           0 :                         old_list[j] = old_list[i];
    4199           0 :                         old_list[i] = NULL;
    4200             :                 }
    4201           0 :                 ++j;
    4202             :         }
    4203             :         /* do not restrict too much, j here contains minimun size */
    4204         250 :         if (maxprocs < j)
    4205           0 :                 maxprocs = j;
    4206             : 
    4207             :         /*
    4208             :          * Don't reallocate less memory.  
    4209             :          * If maxprocs is less than was initially allocated, just reduce the represented list size.  
    4210             :          * If larger, reallocate and copy.
    4211             :          * We probably should check for valid connections beyond the new max.
    4212             :          */
    4213         250 :         if (maxprocs <= g_dblib_ctx.connection_list_size) {
    4214         250 :                 g_dblib_ctx.connection_list_size_represented = maxprocs;
    4215         250 :                 tds_mutex_unlock(&dblib_mutex);
    4216         250 :                 return SUCCEED;
    4217             :         }
    4218             : 
    4219           0 :         g_dblib_ctx.connection_list = tds_new0(TDSSOCKET *, maxprocs);
    4220             : 
    4221           0 :         if (g_dblib_ctx.connection_list == NULL) {
    4222           0 :                 g_dblib_ctx.connection_list = old_list;
    4223           0 :                 tds_mutex_unlock(&dblib_mutex);
    4224           0 :                 dbperror(NULL, SYBEMEM, errno);
    4225           0 :                 return FAIL;
    4226             :         }
    4227             : 
    4228           0 :         for (i = 0; i < g_dblib_ctx.connection_list_size; i++) {
    4229           0 :                 g_dblib_ctx.connection_list[i] = old_list[i];
    4230             :         }
    4231             : 
    4232           0 :         g_dblib_ctx.connection_list_size = maxprocs;
    4233           0 :         g_dblib_ctx.connection_list_size_represented = maxprocs;
    4234             : 
    4235           0 :         tds_mutex_unlock(&dblib_mutex);
    4236             : 
    4237           0 :         free(old_list);
    4238             : 
    4239           0 :         return SUCCEED;
    4240             : }
    4241             : 
    4242             : /**
    4243             :  * \ingroup dblib_core
    4244             :  * \brief get maximum simultaneous connections db-lib will open to the server.
    4245             :  * 
    4246             :  * \return Current maximum.  
    4247             :  * \sa dbsetmaxprocs(), dbopen()
    4248             :  */
    4249             : int
    4250           0 : dbgetmaxprocs(void)
    4251             : {
    4252             :         int r;
    4253             : 
    4254           0 :         tdsdump_log(TDS_DBG_FUNC, "dbgetmaxprocs(void)\n");
    4255             : 
    4256           0 :         tds_mutex_lock(&dblib_mutex);
    4257           0 :         r = g_dblib_ctx.connection_list_size_represented;
    4258           0 :         tds_mutex_unlock(&dblib_mutex);
    4259           0 :         return r;
    4260             : }
    4261             : 
    4262             : /**
    4263             :  * \ingroup dblib_core
    4264             :  * \brief Set maximum seconds db-lib waits for a server response to query.  
    4265             :  * 
    4266             :  * \param seconds New limit for application.  
    4267             :  * \retval SUCCEED Always.  
    4268             :  * \sa dberrhandle(), DBGETTIME(), dbsetlogintime(), dbsqlexec(), dbsqlok(), dbsqlsend().
    4269             :  */
    4270             : RETCODE
    4271          70 : dbsettime(int seconds)
    4272             : {
    4273             :         TDSSOCKET **tds;
    4274             :         int i;
    4275             :         DBPROCESS *dbproc;
    4276          70 :         tdsdump_log(TDS_DBG_FUNC, "dbsettime(%d)\n", seconds);
    4277             : 
    4278          70 :         tds_mutex_lock(&dblib_mutex);
    4279          70 :         g_dblib_ctx.query_timeout = seconds;
    4280             :         
    4281          70 :         tds = g_dblib_ctx.connection_list;
    4282      286790 :         for (i = 0; i <  TDS_MAX_CONN; i++) {
    4283      286720 :                 if (tds[i]) {
    4284         100 :                         dbproc = (DBPROCESS *) tds_get_parent(tds[i]);
    4285         100 :                         if (!dbisopt(dbproc, DBSETTIME, 0))
    4286          80 :                                 tds[i]->query_timeout = seconds;
    4287             :                 }
    4288             :         }
    4289             :         
    4290          70 :         tds_mutex_unlock(&dblib_mutex);
    4291          70 :         return SUCCEED;
    4292             : }
    4293             : 
    4294             : /**
    4295             :  * \ingroup dblib_core
    4296             :  * \brief Get maximum seconds db-lib waits for a server response to query.  
    4297             :  * 
    4298             :  * \retval query timeout limit, in seconds
    4299             :  * \sa dberrhandle(), DBSETTIME(), dbsetlogintime(), dbsqlexec(), dbsqlok(), dbsqlsend().
    4300             :  */
    4301             : int
    4302           0 : dbgettime(void)
    4303             : {
    4304           0 :         tdsdump_log(TDS_DBG_FUNC, "dbgettime()\n");
    4305             : 
    4306           0 :         return g_dblib_ctx.query_timeout;
    4307             : }
    4308             : 
    4309             : /**
    4310             :  * \ingroup dblib_core
    4311             :  * \brief Set maximum seconds db-lib waits for a server response to a login attempt.  
    4312             :  * 
    4313             :  * \param seconds New limit for application.  
    4314             :  * \retval SUCCEED Always.  
    4315             :  * \sa dberrhandle(), dbsettime()
    4316             :  */
    4317             : RETCODE
    4318          20 : dbsetlogintime(int seconds)
    4319             : {
    4320          20 :         tdsdump_log(TDS_DBG_FUNC, "dbsetlogintime(%d)\n", seconds);
    4321             : 
    4322          20 :         tds_mutex_lock(&dblib_mutex);
    4323          20 :         g_dblib_ctx.login_timeout = seconds;
    4324          20 :         tds_mutex_unlock(&dblib_mutex);
    4325          20 :         return SUCCEED;
    4326             : }
    4327             : 
    4328             : /** \internal
    4329             :  * \ingroup dblib_internal
    4330             :  * \brief See if the current command can return rows.
    4331             :  * 
    4332             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4333             :  * \retval SUCCEED Yes, it can.  
    4334             :  * \retval FAIL No, it can't.
    4335             :  * \remarks Use   DBCMDROW() macro instead.  
    4336             :  * \sa DBCMDROW(), dbnextrow(), dbresults(), DBROWS(), DBROWTYPE().
    4337             :  */
    4338             : RETCODE
    4339           0 : dbcmdrow(DBPROCESS * dbproc)
    4340             : {
    4341             :         TDSSOCKET *tds;
    4342             : 
    4343           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcmdrow(%p)\n", dbproc);
    4344           0 :         CHECK_CONN(FAIL);
    4345             : 
    4346           0 :         tds = dbproc->tds_socket;
    4347           0 :         if (tds->res_info)
    4348             :                 return SUCCEED;
    4349           0 :         return FAIL;
    4350             : }
    4351             : 
    4352             : /**
    4353             :  * \ingroup dblib_core
    4354             :  * \brief Get column ID of a compute column.
    4355             :  * 
    4356             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4357             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4358             :  * \param column Nth column in \a computeid, starting from 1.
    4359             :  * \return Nth column in the base result set, on which \a column was computed.  
    4360             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbgetrow(), dbnextrow(), dbnumalts(), dbprtype(). 
    4361             :  */
    4362             : int
    4363          16 : dbaltcolid(DBPROCESS * dbproc, int computeid, int column)
    4364             : {
    4365             :         TDSCOLUMN *curcol;
    4366             : 
    4367          16 :         tdsdump_log(TDS_DBG_FUNC, "dbaltcolid(%p, %d, %d)\n", dbproc, computeid, column);
    4368             : 
    4369          16 :         curcol = dbacolptr(dbproc, computeid, column, false);
    4370          16 :         if (!curcol)
    4371             :                 return -1;
    4372             : 
    4373          16 :         return curcol->column_operand;
    4374             : }
    4375             : 
    4376             : /**
    4377             :  * \ingroup dblib_core
    4378             :  * \brief Get size of data in a compute column.
    4379             :  * 
    4380             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4381             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4382             :  * \param column Nth column in \a computeid, starting from 1.
    4383             :  * \return size of the data, in bytes.
    4384             :  * \retval -1 no such \a column or \a computeid. 
    4385             :  * \retval 0 data are NULL.
    4386             :  * \sa dbadata(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
    4387             :  */
    4388             : DBINT
    4389           0 : dbadlen(DBPROCESS * dbproc, int computeid, int column)
    4390             : {
    4391             :         TDSCOLUMN *colinfo;
    4392             :         DBINT len;
    4393             : 
    4394           0 :         tdsdump_log(TDS_DBG_FUNC, "dbadlen(%p, %d, %d)\n", dbproc, computeid, column);
    4395             : 
    4396           0 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    4397           0 :         if (!colinfo)
    4398             :                 return -1;
    4399             : 
    4400           0 :         len = TDS_MAX(colinfo->column_cur_size, 0);
    4401             : 
    4402           0 :         tdsdump_log(TDS_DBG_FUNC, "leaving dbadlen() type = %d, returning %d\n", colinfo->column_type, len);
    4403             : 
    4404             :         return len;
    4405             : }
    4406             : 
    4407             : /**
    4408             :  * \ingroup dblib_core
    4409             :  * \brief Get datatype for a compute column.
    4410             :  * 
    4411             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4412             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4413             :  * \param column Nth column in \a computeid, starting from 1.
    4414             :  * \return \c SYB* dataype token.
    4415             :  * \retval -1 no such \a column or \a computeid. 
    4416             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbnextrow(), dbnumalts(), dbprtype().
    4417             :  */
    4418             : int
    4419          16 : dbalttype(DBPROCESS * dbproc, int computeid, int column)
    4420             : {
    4421             :         TDSCOLUMN *colinfo;
    4422             : 
    4423          16 :         tdsdump_log(TDS_DBG_FUNC, "dbalttype(%p, %d, %d)\n", dbproc, computeid, column);
    4424             : 
    4425          16 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    4426          16 :         if (!colinfo)
    4427             :                 return -1;
    4428             : 
    4429          16 :         return dblib_coltype(colinfo);
    4430             : }
    4431             : 
    4432             : /**
    4433             :  * \ingroup dblib_core
    4434             :  * \brief Bind a compute column to a program variable.
    4435             :  * 
    4436             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4437             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4438             :  * \param column Nth column in \a computeid, starting from 1.
    4439             :  * \param vartype datatype of the host variable that will receive the data
    4440             :  * \param varlen size of host variable pointed to \a varaddr
    4441             :  * \param varaddr address of host variable
    4442             :  * \retval SUCCEED everything worked.
    4443             :  * \retval FAIL no such \a computeid or \a column, or no such conversion possible, or target buffer too small.
    4444             :  * \sa dbadata(), dbaltbind_ps(), dbanullbind(), dbbind(), dbbind_ps(), dbconvert(),
    4445             :  *      dbconvert_ps(), dbnullbind(), dbsetnull(), dbsetversion(), dbwillconvert().
    4446             :  */
    4447             : RETCODE
    4448          16 : dbaltbind(DBPROCESS * dbproc, int computeid, int column, int vartype, DBINT varlen, BYTE * varaddr)
    4449             : {
    4450             :         TDS_SERVER_TYPE srctype, desttype;
    4451          16 :         TDSCOLUMN *colinfo = NULL;
    4452             : 
    4453          16 :         tdsdump_log(TDS_DBG_FUNC, "dbaltbind(%p, %d, %d, %d, %d, %p)\n", dbproc, computeid, column, vartype, varlen, varaddr);
    4454             : 
    4455          16 :         colinfo = dbacolptr(dbproc, computeid, column, true);
    4456          16 :         if (!colinfo)
    4457             :                 return FAIL;
    4458          16 :         CHECK_PARAMETER(varaddr, SYBEABNV, FAIL);
    4459             : 
    4460          16 :         dbproc->avail_flag = FALSE;
    4461             : 
    4462          16 :         srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    4463          16 :         desttype = dblib_bound_type(vartype);
    4464          16 :         if (desttype == TDS_INVALID_TYPE) {
    4465           0 :                 dbperror(dbproc, SYBEBTYP, 0);
    4466           0 :                 return FAIL;
    4467             :         }
    4468             : 
    4469          16 :         if (!dbwillconvert(srctype, desttype)) {
    4470           0 :                 dbperror(dbproc, SYBEAAMT, 0);
    4471           0 :                 return FAIL;
    4472             :         }
    4473             : 
    4474          16 :         colinfo->column_varaddr = (char *) varaddr;
    4475          16 :         colinfo->column_bindtype = vartype;
    4476          16 :         colinfo->column_bindlen = varlen;
    4477             : 
    4478          16 :         return SUCCEED;
    4479             : }
    4480             : 
    4481             : 
    4482             : /**
    4483             :  * \ingroup dblib_core
    4484             :  * \brief Get address of compute column data.  
    4485             :  * 
    4486             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4487             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4488             :  * \param column Nth column in \a computeid, starting from 1.
    4489             :  * \return pointer to columns's data buffer.  
    4490             :  * \retval NULL no such \a computeid or \a column.  
    4491             :  * \sa dbadlen(), dbaltbind(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
    4492             :  */
    4493             : BYTE *
    4494           0 : dbadata(DBPROCESS * dbproc, int computeid, int column)
    4495             : {
    4496             :         TDSCOLUMN *colinfo;
    4497             : 
    4498           0 :         tdsdump_log(TDS_DBG_FUNC, "dbadata(%p, %d, %d)\n", dbproc, computeid, column);
    4499             : 
    4500           0 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    4501           0 :         if (!colinfo)
    4502             :                 return NULL;
    4503             : 
    4504           0 :         if (is_blob_col(colinfo)) {
    4505           0 :                 return (BYTE *) ((TDSBLOB *) colinfo->column_data)->textvalue;
    4506             :         }
    4507             : 
    4508           0 :         return (BYTE *) colinfo->column_data;
    4509             : }
    4510             : 
    4511             : /**
    4512             :  * \ingroup dblib_core
    4513             :  * \brief Get aggregation operator for a compute column.
    4514             :  * 
    4515             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4516             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4517             :  * \param column Nth column in \a computeid, starting from 1.
    4518             :  * \return token value for the type of the compute column's aggregation operator.
    4519             :  * \retval -1 no such \a computeid or \a column.  
    4520             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbnextrow(), dbnumalts(), dbprtype().
    4521             :  */
    4522             : int
    4523          16 : dbaltop(DBPROCESS * dbproc, int computeid, int column)
    4524             : {
    4525             :         TDSCOLUMN *curcol;
    4526             : 
    4527          16 :         tdsdump_log(TDS_DBG_FUNC, "dbaltop(%p, %d, %d)\n", dbproc, computeid, column);
    4528             : 
    4529          16 :         if ((curcol=dbacolptr(dbproc, computeid, column, false)) == NULL)
    4530             :                 return -1;
    4531             : 
    4532          16 :         return curcol->column_operator;
    4533             : }
    4534             : 
    4535             : /**
    4536             :  * \ingroup dblib_core
    4537             :  * \brief Set db-lib or server option.
    4538             :  * 
    4539             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4540             :  * \param option option to set.  
    4541             :  * \param char_param value to set \a option to, if it wants a null-teminated ASCII string.  
    4542             :  * \param int_param  value to set \a option to, if it wants an integer value.  
    4543             :  * \retval SUCCEED everything worked.
    4544             :  * \retval FAIL no such \a option, or insufficient memory, or unimplemented.  
    4545             :  * \remarks Many are unimplemented.
    4546             :  * \sa dbclropt(), dbisopt().
    4547             :  * \todo Implement more options.  
    4548             :  */
    4549             : RETCODE
    4550          90 : dbsetopt(DBPROCESS * dbproc, int option, const char *char_param, int int_param)
    4551             : {
    4552             :         char *cmd;
    4553             :         RETCODE rc;
    4554             :         int i;
    4555             : 
    4556          90 :         tdsdump_log(TDS_DBG_FUNC, "dbsetopt(%p, %d, %s, %d)\n", dbproc, option, char_param, int_param);
    4557          90 :         CHECK_CONN(FAIL);
    4558          90 :         CHECK_NULP(char_param, "dbsetopt", 3, FAIL);
    4559             : 
    4560          90 :         if ((option < 0) || (option >= DBNUMOPTIONS)) {
    4561           0 :                 dbperror(dbproc, SYBEUNOP, 0);
    4562           0 :                 return FAIL;
    4563             :         }
    4564             : 
    4565          90 :         rc = FAIL;
    4566          90 :         switch (option) {
    4567           0 :         case DBARITHABORT:
    4568             :         case DBARITHIGNORE:
    4569             :         case DBCHAINXACTS:
    4570             :         case DBFIPSFLAG:
    4571             :         case DBISOLATION:
    4572             :         case DBNOCOUNT:
    4573             :         case DBNOEXEC:
    4574             :         case DBPARSEONLY:
    4575             :         case DBSHOWPLAN:
    4576             :         case DBSTORPROCID:
    4577             :         case DBQUOTEDIDENT:
    4578             :                 /* server options (on/off) */
    4579           0 :                 if (asprintf(&cmd, "set %s on\n", dbproc->dbopts[option].text) < 0) {
    4580             :                         return FAIL;
    4581             :                 }
    4582           0 :                 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
    4583           0 :                 free(cmd);
    4584           0 :                 break;
    4585           0 :         case DBNATLANG:
    4586             :         case DBDATEFIRST:
    4587             :         case DBDATEFORMAT:
    4588             :                 /* server options (char_param) */
    4589           0 :                 if (asprintf(&cmd, "set %s %s\n", dbproc->dbopts[option].text, char_param) < 0) {
    4590             :                         return FAIL;
    4591             :                 }
    4592           0 :                 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
    4593           0 :                 free(cmd);
    4594           0 :                 break;
    4595             :         case DBOFFSET:
    4596             :                 /* server option */
    4597             :                 /* requires param
    4598             :                  * "select", "from", "table", "order", "compute",
    4599             :                  * "statement", "procedure", "execute", or "param"
    4600             :                  */
    4601             :                 rc = SUCCEED;
    4602             :                 break;
    4603             :         case DBROWCOUNT:
    4604             :                 /* server option */
    4605             :                 /* requires param "0" to "2147483647" */
    4606             :                 rc = SUCCEED;
    4607             :                 break;
    4608             :         case DBSTAT:
    4609             :                 /* server option */
    4610             :                 /* requires param "io" or "time" */
    4611             :                 rc = SUCCEED;
    4612             :                 break;
    4613             :         case DBTEXTLIMIT:
    4614             :                 /* dblib option */
    4615             :                 /* requires param "0" to "2147483647" */
    4616             :                 /* dblib do not return more than this length from text/image */
    4617             :                 /* TODO required for PHP */
    4618             :                 rc = SUCCEED;
    4619             :                 break;
    4620             :         case DBTEXTSIZE:
    4621             :                 /* server option */
    4622             :                 /* requires param "0" to "2147483647" */
    4623             :                 /* limit text/image from network */
    4624             :                 if (!char_param)
    4625             :                         char_param = "0";
    4626           0 :                 i = atoi(char_param);
    4627           0 :                 if (i < 0 || i > 2147483647)
    4628             :                         return FAIL;
    4629           0 :                 if (asprintf(&cmd, "set textsize %d\n", i) < 0)
    4630             :                         return FAIL;
    4631           0 :                 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
    4632           0 :                 free(cmd);
    4633           0 :                 break;
    4634             :         case DBAUTH:
    4635             :                 /* ??? */
    4636             :                 rc = SUCCEED;
    4637             :                 break;
    4638             :         case DBNOAUTOFREE:
    4639             :                 /* dblib option */
    4640             :                 rc = SUCCEED;
    4641             :                 break;
    4642             :         case DBBUFFER:
    4643             :                 /* 
    4644             :                  * Requires param "2" to "2147483647" 
    4645             :                  * (0 or 1 is an error, < 0 yields the default 100) 
    4646             :                  */
    4647             :                 {
    4648             :                         int nrows; 
    4649             :                         
    4650             :                         /* 100 is the default, according to Microsoft */
    4651             :                         if( !char_param )
    4652             :                                 char_param = "100";
    4653             : 
    4654          60 :                         nrows = atoi(char_param);
    4655             : 
    4656          60 :                         nrows = (nrows < 0 )? 100 : nrows;
    4657             : 
    4658          60 :                         if( 1 < nrows && nrows <= 2147483647 ) {
    4659          60 :                                 buffer_set_capacity(dbproc, nrows);
    4660          60 :                                 rc = SUCCEED;
    4661             :                         }
    4662             :                 }
    4663             :                 break;
    4664           0 :         case DBPRCOLSEP:
    4665             :         case DBPRLINELEN:
    4666             :         case DBPRLINESEP:
    4667           0 :                 rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param);
    4668           0 :                 break;
    4669           0 :         case DBPRPAD:
    4670             :                 /*
    4671             :                  * "If the character is not specified, the ASCII space character is used." 
    4672             :                  * A NULL pointer to the pad character signifies that padding is turned off. 
    4673             :                  */
    4674           0 :                 if (int_param) {
    4675           0 :                         rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param? char_param : " ");
    4676             :                 } else {
    4677           0 :                         rc = dbstring_assign(&(dbproc->dbopts[option].param), NULL);
    4678             :                 }
    4679             :                 break;
    4680             :         case DBSETTIME:
    4681             :                 if (char_param) {
    4682          30 :                         i = atoi(char_param);
    4683          30 :                         if (0 < i) {
    4684          60 :                                 rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param);
    4685          30 :                                 if (rc == SUCCEED) {
    4686          30 :                                         dbproc->tds_socket->query_timeout = i;
    4687             :                                 }
    4688             :                         }
    4689             :                 }
    4690             :                 break;
    4691           0 :         default:
    4692           0 :                 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetopt(option = %d)\n", option);
    4693             :                 return FAIL;
    4694             :         }
    4695          30 :         if (rc == SUCCEED)
    4696          90 :                 dbproc->dbopts[option].factive = 1;
    4697             :         return rc;
    4698             : }
    4699             : 
    4700             : /**
    4701             :  * \ingroup dblib_core
    4702             :  * \brief Set interrupt handler for db-lib to use while blocked against a read from the server.
    4703             :  * 
    4704             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4705             :  * \param chkintr
    4706             :  * \param hndlintr
    4707             :  * \sa dbcancel(), dbgetuserdata(), dbsetuserdata(), dbsetbusy(), dbsetidle().
    4708             :  */
    4709             : void
    4710          20 : dbsetinterrupt(DBPROCESS * dbproc, DB_DBCHKINTR_FUNC chkintr, DB_DBHNDLINTR_FUNC hndlintr)
    4711             : {
    4712          20 :         tdsdump_log(TDS_DBG_FUNC, "dbsetinterrupt(%p, %p, %p)\n", dbproc, chkintr, hndlintr);
    4713          20 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    4714             : 
    4715          20 :         dbproc->chkintr = chkintr;
    4716          20 :         dbproc->hndlintr = hndlintr;
    4717             : }
    4718             : 
    4719             : /**
    4720             :  * \ingroup dblib_rpc
    4721             :  * \brief Determine if query generated a return status number.
    4722             :  * 
    4723             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4724             :  * \retval TRUE fetch return status with dbretstatus().  
    4725             :  * \retval FALSE no return status.  
    4726             :  * \sa dbnextrow(), dbresults(), dbretdata(), dbretstatus(), dbrpcinit(), dbrpcparam(), dbrpcsend().
    4727             :  */
    4728             : DBBOOL
    4729          62 : dbhasretstat(DBPROCESS * dbproc)
    4730             : {
    4731             :         TDSSOCKET *tds;
    4732             : 
    4733          62 :         tdsdump_log(TDS_DBG_FUNC, "dbhasretstat(%p)\n", dbproc);
    4734          62 :         CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
    4735             : 
    4736          62 :         tds = dbproc->tds_socket;
    4737          62 :         if (tds->has_status) {
    4738             :                 return TRUE;
    4739             :         } else {
    4740          30 :                 return FALSE;
    4741             :         }
    4742             : }
    4743             : 
    4744             : /**
    4745             :  * \ingroup dblib_rpc
    4746             :  * \brief Fetch status value returned by query or remote procedure call.
    4747             :  * 
    4748             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4749             :  * \return return value 
    4750             :  * \sa dbhasretstat(), dbnextrow(), dbresults(), dbretdata(), dbrpcinit(), dbrpcparam(), dbrpcsend().
    4751             :  */
    4752             : DBINT
    4753         518 : dbretstatus(DBPROCESS * dbproc)
    4754             : {
    4755         518 :         tdsdump_log(TDS_DBG_FUNC, "dbretstatus(%p)\n", dbproc);
    4756         518 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    4757             : 
    4758         518 :         return dbproc->tds_socket->ret_status;
    4759             : }
    4760             : 
    4761             : /**
    4762             :  * \ingroup dblib_rpc
    4763             :  * \brief Get count of output parameters filled by a stored procedure.
    4764             :  * 
    4765             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4766             :  * \return How many, possibly zero.  
    4767             :  * \remarks This name sounds funny.  
    4768             :  * \sa 
    4769             :  */
    4770             : int
    4771         806 : dbnumrets(DBPROCESS * dbproc)
    4772             : {
    4773             :         TDSSOCKET *tds;
    4774             :         TDS_INT result_type;
    4775             : 
    4776         806 :         tdsdump_log(TDS_DBG_FUNC, "dbnumrets(%p)\n", dbproc);
    4777         806 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    4778             : 
    4779         806 :         tds = dbproc->tds_socket;
    4780             : 
    4781         806 :         tdsdump_log(TDS_DBG_FUNC, "dbnumrets() finds %d columns\n", (tds->param_info? tds->param_info->num_cols : 0));
    4782             : 
    4783             :         /* try to fetch output parameters and return status, if we have not already done so */
    4784         806 :         if (!tds->param_info) 
    4785         508 :                 tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_TRAILING);
    4786             :                 
    4787         806 :         if (!tds->param_info)
    4788             :                 return 0;
    4789             : 
    4790         300 :         return tds->param_info->num_cols;
    4791             : }
    4792             : 
    4793             : /**
    4794             :  * \ingroup dblib_rpc
    4795             :  * \brief Get name of an output parameter filled by a stored procedure.
    4796             :  * 
    4797             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4798             :  * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
    4799             :  * \returns ASCII null-terminated string, \c NULL if no such \a retnum.  
    4800             :  * \sa dbnextrow(), dbnumrets(), dbresults(), dbretdata(), dbretlen(), dbrettype(), dbrpcinit(), dbrpcparam().
    4801             :  */
    4802             : char *
    4803          66 : dbretname(DBPROCESS * dbproc, int retnum)
    4804             : {
    4805             :         TDSPARAMINFO *param_info;
    4806             : 
    4807          66 :         tdsdump_log(TDS_DBG_FUNC, "dbretname(%p, %d)\n", dbproc, retnum);
    4808          66 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    4809             : 
    4810          66 :         if (!dbproc->tds_socket)
    4811             :                 return NULL;
    4812             : 
    4813          66 :         dbnumrets(dbproc);
    4814             : 
    4815          66 :         param_info = dbproc->tds_socket->param_info;
    4816          66 :         if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
    4817             :                 return NULL;
    4818         132 :         return tds_dstr_buf(&param_info->columns[retnum - 1]->column_name);
    4819             : }
    4820             : 
    4821             : /**
    4822             :  * \ingroup dblib_rpc
    4823             :  * \brief Get value of an output parameter filled by a stored procedure.
    4824             :  * 
    4825             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4826             :  * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
    4827             :  * \returns Address of a return parameter value, or \c NULL if no such \a retnum.  
    4828             :  * \sa dbnextrow(), dbnumrets(), dbresults(), dbretlen(), dbretname(), dbrettype(), dbrpcinit(), dbrpcparam().
    4829             :  * \todo Handle blobs.
    4830             :  */
    4831             : BYTE *
    4832          70 : dbretdata(DBPROCESS * dbproc, int retnum)
    4833             : {
    4834             :         TDSPARAMINFO *param_info;
    4835             : 
    4836          70 :         tdsdump_log(TDS_DBG_FUNC, "dbretdata(%p, %d)\n", dbproc, retnum);
    4837          70 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    4838             : 
    4839          70 :         dbnumrets(dbproc);
    4840             : 
    4841          70 :         param_info = dbproc->tds_socket->param_info;
    4842          70 :         if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
    4843             :                 return NULL;
    4844             : 
    4845          70 :         return _dbcoldata(param_info->columns[retnum - 1]);
    4846             : }
    4847             : 
    4848             : /**
    4849             :  * \ingroup dblib_rpc
    4850             :  * \brief Get size of an output parameter filled by a stored procedure.
    4851             :  * 
    4852             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4853             :  * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
    4854             :  * \returns Size of a return parameter value, or \c NULL if no such \a retnum.  
    4855             :  * \sa dbnextrow(), dbnumrets(), dbresults(), dbretdata(), dbretname(), dbrettype(), dbrpcinit(), dbrpcparam().
    4856             :  */
    4857             : int
    4858          66 : dbretlen(DBPROCESS * dbproc, int retnum)
    4859             : {
    4860             :         TDSCOLUMN *column;
    4861             :         TDSPARAMINFO *param_info;
    4862             :         TDSSOCKET *tds;
    4863             : 
    4864          66 :         tdsdump_log(TDS_DBG_FUNC, "dbretlen(%p, %d)\n", dbproc, retnum);
    4865          66 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    4866             : 
    4867          66 :         dbnumrets(dbproc);
    4868             : 
    4869          66 :         tds = dbproc->tds_socket;
    4870          66 :         param_info = tds->param_info;
    4871          66 :         if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
    4872             :                 return -1;
    4873             : 
    4874          66 :         column = param_info->columns[retnum - 1];
    4875          66 :         if (column->column_cur_size < 0)
    4876             :                 return 0;
    4877             : 
    4878          52 :         return column->column_cur_size;
    4879             : }
    4880             : 
    4881             : /**
    4882             :  * \ingroup dblib_core
    4883             :  * \brief Wait for results of a query from the server.  
    4884             :  * 
    4885             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4886             :  * \retval SUCCEED everything worked, fetch results with \c dbnextresults().
    4887             :  * \retval FAIL SQL syntax error, typically.  
    4888             :  * \sa dbcmd(), dbfcmd(), DBIORDESC(), DBIOWDESC(), dbmoretext(), dbnextrow(),
    4889             :         dbpoll(), DBRBUF(), dbresults(), dbretstatus(), dbrpcsend(), dbsettime(), dbsqlexec(),
    4890             :         dbsqlsend(), dbwritetext().
    4891             :  */
    4892             : RETCODE
    4893       20708 : dbsqlok(DBPROCESS * dbproc)
    4894             : {
    4895             :         TDSSOCKET *tds;
    4896             :         TDS_INT result_type;
    4897       20708 :         RETCODE return_code = SUCCEED;
    4898             : 
    4899       20708 :         tdsdump_log(TDS_DBG_FUNC, "dbsqlok(%p)\n", dbproc);
    4900       20708 :         CHECK_CONN(FAIL);
    4901             : 
    4902       20708 :         tds = dbproc->tds_socket;
    4903             : 
    4904             :         /*
    4905             :          * dbsqlok has been called after dbmoretext()
    4906             :          * This is the trigger to send the text data.
    4907             :          */
    4908             : 
    4909       20708 :         if (dbproc->text_sent) {
    4910           0 :                 tds_flush_packet(tds);
    4911           0 :                 dbproc->text_sent = 0;
    4912             :         }
    4913             : 
    4914             :         /* 
    4915             :          * See what the next packet from the server is.
    4916             :          * We want to skip any messages which are not processable. 
    4917             :          * We're looking for a result token or a done token.
    4918             :          */
    4919         289 :         for (;;) {
    4920             :                 TDSRET tds_code;
    4921       20997 :                 int done_flags = 0;
    4922             : 
    4923             :                 /* 
    4924             :                  * If we hit an end token -- e.g. if the command
    4925             :                  * submitted returned no data (like an insert) -- then
    4926             :                  * we process the end token to extract the status code. 
    4927             :                  */
    4928       20997 :                 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() not done, calling tds_process_tokens()\n");
    4929             : 
    4930       20997 :                 tds_code = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS);
    4931             : 
    4932             :                 /* 
    4933             :                  * The error flag may be set for any intervening DONEINPROC packet, in particular
    4934             :                  * by a RAISERROR statement.  Microsoft db-lib returns FAIL in that case. 
    4935             :                  */
    4936       20997 :                 if (done_flags & TDS_DONE_ERROR) {
    4937          78 :                         return_code = FAIL;
    4938             :                 }
    4939             :                 
    4940       20997 :                 switch (tds_code) {
    4941             :                 case TDS_NO_MORE_RESULTS:
    4942       20708 :                         return SUCCEED;
    4943             :                         break;
    4944             : 
    4945       20939 :                 case TDS_SUCCESS:
    4946       20939 :                         switch (result_type) {
    4947       12190 :                         case TDS_ROWFMT_RESULT:
    4948       12190 :                                 buffer_free(&dbproc->row_buf);
    4949       12190 :                                 buffer_alloc(dbproc);
    4950       12190 :                         case TDS_COMPUTEFMT_RESULT:
    4951       12190 :                                 dbproc->dbresults_state = _DB_RES_RESULTSET_EMPTY;
    4952       12190 :                         case TDS_COMPUTE_RESULT:
    4953             :                         case TDS_ROW_RESULT:
    4954       12190 :                                 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() found result token\n");
    4955             :                                 return SUCCEED;
    4956             :                                 break;
    4957             :                         case TDS_DONEINPROC_RESULT:
    4958             :                                 break;
    4959        8460 :                         case TDS_DONE_RESULT:
    4960             :                         case TDS_DONEPROC_RESULT:
    4961        8460 :                                 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() end status is %d (%s)\n", return_code, prdbretcode(return_code));
    4962             : #if 1
    4963        8460 :                                 if (done_flags & TDS_DONE_ERROR) {
    4964             : 
    4965          78 :                                         if (done_flags & TDS_DONE_MORE_RESULTS) {
    4966           0 :                                                 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    4967             :                                         } else {
    4968          78 :                                                 dbproc->dbresults_state = _DB_RES_NO_MORE_RESULTS;
    4969             :                                         }
    4970             : 
    4971             :                                 } else {
    4972        8382 :                                         tdsdump_log(TDS_DBG_FUNC, "dbsqlok() end status was success\n");
    4973             : 
    4974        8382 :                                         dbproc->dbresults_state = _DB_RES_SUCCEED;
    4975             :                                 }
    4976             : 
    4977             :                                 return return_code;
    4978             :                                 break;
    4979             : #else
    4980             :                                 int retcode = (done_flags & TDS_DONE_ERROR)? FAIL : SUCCEED;
    4981             :                                 dbproc->dbresults_state = (done_flags & TDS_DONE_MORE_RESULTS)?
    4982             :                                         _DB_RES_NEXT_RESULT : _DB_RES_NO_MORE_RESULTS;
    4983             : 
    4984             :                                 tdsdump_log(TDS_DBG_FUNC, "dbsqlok: returning %s with %s (%#x)\n", 
    4985             :                                                 prdbretcode(retcode), prdbresults_state(dbproc->dbresults_state), done_flags);
    4986             :                                                 
    4987             :                                 if (retcode == SUCCEED && (done_flags & TDS_DONE_MORE_RESULTS))
    4988             :                                         continue;
    4989             :                                          
    4990             :                                 return retcode;
    4991             : #endif
    4992          39 :                         default:
    4993          39 :                                 tdsdump_log(TDS_DBG_FUNC, "%s %d: logic error: tds_process_tokens result_type %d\n", 
    4994             :                                                 __FILE__, __LINE__, result_type);
    4995             :                                 break;
    4996             :                         }
    4997             :                         break;
    4998             : 
    4999          40 :                 default:
    5000          40 :                         assert(TDS_FAILED(tds_code));
    5001             :                         return FAIL;
    5002             :                         break;
    5003             :                 }
    5004             :         }
    5005             : 
    5006             :         return SUCCEED;
    5007             : }
    5008             : 
    5009             : /**
    5010             :  * \ingroup dblib_core
    5011             :  * \brief Get count of columns in a compute row.
    5012             :  * 
    5013             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5014             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    5015             :  * \return number of columns, else -1 if no such \a computeid.  
    5016             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumcols(). 
    5017             :  */
    5018             : int
    5019          16 : dbnumalts(DBPROCESS * dbproc, int computeid)
    5020             : {
    5021             :         TDSSOCKET *tds;
    5022             :         TDSCOMPUTEINFO *info;
    5023             :         TDS_SMALLINT compute_id;
    5024             :         TDS_UINT i;
    5025             : 
    5026          16 :         tdsdump_log(TDS_DBG_FUNC, "dbnumalts(%p, %d)\n", dbproc, computeid);
    5027          16 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    5028             : 
    5029          16 :         tds = dbproc->tds_socket;
    5030          16 :         compute_id = computeid;
    5031             : 
    5032          24 :         for (i = 0;; ++i) {
    5033          32 :                 if (i >= tds->num_comp_info)
    5034             :                         return -1;
    5035          24 :                 info = tds->comp_info[i];
    5036          24 :                 if (info->computeid == compute_id)
    5037             :                         break;
    5038             :         }
    5039             : 
    5040          16 :         return info->num_cols;
    5041             : }
    5042             : 
    5043             : /**
    5044             :  * \ingroup dblib_core
    5045             :  * \brief Get count of \c COMPUTE clauses for a result set.
    5046             :  * 
    5047             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5048             :  * \return number of compute clauses for the current query, possibly zero.  
    5049             :  * \sa dbnumalts(), dbresults().
    5050             :  */
    5051             : int
    5052           0 : dbnumcompute(DBPROCESS * dbproc)
    5053             : {
    5054             :         TDSSOCKET *tds;
    5055             : 
    5056           0 :         tdsdump_log(TDS_DBG_FUNC, "dbnumcompute(%p)\n", dbproc);
    5057           0 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    5058             : 
    5059           0 :         tds = dbproc->tds_socket;
    5060             : 
    5061           0 :         return tds->num_comp_info;
    5062             : }
    5063             : 
    5064             : 
    5065             : /**
    5066             :  * \ingroup dblib_core
    5067             :  * \brief Get \c bylist for a compute row.
    5068             :  * 
    5069             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5070             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    5071             :  * \param size \em output: size of \c bylist buffer whose address is returned, possibly zero.  
    5072             :  * \return address of \c bylist for \a computeid.  
    5073             :  * \retval NULL no such \a computeid.
    5074             :  * \remarks Do not free returned pointer.  
    5075             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbalttype(), dbcolname(), dbgetrow(), dbnextrow().
    5076             :  */
    5077             : BYTE *
    5078           0 : dbbylist(DBPROCESS * dbproc, int computeid, int *size)
    5079             : {
    5080             :         TDSSOCKET *tds;
    5081             :         TDSCOMPUTEINFO *info;
    5082             :         TDS_UINT i;
    5083           0 :         const TDS_SMALLINT byte_flag = -0x8000;
    5084             : 
    5085           0 :         tdsdump_log(TDS_DBG_FUNC, "dbbylist(%p, %d, %p)\n", dbproc, computeid, size);
    5086           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    5087             : 
    5088           0 :         tds = dbproc->tds_socket;
    5089             : 
    5090           0 :         for (i = 0;; ++i) {
    5091           0 :                 if (i >= tds->num_comp_info) {
    5092           0 :                         if (size)
    5093           0 :                                 *size = 0;
    5094             :                         return NULL;
    5095             :                 }
    5096           0 :                 info = tds->comp_info[i];
    5097           0 :                 if (info->computeid == computeid)
    5098             :                         break;
    5099             :         }
    5100             : 
    5101           0 :         if (size)
    5102           0 :                 *size = info->by_cols;
    5103             : 
    5104             :         /*
    5105             :          * libtds stores this information using TDS_SMALLINT so we 
    5106             :          * have to convert it. We can do this because libtds just
    5107             :          * stores these data.
    5108             :          */
    5109           0 :         if (info->by_cols > 0 && info->bycolumns[0] != byte_flag) {
    5110             :                 int n;
    5111           0 :                 TDS_TINYINT *p = (TDS_TINYINT*) malloc(sizeof(info->bycolumns[0]) + info->by_cols);
    5112           0 :                 if (!p) {
    5113           0 :                         dbperror(dbproc, SYBEMEM, errno);
    5114           0 :                         return NULL;
    5115             :                 }
    5116           0 :                 for (n = 0; n < info->by_cols; ++n)
    5117           0 :                         p[sizeof(info->bycolumns[0]) + n] = TDS_MIN(info->bycolumns[n], 255);
    5118           0 :                 *((TDS_SMALLINT *)p) = byte_flag;
    5119           0 :                 free(info->bycolumns);
    5120           0 :                 info->bycolumns = (TDS_SMALLINT *) p;
    5121             :         }
    5122           0 :         return (BYTE *) (&info->bycolumns[1]);
    5123             : }
    5124             : 
    5125             : /** \internal
    5126             :  * \ingroup dblib_internal
    5127             :  * \brief Check if \a dbproc is an ex-parrot.  
    5128             :  * 
    5129             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5130             :  * \retval TRUE process has been marked \em dead.
    5131             :  * \retval FALSE process is OK.  
    5132             :  * \remarks dbdead() does not communicate with the server.  
    5133             :  *      Unless a previously db-lib marked \a dbproc \em dead, dbdead() returns \c FALSE.  
    5134             :  * \sa dberrhandle().
    5135             :  */
    5136             : DBBOOL
    5137          10 : dbdead(DBPROCESS * dbproc)
    5138             : {
    5139          10 :         tdsdump_log(TDS_DBG_FUNC, "dbdead(%p) [%s]\n", dbproc, dbproc? IS_TDSDEAD(dbproc->tds_socket)? "dead":"alive" : "quite dead");
    5140             : 
    5141          10 :         if( NULL == dbproc ) 
    5142             :                 return TRUE;
    5143             : 
    5144          10 :         if (IS_TDSDEAD(dbproc->tds_socket))
    5145             :                 return TRUE;
    5146             : 
    5147          10 :         return FALSE;
    5148             : }
    5149             : 
    5150             : /** \internal
    5151             :  * \ingroup dblib_internal
    5152             :  * \brief default error handler for db-lib (handles library-generated errors)
    5153             :  * 
    5154             :  * The default error handler doesn't print anything.  If you want to see your messages printed, 
    5155             :  * install an error handler.  If you think that should be an optional compile- or run-time default, 
    5156             :  * submit a patch.  It could be done.  
    5157             :  * 
    5158             :  * \sa DBDEAD(), dberrhandle().
    5159             :  */
    5160             : /* Thus saith Sybase:
    5161             :  *     "If the user does not supply an error handler (or passes a NULL pointer to 
    5162             :  *      dberrhandle), DB-Library will exhibit its default error-handling 
    5163             :  *      behavior: It will abort the program if the error has made the affected 
    5164             :  *      DBPROCESS unusable (the user can call DBDEAD to determine whether 
    5165             :  *      or not a DBPROCESS has become unusable). If the error has not made the 
    5166             :  *      DBPROCESS unusable, DB-Library will simply return an error code to its caller." 
    5167             :  *
    5168             :  * It is not the error handler, however, that aborts anything.  It is db-lib, cf. dbperror().  
    5169             :  */ 
    5170             : static int
    5171          10 : default_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
    5172             : {
    5173          10 :         tdsdump_log(TDS_DBG_FUNC, "default_err_handler %p, %d, %d, %d, %p, %p", dbproc, severity, dberr, oserr, dberrstr, oserrstr);
    5174             : 
    5175          10 :         if (DBDEAD(dbproc) && (!dbproc || !dbproc->msdblib)) {
    5176             :                 return INT_EXIT;
    5177             :         }
    5178             :         
    5179          10 :         if (!dbproc || !dbproc->msdblib) {   /* i.e. Sybase behavior */
    5180          10 :                 switch(dberr) {
    5181             :                 case SYBETIME:
    5182             :                         return INT_EXIT;
    5183             :                 default: 
    5184             :                         break;
    5185             :                 }
    5186           0 :         }
    5187          10 :         return INT_CANCEL;
    5188             : }
    5189             : 
    5190             : /**
    5191             :  * \ingroup dblib_core
    5192             :  * \brief Set an error handler, for messages from db-lib.
    5193             :  * 
    5194             :  * \param handler pointer to callback function that will handle errors.
    5195             :  *        Pass NULL to restore the default handler.  
    5196             :  * \return address of prior handler, or NULL if none was previously installed. 
    5197             :  * \sa DBDEAD(), dbmsghandle().
    5198             :  */
    5199             : EHANDLEFUNC
    5200        1152 : dberrhandle(EHANDLEFUNC handler)
    5201             : {
    5202        1152 :         EHANDLEFUNC old_handler = _dblib_err_handler;
    5203             : 
    5204        1152 :         tdsdump_log(TDS_DBG_FUNC, "dberrhandle(%p)\n", handler);
    5205             : 
    5206        1152 :         _dblib_err_handler = handler? handler : default_err_handler;
    5207             :         
    5208        1152 :         return (old_handler == default_err_handler)? NULL : old_handler;
    5209             : }
    5210             : 
    5211             : /**
    5212             :  * \ingroup dblib_core
    5213             :  * \brief Set a message handler, for messages from the server.
    5214             :  * 
    5215             :  * \param handler address of the function that will process the messages.
    5216             :  * \sa DBDEAD(), dberrhandle().
    5217             :  */
    5218             : MHANDLEFUNC
    5219        1172 : dbmsghandle(MHANDLEFUNC handler)
    5220             : {
    5221        1172 :         MHANDLEFUNC retFun = _dblib_msg_handler;
    5222             : 
    5223        1172 :         tdsdump_log(TDS_DBG_FUNC, "dbmsghandle(%p)\n", handler);
    5224             : 
    5225        1172 :         _dblib_msg_handler = handler;
    5226        1172 :         return retFun;
    5227             : }
    5228             : 
    5229             : #if defined(DBLIB_UNIMPLEMENTED)
    5230             : /**
    5231             :  * \ingroup dblib_money
    5232             :  * \brief Add two DBMONEY values.
    5233             :  * 
    5234             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5235             :  * \param m1 first operand.
    5236             :  * \param m2 other operand. 
    5237             :  * \param sum \em output: result of computation.  
    5238             :  * \retval SUCCEED Always.  
    5239             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5240             :  * \todo Unimplemented.
    5241             :  */
    5242             : RETCODE
    5243             : dbmnyadd(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * sum)
    5244             : {
    5245             :         tdsdump_log(TDS_DBG_FUNC, "dbmnyadd(%p, %p, %p, %p)\n", dbproc, m1, m2, sum);
    5246             :         CHECK_CONN(FAIL);
    5247             :         CHECK_NULP(m1,  "dbmnyadd", 2, FAIL);
    5248             :         CHECK_NULP(m2,  "dbmnyadd", 3, FAIL);
    5249             :         CHECK_NULP(sum, "dbmnyadd", 4, FAIL);
    5250             : 
    5251             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyadd()\n");
    5252             :         return SUCCEED;
    5253             : }
    5254             : 
    5255             : 
    5256             : /**
    5257             :  * \ingroup dblib_money
    5258             :  * \brief Subtract two DBMONEY values.
    5259             :  * 
    5260             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5261             :  * \param m1 first operand.
    5262             :  * \param m2 other operand, subtracted from \a m1. 
    5263             :  * \param difference \em output: result of computation.  
    5264             :  * \retval SUCCEED Always.  
    5265             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5266             :  * \todo Unimplemented.
    5267             :  */
    5268             : RETCODE
    5269             : dbmnysub(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * difference)
    5270             : {
    5271             :         tdsdump_log(TDS_DBG_FUNC, "dbmnysub(%p, %p, %p, %p)\n", dbproc, m1, m2, difference);
    5272             :         CHECK_CONN(FAIL);
    5273             :         CHECK_NULP(m1, "dbmnysub", 2, FAIL);
    5274             :         CHECK_NULP(m2, "dbmnysub", 3, FAIL);
    5275             :         CHECK_NULP(difference, "dbmnysub", 4, FAIL);
    5276             : 
    5277             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnysyb()\n");
    5278             :         return SUCCEED;
    5279             : }
    5280             : 
    5281             : /**
    5282             :  * \ingroup dblib_money
    5283             :  * \brief Multiply two DBMONEY values.
    5284             :  * 
    5285             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5286             :  * \param m1 first operand.
    5287             :  * \param m2 other operand. 
    5288             :  * \param prod \em output: result of computation.  
    5289             :  * \retval SUCCEED Always.  
    5290             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5291             :  * \todo Unimplemented.
    5292             :  */
    5293             : RETCODE
    5294             : dbmnymul(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * prod)
    5295             : {
    5296             :         tdsdump_log(TDS_DBG_FUNC, "dbmnymul(%p, %p, %p, %p)\n", dbproc, m1, m2, prod);
    5297             :         CHECK_CONN(FAIL);
    5298             :         CHECK_NULP(m1, "dbmnymul", 2, FAIL);
    5299             :         CHECK_NULP(m2, "dbmnymul", 3, FAIL);
    5300             :         CHECK_NULP(prod, "dbmnymul", 4, FAIL);
    5301             : 
    5302             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnymul()\n");
    5303             :         return SUCCEED;
    5304             : }
    5305             : 
    5306             : /**
    5307             :  * \ingroup dblib_money
    5308             :  * \brief Divide two DBMONEY values.
    5309             :  * 
    5310             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5311             :  * \param m1 dividend.
    5312             :  * \param m2 divisor. 
    5313             :  * \param quotient \em output: result of computation.  
    5314             :  * \retval SUCCEED Always.  
    5315             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5316             :  * \todo Unimplemented.
    5317             :  */
    5318             : RETCODE
    5319             : dbmnydivide(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * quotient)
    5320             : {
    5321             :         tdsdump_log(TDS_DBG_FUNC, "dbmnydivide(%p, %p, %p, %p)\n", dbproc, m1, m2, quotient);
    5322             :         CHECK_CONN(FAIL);
    5323             :         CHECK_NULP(m1, "dbmnydivide", 2, FAIL);
    5324             :         CHECK_NULP(m2, "dbmnydivide", 3, FAIL);
    5325             :         CHECK_NULP(quotient, "dbmnydivide", 4, FAIL);
    5326             : 
    5327             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnydivide()\n");
    5328             :         return SUCCEED;
    5329             : }
    5330             : #endif
    5331             : 
    5332             : /**
    5333             :  * \ingroup dblib_money
    5334             :  * \brief Compare two DBMONEY values.
    5335             :  * 
    5336             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5337             :  * \param m1 some money.
    5338             :  * \param m2 some other money. 
    5339             :  * \retval 0 m1 == m2. 
    5340             :  * \retval -1 m1 < m2.
    5341             :  * \retval  1 m1 > m2.
    5342             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5343             :  */
    5344             : int
    5345           0 : dbmnycmp(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2)
    5346             : {
    5347           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnycmp(%p, %p, %p)\n", dbproc, m1, m2);
    5348           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    5349           0 :         CHECK_NULP(m1, "dbmnycmp", 2, 0);
    5350           0 :         CHECK_NULP(m2, "dbmnycmp", 3, 0);
    5351             : 
    5352           0 :         if (m1->mnyhigh < m2->mnyhigh) {
    5353             :                 return -1;
    5354             :         }
    5355           0 :         if (m1->mnyhigh > m2->mnyhigh) {
    5356             :                 return 1;
    5357             :         }
    5358           0 :         if (m1->mnylow < m2->mnylow) {
    5359             :                 return -1;
    5360             :         }
    5361           0 :         if (m1->mnylow > m2->mnylow) {
    5362             :                 return 1;
    5363             :         }
    5364           0 :         return 0;
    5365             : }
    5366             : 
    5367             : #if defined(DBLIB_UNIMPLEMENTED)
    5368             : /**
    5369             :  * \ingroup dblib_money
    5370             :  * \brief Multiply a DBMONEY value by a positive integer, and add an amount. 
    5371             :  * 
    5372             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5373             :  * \param amount starting amount of money, also holds output.
    5374             :  * \param multiplier amount to multiply \a amount by. 
    5375             :  * \param addend amount to add to \a amount, after multiplying by \a multiplier. 
    5376             :  * \retval SUCCEED Always.  
    5377             :  * \remarks This function is goofy.  
    5378             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5379             :  * \todo Unimplemented.
    5380             :  */
    5381             : RETCODE
    5382             : dbmnyscale(DBPROCESS * dbproc, DBMONEY * amount, int multiplier, int addend)
    5383             : {
    5384             :         tdsdump_log(TDS_DBG_FUNC, "dbmnyscale(%p, %p, %d, %d)\n", dbproc, amount, multiplier, addend);
    5385             :         CHECK_CONN(FAIL);
    5386             :         CHECK_NULP(amount, "dbmnyscale", 2, FAIL);
    5387             : 
    5388             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyscale()\n");
    5389             :         return SUCCEED;
    5390             : }
    5391             : #endif
    5392             : 
    5393             : /**
    5394             :  * \ingroup dblib_money
    5395             :  * \brief Set a DBMONEY value to zero.
    5396             :  * 
    5397             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5398             :  * \param dest address of a DBMONEY structure.  
    5399             :  * \retval SUCCEED unless \a amount is NULL.  
    5400             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5401             :  */
    5402             : RETCODE
    5403           0 : dbmnyzero(DBPROCESS * dbproc, DBMONEY * dest)
    5404             : {
    5405           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnyzero(%p, %p)\n", dbproc, dest);
    5406           0 :         CHECK_CONN(FAIL);
    5407           0 :         CHECK_NULP(dest, "dbmnyzero", 2, FAIL);
    5408             : 
    5409           0 :         dest->mnylow = 0;
    5410           0 :         dest->mnyhigh = 0;
    5411           0 :         return SUCCEED;
    5412             : }
    5413             : 
    5414             : /**
    5415             :  * \ingroup dblib_money
    5416             :  * \brief Get maximum positive DBMONEY value supported.
    5417             :  * 
    5418             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5419             :  * \param amount address of a DBMONEY structure.  
    5420             :  * \retval SUCCEED Always.  
    5421             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5422             :  */
    5423             : RETCODE
    5424           0 : dbmnymaxpos(DBPROCESS * dbproc, DBMONEY * amount)
    5425             : {
    5426           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnymaxpos(%p, %p)\n", dbproc, amount);
    5427           0 :         CHECK_CONN(FAIL);
    5428           0 :         CHECK_NULP(amount, "dbmnymaxpos", 2, FAIL);
    5429             : 
    5430           0 :         amount->mnylow = 0xFFFFFFFFlu;
    5431           0 :         amount->mnyhigh = 0x7FFFFFFFl;
    5432           0 :         return SUCCEED;
    5433             : }
    5434             : 
    5435             : /**
    5436             :  * \ingroup dblib_money
    5437             :  * \brief Get maximum negative DBMONEY value supported.
    5438             :  * 
    5439             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5440             :  * \param amount address of a DBMONEY structure.  
    5441             :  * \retval SUCCEED Always.  
    5442             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5443             :  */
    5444             : RETCODE
    5445           0 : dbmnymaxneg(DBPROCESS * dbproc, DBMONEY * amount)
    5446             : {
    5447           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnymaxneg(%p, %p)\n", dbproc, amount);
    5448           0 :         CHECK_CONN(FAIL);
    5449           0 :         CHECK_NULP(amount, "dbmnymaxneg", 2, FAIL);
    5450             : 
    5451           0 :         amount->mnylow = 0;
    5452           0 :         amount->mnyhigh = -0x7FFFFFFFL - 1;
    5453           0 :         return SUCCEED;
    5454             : }
    5455             : 
    5456             : #if defined(DBLIB_UNIMPLEMENTED)
    5457             : /**
    5458             :  * \ingroup dblib_money
    5459             :  * \brief Get the least significant digit of a DBMONEY value, represented as a character.
    5460             :  * 
    5461             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5462             :  * \param mnyptr \em input the money amount, \em and \em output: \a mnyptr divided by 10.  
    5463             :  * \param digit the character value (between '0' and '9') of the rightmost digit in \a mnyptr.  
    5464             :  * \param zero \em output: \c TRUE if \a mnyptr is zero on output, else \c FALSE.  
    5465             :  * \retval SUCCEED Always.  
    5466             :  * \sa dbconvert(), dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5467             :  * \remarks Unimplemented and likely to remain so.  We'd be amused to learn anyone wants this function.  
    5468             :  * \todo Unimplemented.
    5469             :  */
    5470             : RETCODE
    5471             : dbmnyndigit(DBPROCESS * dbproc, DBMONEY * mnyptr, DBCHAR * digit, DBBOOL * zero)
    5472             : {
    5473             :         tdsdump_log(TDS_DBG_FUNC, "dbmnyndigit(%p, %p, %s, %p)\n", dbproc, mnyptr, digit, zero);
    5474             :         CHECK_CONN(FAIL);
    5475             :         CHECK_NULP(mnyptr, "dbmnyndigit", 2, FAIL);
    5476             :         CHECK_NULP(digit, "dbmnyndigit", 3, FAIL);
    5477             :         CHECK_NULP(zero, "dbmnyndigit", 4, FAIL);
    5478             : 
    5479             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyndigit()\n");
    5480             :         return SUCCEED;
    5481             : }
    5482             : 
    5483             : /**
    5484             :  * \ingroup dblib_money
    5485             :  * \brief Prepare a DBMONEY value for use with dbmnyndigit().
    5486             :  * 
    5487             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5488             :  * \param amount address of a DBMONEY structure.  
    5489             :  * \param trim number of digits to trim from \a amount.
    5490             :  * \param negative \em output: \c TRUE if \a amount < 0.  
    5491             :  * \retval SUCCEED Always.  
    5492             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5493             :  * \todo Unimplemented.
    5494             :  */
    5495             : RETCODE
    5496             : dbmnyinit(DBPROCESS * dbproc, DBMONEY * amount, int trim, DBBOOL * negative)
    5497             : {
    5498             :         tdsdump_log(TDS_DBG_FUNC, "dbmnyinit(%p, %p, %d, %p)\n", dbproc, amount, trim, negative);
    5499             :         CHECK_CONN(FAIL);
    5500             :         CHECK_NULP(amount, "dbmnyinit", 2, FAIL);
    5501             :         CHECK_NULP(negative, "dbmnyinit", 4, FAIL);
    5502             : 
    5503             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyinit()\n");
    5504             :         return SUCCEED;
    5505             : }
    5506             : 
    5507             : 
    5508             : /**
    5509             :  * \ingroup dblib_money
    5510             :  * \brief Divide a DBMONEY value by a positive integer.
    5511             :  * 
    5512             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5513             :  * \param amount address of a DBMONEY structure.  
    5514             :  * \param divisor of \a amount.
    5515             :  * \param remainder \em output: modulo of integer division.
    5516             :  * \retval SUCCEED Always.  
    5517             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5518             :  * \todo Unimplemented.
    5519             :  */
    5520             : RETCODE
    5521             : dbmnydown(DBPROCESS * dbproc, DBMONEY * amount, int divisor, int *remainder)
    5522             : {
    5523             :         tdsdump_log(TDS_DBG_FUNC, "dbmnydown(%p, %p, %d, %p)\n", dbproc, amount, divisor, remainder);
    5524             :         CHECK_CONN(FAIL);
    5525             :         CHECK_NULP(amount, "dbmnydown", 2, FAIL);
    5526             :         CHECK_NULP(remainder, "dbmnydown", 4, FAIL);
    5527             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnydown()\n");
    5528             :         return SUCCEED;
    5529             : }
    5530             : #endif
    5531             : 
    5532             : /**
    5533             :  * \ingroup dblib_money
    5534             :  * \brief Add $0.0001 to a DBMONEY value.
    5535             :  * 
    5536             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5537             :  * \param amount address of a DBMONEY structure.  
    5538             :  * \retval SUCCEED or FAIL if overflow or amount NULL.  
    5539             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5540             :  */
    5541             : RETCODE
    5542           0 : dbmnyinc(DBPROCESS * dbproc, DBMONEY * amount)
    5543             : {
    5544           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnyinc(%p, %p)\n", dbproc, amount);
    5545           0 :         CHECK_CONN(FAIL);
    5546           0 :         CHECK_NULP(amount, "dbmnyinc", 2, FAIL);
    5547             : 
    5548           0 :         if (amount->mnylow != 0xFFFFFFFFlu) {
    5549           0 :                 ++amount->mnylow;
    5550           0 :                 return SUCCEED;
    5551             :         }
    5552           0 :         if (amount->mnyhigh == 0x7FFFFFFFl)
    5553             :                 return FAIL;
    5554           0 :         amount->mnylow = 0;
    5555           0 :         ++amount->mnyhigh;
    5556           0 :         return SUCCEED;
    5557             : }
    5558             : 
    5559             : 
    5560             : /**
    5561             :  * \ingroup dblib_money
    5562             :  * \brief Subtract $0.0001 from a DBMONEY value.
    5563             :  *
    5564             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5565             :  * \param amount address of a DBMONEY structure.  
    5566             :  * \retval SUCCEED or FAIL if overflow or amount NULL.  
    5567             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5568             :  */
    5569             : RETCODE
    5570           0 : dbmnydec(DBPROCESS * dbproc, DBMONEY * amount)
    5571             : {
    5572           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnydec(%p, %p)\n", dbproc, amount);
    5573           0 :         CHECK_CONN(FAIL);
    5574           0 :         CHECK_NULP(amount, "dbmnydec", 2, FAIL);
    5575             : 
    5576           0 :         if (amount->mnylow != 0) {
    5577           0 :                 --amount->mnylow;
    5578           0 :                 return SUCCEED;
    5579             :         }
    5580           0 :         if (amount->mnyhigh == -0x7FFFFFFFL - 1)
    5581             :                 return FAIL;
    5582           0 :         amount->mnylow = 0xFFFFFFFFlu;
    5583           0 :         --amount->mnyhigh;
    5584           0 :         return SUCCEED;
    5585             : }
    5586             : 
    5587             : /**
    5588             :  * \ingroup dblib_money
    5589             :  * \brief Negate a DBMONEY value.
    5590             :  * 
    5591             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5592             :  * \param src address of a DBMONEY structure.  
    5593             :  * \param dest \em output: result of negation. 
    5594             :  * \retval SUCCEED or FAIL if overflow or src/dest NULL.
    5595             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5596             :  */
    5597             : RETCODE
    5598           0 : dbmnyminus(DBPROCESS * dbproc, DBMONEY * src, DBMONEY * dest)
    5599             : {
    5600           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnyminus(%p, %p, %p)\n", dbproc, src, dest);
    5601           0 :         CHECK_CONN(FAIL);
    5602           0 :         CHECK_NULP(src, "dbmnyminus", 2, FAIL);
    5603           0 :         CHECK_NULP(dest, "dbmnyminus", 3, FAIL);
    5604             : 
    5605           0 :         if (src->mnyhigh == -0x7FFFFFFFL - 1  &&  src->mnylow == 0)
    5606             :                 return FAIL;
    5607           0 :         dest->mnyhigh = -src->mnyhigh;
    5608           0 :         dest->mnylow = (~src->mnylow) + 1u;
    5609           0 :         return SUCCEED;
    5610             : }
    5611             : 
    5612             : 
    5613             : /**
    5614             :  * \ingroup dblib_money
    5615             :  * \brief Negate a DBMONEY4 value.
    5616             :  * 
    5617             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5618             :  * \param src address of a DBMONEY4 structure.  
    5619             :  * \param dest \em output: result of negation. 
    5620             :  * \retval SUCCEED usually.  
    5621             :  * \retval FAIL  on overflow.  
    5622             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5623             :  */
    5624             : RETCODE
    5625           0 : dbmny4minus(DBPROCESS * dbproc, DBMONEY4 * src, DBMONEY4 * dest)
    5626             : {
    5627             :         DBMONEY4 zero;
    5628             : 
    5629           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4minus(%p, %p, %p)\n", dbproc, src, dest);
    5630           0 :         CHECK_CONN(FAIL);
    5631           0 :         CHECK_NULP(src, "dbmny4minus", 2, FAIL);
    5632           0 :         CHECK_NULP(dest, "dbmny4minus", 3, FAIL);
    5633             : 
    5634           0 :         dbmny4zero(dbproc, &zero);
    5635           0 :         return (dbmny4sub(dbproc, &zero, src, dest));
    5636             : }
    5637             : 
    5638             : /**
    5639             :  * \ingroup dblib_money
    5640             :  * \brief Zero a DBMONEY4 value.
    5641             :  * 
    5642             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5643             :  * \param dest address of a DBMONEY structure.  
    5644             :  * \retval SUCCEED usually.  
    5645             :  * \retval FAIL  \a dest is NULL.  
    5646             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5647             :  */
    5648             : RETCODE
    5649           0 : dbmny4zero(DBPROCESS * dbproc, DBMONEY4 * dest)
    5650             : {
    5651           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4zero(%p, %p)\n", dbproc, dest);
    5652           0 :         CHECK_CONN(FAIL);
    5653           0 :         CHECK_NULP(dest, "dbmny4zero", 2, FAIL);
    5654             : 
    5655           0 :         dest->mny4 = 0;
    5656           0 :         return SUCCEED;
    5657             : }
    5658             : 
    5659             : /**
    5660             :  * \ingroup dblib_money
    5661             :  * \brief Add two DBMONEY4 values.
    5662             :  * 
    5663             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5664             :  * \param m1 first operand.
    5665             :  * \param m2 other operand. 
    5666             :  * \param sum \em output: result of computation.  
    5667             :  * \retval SUCCEED usually.  
    5668             :  * \retval FAIL  on overflow.  
    5669             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5670             :  */
    5671             : RETCODE
    5672           0 : dbmny4add(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * sum)
    5673             : {
    5674           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4add(%p, %p, %p, %p)\n", dbproc, m1, m2, sum);
    5675           0 :         CHECK_CONN(FAIL);
    5676           0 :         CHECK_NULP(m1, "dbmny4add", 2, FAIL);
    5677           0 :         CHECK_NULP(m2, "dbmny4add", 3, FAIL);
    5678           0 :         CHECK_NULP(sum, "dbmny4add", 4, FAIL);
    5679             : 
    5680           0 :         sum->mny4 = m1->mny4 + m2->mny4;
    5681           0 :         if (((m1->mny4 < 0) && (m2->mny4 < 0) && (sum->mny4 >= 0))
    5682           0 :             || ((m1->mny4 > 0) && (m2->mny4 > 0) && (sum->mny4 <= 0))) {
    5683             :                 /* overflow */
    5684           0 :                 sum->mny4 = 0;
    5685           0 :                 return FAIL;
    5686             :         }
    5687             :         return SUCCEED;
    5688             : }
    5689             : 
    5690             : /**
    5691             :  * \ingroup dblib_money
    5692             :  * \brief Subtract two DBMONEY4 values.
    5693             :  * 
    5694             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5695             :  * \param m1 first operand.
    5696             :  * \param m2 other operand, subtracted from \a m1. 
    5697             :  * \param diff \em output: result of computation.  
    5698             :  * \retval SUCCEED usually.  
    5699             :  * \retval FAIL  on overflow.  
    5700             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5701             :  */
    5702             : RETCODE
    5703           0 : dbmny4sub(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * diff)
    5704             : {
    5705             : 
    5706           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4sub(%p, %p, %p, %p)\n", dbproc, m1, m2, diff);
    5707           0 :         CHECK_CONN(FAIL);
    5708           0 :         CHECK_NULP(m1, "dbmny4sub", 2, FAIL);
    5709           0 :         CHECK_NULP(m2, "dbmny4sub", 3, FAIL);
    5710           0 :         CHECK_NULP(diff, "dbmny4sub", 4, FAIL);
    5711             : 
    5712           0 :         diff->mny4 = m1->mny4 - m2->mny4;
    5713           0 :         if (((m1->mny4 <= 0) && (m2->mny4 > 0) && (diff->mny4 > 0))
    5714           0 :             || ((m1->mny4 >= 0) && (m2->mny4 < 0) && (diff->mny4 < 0))) {
    5715             :                 /* overflow */
    5716           0 :                 diff->mny4 = 0;
    5717           0 :                 return FAIL;
    5718             :         }
    5719             :         return SUCCEED;
    5720             : }
    5721             : 
    5722             : #if defined(DBLIB_UNIMPLEMENTED)
    5723             : /**
    5724             :  * \ingroup dblib_money
    5725             :  * \brief Multiply two DBMONEY4 values.
    5726             :  * 
    5727             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5728             :  * \param m1 first operand.
    5729             :  * \param m2 other operand. 
    5730             :  * \param prod \em output: result of computation.  
    5731             :  * \retval SUCCEED usually.  
    5732             :  * \retval FAIL a parameter is NULL.  
    5733             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5734             :  * \todo Unimplemented.
    5735             :  */
    5736             : RETCODE
    5737             : dbmny4mul(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * prod)
    5738             : {
    5739             : 
    5740             :         tdsdump_log(TDS_DBG_FUNC, "dbmny4mul(%p, %p, %p, %p)\n", dbproc, m1, m2, prod);
    5741             :         CHECK_CONN(FAIL);
    5742             :         CHECK_NULP(m1, "dbmny4mul", 2, FAIL);
    5743             :         CHECK_NULP(m2, "dbmny4mul", 3, FAIL);
    5744             :         CHECK_NULP(prod, "dbmny4mul", 4, FAIL);
    5745             : 
    5746             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmny4mul()\n");
    5747             :         return FAIL;
    5748             : }
    5749             : 
    5750             : /**
    5751             :  * \ingroup dblib_money
    5752             :  * \brief Divide two DBMONEY4 values.
    5753             :  * 
    5754             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5755             :  * \param m1 dividend.
    5756             :  * \param m2 divisor. 
    5757             :  * \param quotient \em output: result of computation.  
    5758             :  * \retval SUCCEED usually.  
    5759             :  * \retval FAIL a parameter is NULL.  
    5760             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5761             :  * \todo Unimplemented.
    5762             :  */
    5763             : RETCODE
    5764             : dbmny4divide(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * quotient)
    5765             : {
    5766             : 
    5767             :         tdsdump_log(TDS_DBG_FUNC, "dbmny4divide(%p, %p, %p, %p)\n", dbproc, m1, m2, quotient);
    5768             :         CHECK_CONN(FAIL);
    5769             :         CHECK_NULP(m1, "dbmny4divide", 2, FAIL);
    5770             :         CHECK_NULP(m2, "dbmny4divide", 3, FAIL);
    5771             :         CHECK_NULP(quotient, "dbmny4divide", 4, FAIL);
    5772             : 
    5773             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmny4divide()\n");
    5774             :         return FAIL;
    5775             : }
    5776             : #endif
    5777             : 
    5778             : /**
    5779             :  * \ingroup dblib_money
    5780             :  * \brief Compare two DBMONEY4 values.
    5781             :  * 
    5782             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5783             :  * \param m1 some money.
    5784             :  * \param m2 some other money. 
    5785             :  * \retval 0 m1 == m2. 
    5786             :  * \retval -1 m1 < m2.
    5787             :  * \retval  1 m1 > m2.
    5788             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5789             :  */
    5790             : int
    5791           0 : dbmny4cmp(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2)
    5792             : {
    5793             : 
    5794           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4cmp(%p, %p, %p)\n", dbproc, m1, m2);
    5795           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    5796           0 :         CHECK_NULP(m1, "dbmny4cmp", 2, 0);
    5797           0 :         CHECK_NULP(m2, "dbmny4cmp", 3, 0);
    5798             : 
    5799           0 :         if (m1->mny4 < m2->mny4) {
    5800             :                 return -1;
    5801             :         }
    5802           0 :         if (m1->mny4 > m2->mny4) {
    5803             :                 return 1;
    5804             :         }
    5805           0 :         return 0;
    5806             : }
    5807             : 
    5808             : /**
    5809             :  * \ingroup dblib_money
    5810             :  * \brief Copy a DBMONEY4 value.
    5811             :  * 
    5812             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5813             :  * \param src address of a DBMONEY4 structure.  
    5814             :  * \param dest \em output: new money. 
    5815             :  * \retval SUCCEED or FAIL if src/dest NULL.
    5816             :  * \sa dbmnycopy(), dbmnyminus(), dbmny4minus(). 
    5817             :  */
    5818             : RETCODE
    5819           0 : dbmny4copy(DBPROCESS * dbproc, DBMONEY4 * src, DBMONEY4 * dest)
    5820             : {
    5821             : 
    5822           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4copy(%p, %p, %p)\n", dbproc, src, dest);
    5823           0 :         CHECK_CONN(FAIL);
    5824           0 :         CHECK_NULP(src, "dbmny4copy", 2, FAIL);
    5825           0 :         CHECK_NULP(dest, "dbmny4copy", 3, FAIL);
    5826             : 
    5827           0 :         dest->mny4 = src->mny4;
    5828           0 :         return SUCCEED;
    5829             : }
    5830             : 
    5831             : /**
    5832             :  * \ingroup dblib_datetime
    5833             :  * \brief Compare DBDATETIME values, similar to strcmp(3).
    5834             :  * 
    5835             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5836             :  * \param d1 a \c DBDATETIME structure address
    5837             :  * \param d2 another \c DBDATETIME structure address
    5838             :  * \retval   0 d1 = d2.
    5839             :  * \retval  -1 d1 < d2.
    5840             :  * \retval   1 d1 > d2.
    5841             :  * \sa dbdate4cmp(), dbmnycmp(), dbmny4cmp().
    5842             :  */
    5843             : int
    5844           0 : dbdatecmp(DBPROCESS * dbproc, DBDATETIME * d1, DBDATETIME * d2)
    5845             : {
    5846           0 :         tdsdump_log(TDS_DBG_FUNC, "dbdatecmp(%p, %p, %p)\n", dbproc, d1, d2);
    5847           0 :         CHECK_CONN(FAIL);
    5848           0 :         CHECK_NULP(d1, "dbdatecmp", 2, 0);
    5849           0 :         CHECK_NULP(d2, "dbdatecmp", 3, 0);
    5850             : 
    5851           0 :         if (d1->dtdays == d2->dtdays) {
    5852           0 :                 if (d1->dttime == d2->dttime)
    5853             :                         return 0;
    5854           0 :                 return d1->dttime > d2->dttime ? 1 : -1;
    5855             :         }
    5856             : 
    5857             :         /* date 1 is before 1900 */
    5858           0 :         if (d1->dtdays > 2958463) {
    5859             : 
    5860           0 :                 if (d2->dtdays > 2958463) /* date 2 is before 1900 */
    5861           0 :                         return d1->dtdays > d2->dtdays ? 1 : -1;
    5862             :                 return -1;
    5863             :         }
    5864             : 
    5865             :         /* date 1 is after 1900 */
    5866           0 :         if (d2->dtdays < 2958463) /* date 2 is after 1900 */
    5867           0 :                 return d1->dtdays > d2->dtdays ? 1 : -1;
    5868             :         return 1;
    5869             : }
    5870             : 
    5871             : static RETCODE
    5872          24 : dblib_datecrack(DBPROCESS * dbproc, BOOL nano_precision, DBDATEREC * output, int type, const void * data)
    5873             : {
    5874             :         TDSDATEREC dr;
    5875          24 :         struct tds_sybase_dbdaterec *di = (struct tds_sybase_dbdaterec*) output;
    5876             : 
    5877          24 :         tdsdump_log(TDS_DBG_FUNC, "dblib_datecrack(%p, %d, %p, %d, %p)\n", dbproc, nano_precision, output, type, data);
    5878          24 :         CHECK_NULP(output, "dbdatecrack", 2, FAIL);
    5879          24 :         CHECK_PARAMETER(data, SYBENDTP, FAIL);
    5880             : 
    5881          24 :         if (TDS_FAILED(tds_datecrack(type, data, &dr)))
    5882             :                 return FAIL;
    5883             : 
    5884          24 :         di->dateyear = dr.year;
    5885          24 :         di->quarter = dr.quarter;
    5886          24 :         di->datemonth = dr.month;
    5887          24 :         di->datedmonth = dr.day;
    5888          24 :         di->datedyear = dr.dayofyear;
    5889          24 :         di->datedweek = dr.weekday;
    5890          24 :         di->datehour = dr.hour;
    5891          24 :         di->dateminute = dr.minute;
    5892          24 :         di->datesecond = dr.second;
    5893          24 :         di->datetzone = dr.timezone;
    5894          24 :         if (nano_precision)
    5895             :                 /* here we are writing to nanosecond field */
    5896           4 :                 di->datemsecond = dr.decimicrosecond * 100u;
    5897             :         else
    5898          20 :                 di->datemsecond = dr.decimicrosecond / 10000u;
    5899             :         /* Revert to compiled-in default if dbproc can't be used to find the runtime override. */
    5900          24 :         if (dbproc ? dbproc->msdblib : dblib_msdblib) {
    5901           0 :                 ++di->quarter;
    5902           0 :                 ++di->datemonth;
    5903           0 :                 ++di->datedweek;
    5904             :         }
    5905             :         return SUCCEED;
    5906             : }
    5907             : 
    5908             : /**
    5909             :  * \ingroup dblib_core
    5910             :  * \brief Break a DBDATETIME value into useful pieces.
    5911             :  *
    5912             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5913             :  * \param di \em output: structure to contain the exploded parts of \a datetime.
    5914             :  * \param datetime \em input: \c DBDATETIME to be converted.
    5915             :  * \retval SUCCEED always.
    5916             :  * \remarks The members of \a di have different names, depending on whether \c --with-msdblib was configured.
    5917             :  *
    5918             :  * If DBPROCESS is NULL, dbdatecrack() uses the compiled in default
    5919             :  * value of MSDBLIB as of when libsybdb was compiled, irrespective of its value when the
    5920             :  * application is compiled.  This can lead to incorrect results because Sybase and Microsoft use different
    5921             :  * ranges -- [0,11] vs. [1,12] -- for the month.
    5922             :  *
    5923             :  * \sa dbconvert(), dbdata(), dbdatechar(), dbdatename(), dbdatepart(), tdsdbopen().
    5924             :  */
    5925             : RETCODE
    5926          20 : dbdatecrack(DBPROCESS * dbproc, DBDATEREC * di, DBDATETIME * datetime)
    5927             : {
    5928          20 :         return dblib_datecrack(dbproc, FALSE, di, SYBDATETIME, datetime);
    5929             : }
    5930             : 
    5931             : /**
    5932             :  * \ingroup dblib_core
    5933             :  * \brief Break any kind of date or time value into useful pieces.
    5934             :  *
    5935             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5936             :  * \param di \em output: structure to contain the exploded parts of \a datetime.
    5937             :  * \param type \em input: \c type of date/time value returned by dbcoltype().
    5938             :  * \param data \em input: \c date/time value to be converted.
    5939             :  * \retval SUCCEED always.
    5940             :  * \remarks The members of \a di have different names, depending on whether \c --with-msdblib was configured.
    5941             :  *
    5942             :  * This is an extension to dbdatecrack(), see it for more information.
    5943             :  *
    5944             :  * \sa dbdatecrack(), dbconvert(), dbdata(), dbdatechar(), dbdatename(), dbdatepart(), tdsdbopen().
    5945             :  */
    5946             : RETCODE
    5947           4 : dbanydatecrack(DBPROCESS * dbproc, DBDATEREC2 * di, int type, const void *data)
    5948             : {
    5949           4 :         return dblib_datecrack(dbproc, TRUE, (DBDATEREC *) di, type, data);
    5950             : }
    5951             : 
    5952             : #if defined(DBLIB_UNIMPLEMENTED)
    5953             : /**
    5954             :  * \ingroup dblib_core
    5955             :  * \brief Clear remote passwords from the LOGINREC structure.
    5956             :  * 
    5957             :  * \param login structure to pass to dbopen().
    5958             :  * \sa dblogin(), dbopen(), dbrpwset(), DBSETLAPP(), DBSETLHOST(), DBSETLPWD(), DBSETLUSER().  
    5959             :  * \remarks Useful for remote stored procedure calls, but not in high demand from FreeTDS.  
    5960             :  * \todo Unimplemented.
    5961             :  */
    5962             : void
    5963             : dbrpwclr(LOGINREC * login)
    5964             : {
    5965             :         tdsdump_log(TDS_DBG_FUNC, "dbrpwclr(%p)\n", login);
    5966             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbrpwclr()\n");
    5967             : }
    5968             : 
    5969             : /**
    5970             :  * \ingroup dblib_core
    5971             :  * \brief Add a remote password to the LOGINREC structure.
    5972             :  * 
    5973             :  * \param login structure to pass to dbopen().
    5974             :  * \param srvname server for which \a password should be used.  
    5975             :  * \param password you guessed it, let's hope no else does.  
    5976             :  * \param pwlen count of \a password, in bytes.
    5977             :  * \remarks Useful for remote stored procedure calls, but not in high demand from FreeTDS.  
    5978             :  * \sa dblogin(), dbopen(), dbrpwclr(), DBSETLAPP(), DBSETLHOST(), DBSETLPWD(), DBSETLUSER().  
    5979             :  * \todo Unimplemented.
    5980             :  */
    5981             : RETCODE
    5982             : dbrpwset(LOGINREC * login, char *srvname, char *password, int pwlen)
    5983             : {
    5984             :         tdsdump_log(TDS_DBG_FUNC, "dbrpwset(%p, %s, %s, %d)\n", login, srvname, password, pwlen);
    5985             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbrpwset()\n");
    5986             :         return SUCCEED;
    5987             : }
    5988             : #endif
    5989             : 
    5990             : /**
    5991             :  * \ingroup dblib_core
    5992             :  * \brief Get server process ID for a \c DBPROCESS.
    5993             :  * 
    5994             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5995             :  * \return \em "spid", the server's process ID.  
    5996             :  * \sa dbopen().  
    5997             :  */
    5998             : int
    5999          10 : dbspid(DBPROCESS * dbproc)
    6000             : {
    6001          10 :         tdsdump_log(TDS_DBG_FUNC, "dbspid(%p)\n", dbproc);
    6002          10 :         CHECK_CONN(-1);
    6003             : 
    6004          10 :         return dbproc->tds_socket->conn->spid;
    6005             : }
    6006             : 
    6007             : /**
    6008             :  * \ingroup dblib_core
    6009             :  * \brief Associate client-allocated (and defined) data with a \c DBPROCESS.   
    6010             :  * 
    6011             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6012             :  * \param ptr address of client-defined data.
    6013             :  * \remarks \a ptr is the location of user data that \c db-lib will associate with \a dbproc. 
    6014             :  * The client allocates the buffer addressed by \a ptr.  \c db-lib never examines or uses the information; 
    6015             :  * it just stashes the pointer for later retrieval by the application with \c dbgetuserdata().  
    6016             :  * \sa dbgetuserdata().  
    6017             :  */
    6018             : void
    6019         262 : dbsetuserdata(DBPROCESS * dbproc, BYTE * ptr)
    6020             : {
    6021         262 :         tdsdump_log(TDS_DBG_FUNC, "dbsetuserdata(%p, %p)\n", dbproc, ptr);
    6022         262 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    6023             : 
    6024         262 :         dbproc->user_data = ptr;
    6025             : }
    6026             : 
    6027             : /**
    6028             :  * \ingroup dblib_core
    6029             :  * \brief Get address of user-allocated data from a \c DBPROCESS.   
    6030             :  * 
    6031             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6032             :  * \return address of user-defined data that \c db-lib associated with \a dbproc when the client called  dbsetuserdata(). 
    6033             :  * \retval undefined (probably \c NULL) dbsetuserdata() was not previously called.  
    6034             :  * \sa dbsetuserdata().  
    6035             :  */
    6036             : BYTE *
    6037         194 : dbgetuserdata(DBPROCESS * dbproc)
    6038             : {
    6039         194 :         tdsdump_log(TDS_DBG_FUNC, "dbgetuserdata(%p)\n", dbproc);
    6040         194 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    6041             : 
    6042         194 :         return dbproc->user_data;
    6043             : }
    6044             : 
    6045             : /**
    6046             :  * \ingroup dblib_core
    6047             :  * \brief Specify a db-lib version level.
    6048             :  * 
    6049             :  * \param version anything, really. 
    6050             :  * \retval SUCCEED Always.  
    6051             :  * \remarks No effect on behavior of \c db-lib in \c FreeTDS.  
    6052             :  * \sa 
    6053             :  */
    6054             : RETCODE
    6055          40 : dbsetversion(DBINT version)
    6056             : {
    6057          40 :         tdsdump_log(TDS_DBG_FUNC, "dbsetversion(%d)\n", version);
    6058             : 
    6059          40 :         switch (version) {
    6060          40 :         case DBVERSION_42:
    6061             :         case DBVERSION_46:
    6062             :         case DBVERSION_100:
    6063             :         case DBVERSION_70:
    6064             :         case DBVERSION_71:
    6065             :         case DBVERSION_72:
    6066             :         case DBVERSION_73:
    6067             :         case DBVERSION_74:
    6068          40 :                 g_dblib_version = version;
    6069          40 :                 return SUCCEED;
    6070             :         default:
    6071             :                 break;
    6072             :         }
    6073             :         
    6074           0 :         dbperror(NULL, SYBEIVERS, 0);
    6075           0 :         return FAIL;
    6076             : }
    6077             : 
    6078             : /**
    6079             :  * \ingroup dblib_money
    6080             :  * \brief Copy a DBMONEY value.
    6081             :  * 
    6082             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6083             :  * \param src address of a DBMONEY structure.  
    6084             :  * \param dest \em output: new money. 
    6085             :  * \retval SUCCEED always, unless \a src or \a dest is \c NULL.  
    6086             :  * \sa 
    6087             :  */
    6088             : RETCODE
    6089           0 : dbmnycopy(DBPROCESS * dbproc, DBMONEY * src, DBMONEY * dest)
    6090             : {
    6091           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnycopy(%p, %p, %p)\n", dbproc, src, dest);
    6092           0 :         CHECK_CONN(FAIL);
    6093           0 :         CHECK_NULP(src, "dbmnycopy", 2, FAIL);
    6094           0 :         CHECK_NULP(dest, "dbmnycopy", 3, FAIL);
    6095             : 
    6096           0 :         dest->mnylow = src->mnylow;
    6097           0 :         dest->mnyhigh = src->mnyhigh;
    6098           0 :         return SUCCEED;
    6099             : }
    6100             : 
    6101             : 
    6102             : /**
    6103             :  * \ingroup dblib_core
    6104             :  * \brief Cancel the query currently being retrieved, discarding all pending rows.  
    6105             :  * 
    6106             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6107             :  * \sa 
    6108             :  */
    6109             : RETCODE
    6110         814 : dbcanquery(DBPROCESS * dbproc)
    6111             : {
    6112             :         TDSRET rc;
    6113             :         TDS_INT result_type;
    6114             : 
    6115         814 :         tdsdump_log(TDS_DBG_FUNC, "dbcanquery(%p)\n", dbproc);
    6116         814 :         CHECK_CONN(FAIL);
    6117             : 
    6118             :         /* Just throw away all pending rows from the last query */
    6119             : 
    6120         814 :         rc = tds_process_tokens(dbproc->tds_socket, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE);
    6121             : 
    6122         814 :         if (TDS_FAILED(rc))
    6123             :                 return FAIL;
    6124             : 
    6125         814 :         dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    6126             : 
    6127         814 :         return SUCCEED;
    6128             : }
    6129             : 
    6130             : /**
    6131             :  * \ingroup dblib_core
    6132             :  * \brief Erase the command buffer, in case \c DBNOAUTOFREE was set with dbsetopt(). 
    6133             :  * 
    6134             :  * 
    6135             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6136             :  * \sa dbcmd(), dbfcmd(), dbgetchar(), dbsqlexec(), dbsqlsend(), dbsetopt(), dbstrcpy(), dbstrlen().  
    6137             :  */
    6138             : void
    6139       20724 : dbfreebuf(DBPROCESS * dbproc)
    6140             : {
    6141       20724 :         tdsdump_log(TDS_DBG_FUNC, "dbfreebuf(%p)\n", dbproc);
    6142       20724 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    6143             : 
    6144       20724 :         if (dbproc->dbbuf)
    6145       20704 :                 TDS_ZERO_FREE(dbproc->dbbuf);
    6146       20724 :         dbproc->dbbufsz = 0;
    6147             : }
    6148             : 
    6149             : /**
    6150             :  * \ingroup dblib_core
    6151             :  * \brief Reset an option.
    6152             :  * 
    6153             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6154             :  * \param option to be turned off.
    6155             :  * \param param clearing some options requires a parameter, believe it or not.  
    6156             :  * \retval SUCCEED \a option and \a parameter seem sane.
    6157             :  * \retval FAIL no such \a option.
    6158             :  * \remarks Only the following options are recognized:
    6159             :         - DBARITHABORT
    6160             :         - DBARITHIGNORE
    6161             :         - DBCHAINXACTS
    6162             :         - DBFIPSFLAG
    6163             :         - DBISOLATION
    6164             :         - DBNOCOUNT
    6165             :         - DBNOEXEC
    6166             :         - DBPARSEONLY
    6167             :         - DBSHOWPLAN
    6168             :         - DBSTORPROCID
    6169             :         - DBQUOTEDIDENT
    6170             :         - DBSETTIME
    6171             :  * \sa dbisopt(), dbsetopt().
    6172             :  */
    6173             : RETCODE
    6174          20 : dbclropt(DBPROCESS * dbproc, int option, const char param[])
    6175             : {
    6176             :         char *cmd;
    6177             : 
    6178          20 :         tdsdump_log(TDS_DBG_FUNC, "dbclropt(%p, %d, %s)\n", dbproc, option, param);
    6179          20 :         CHECK_CONN(FAIL);
    6180          20 :         if (option != DBSETTIME) {
    6181           0 :                 CHECK_NULP(param, "dbclropt", 3, FAIL);
    6182             :         }
    6183             : 
    6184          20 :         if ((option < 0) || (option >= DBNUMOPTIONS)) {
    6185             :                 return FAIL;
    6186             :         }
    6187          20 :         dbproc->dbopts[option].factive = 0;
    6188             :         switch (option) {
    6189           0 :         case DBARITHABORT:
    6190             :         case DBARITHIGNORE:
    6191             :         case DBCHAINXACTS:
    6192             :         case DBFIPSFLAG:
    6193             :         case DBISOLATION:
    6194             :         case DBNOCOUNT:
    6195             :         case DBNOEXEC:
    6196             :         case DBPARSEONLY:
    6197             :         case DBSHOWPLAN:
    6198             :         case DBSTORPROCID:
    6199             :         case DBQUOTEDIDENT:
    6200             :                 /* server options (on/off) */
    6201           0 :                 if (asprintf(&cmd, "set %s off\n", dbproc->dbopts[option].text) < 0) {
    6202             :                         return FAIL;
    6203             :                 }
    6204           0 :                 dbstring_concat(&(dbproc->dboptcmd), cmd);
    6205           0 :                 free(cmd);
    6206           0 :                 break;
    6207           0 :         case DBBUFFER:
    6208           0 :                 buffer_set_capacity(dbproc, 1); /* frees row_buf->rows */
    6209           0 :                 return SUCCEED;
    6210             :                 break;
    6211          20 :         case DBSETTIME:
    6212          20 :                 tds_mutex_lock(&dblib_mutex);
    6213             :                 /*
    6214             :                  * Use global value of query timeout set by dbsettime() if any,
    6215             :                  * otherwise set it to zero just like tds_init_socket() does.
    6216             :                  */
    6217          20 :                 if (g_dblib_ctx.query_timeout > 0) {
    6218          10 :                         dbproc->tds_socket->query_timeout = g_dblib_ctx.query_timeout;
    6219             :                 } else {
    6220          10 :                         dbproc->tds_socket->query_timeout = 0;
    6221             :                 }
    6222          20 :                 tds_mutex_unlock(&dblib_mutex);
    6223          20 :                 return SUCCEED;
    6224             :                 break;
    6225             :         default:
    6226             :                 break;
    6227             :         }
    6228           0 :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbclropt(option = %d)\n", option);
    6229             :         return FAIL;
    6230             : }
    6231             : 
    6232             : /**
    6233             :  * \ingroup dblib_core
    6234             :  * \brief Get value of an option
    6235             :  * 
    6236             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6237             :  * \param option the option
    6238             :  * \param param a parameter to \a option. 
    6239             :  * \sa dbclropt(), dbsetopt().
    6240             :  */
    6241             : DBBOOL
    6242         180 : dbisopt(DBPROCESS * dbproc, int option, const char param[])
    6243             : {
    6244         180 :         tdsdump_log(TDS_DBG_FUNC, "dbisopt(%p, %d, %s)\n", dbproc, option, param);
    6245         180 :         CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
    6246             :         /* sometimes param can be NULL */
    6247             :         
    6248         180 :         if ((option < 0) || (option >= DBNUMOPTIONS)) {
    6249             :                 return FALSE;
    6250             :         }
    6251         180 :         return dbproc->dbopts[option].factive;
    6252             : }
    6253             : 
    6254             : /** \internal
    6255             :  * \ingroup dblib_internal
    6256             :  * \brief Get number of the row currently being read.
    6257             :  * 
    6258             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6259             :  * \return ostensibly the row number, or 0 if no rows have been read yet.  
    6260             :  * \retval 0 Always.  
    6261             :  * \sa DBCURROW(), dbclrbuf(), DBFIRSTROW(), dbgetrow(), DBLASTROW(), dbnextrow(), dbsetopt(),.  
    6262             :  * \todo Unimplemented.
    6263             :  */
    6264             : DBINT
    6265           0 : dbcurrow(DBPROCESS * dbproc)
    6266             : {
    6267           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcurrow(%p)\n", dbproc);
    6268           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    6269           0 :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbcurrow()\n");
    6270             :         return 0;
    6271             : }
    6272             : 
    6273             : 
    6274             : /** \internal
    6275             :  * \ingroup dblib_internal
    6276             :  * \brief Get returned row's type.
    6277             :  * 
    6278             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6279             :  * \sa DBROWTYPE().  
    6280             :  */
    6281             : STATUS
    6282           0 : dbrowtype(DBPROCESS * dbproc)
    6283             : {
    6284           0 :         tdsdump_log(TDS_DBG_FUNC, "dbrowtype(%p)\n", dbproc);
    6285           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NO_MORE_ROWS);
    6286           0 :         return dbproc->row_type;
    6287             : }
    6288             : 
    6289             : 
    6290             : /** \internal
    6291             :  * \ingroup dblib_internal
    6292             :  * \brief Get number of the row just returned.
    6293             :  * 
    6294             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6295             :  * \sa DBCURROW().
    6296             :  * \todo Unimplemented.
    6297             :  */
    6298             : int
    6299           0 : dbcurcmd(DBPROCESS * dbproc)
    6300             : {
    6301           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcurcmd(%p)\n", dbproc);
    6302           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    6303           0 :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbcurcmd()\n");
    6304             :         return 0;
    6305             : }
    6306             : 
    6307             : 
    6308             : /** 
    6309             :  * \ingroup dblib_core
    6310             :  * \brief See if more commands are to be processed.
    6311             :  * 
    6312             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6313             :  * \sa DBMORECMDS(). DBCMDROW(), dbresults(), DBROWS(), DBROWTYPE().  
    6314             :  */
    6315             : RETCODE
    6316          30 : dbmorecmds(DBPROCESS * dbproc)
    6317             : {
    6318          30 :         tdsdump_log(TDS_DBG_FUNC, "dbmorecmds(%p)\n", dbproc);
    6319          30 :         CHECK_CONN(FAIL);
    6320             : 
    6321          30 :         if (dbproc->tds_socket->res_info == NULL) {
    6322             :                 return FAIL;
    6323             :         }
    6324             : 
    6325          30 :         if (!dbproc->tds_socket->res_info->more_results) {
    6326          20 :                 tdsdump_log(TDS_DBG_FUNC, "more_results is false; returns FAIL\n");
    6327             :                 return FAIL;
    6328             :         }
    6329             :         
    6330          10 :         tdsdump_log(TDS_DBG_FUNC, "more_results is true; returns SUCCEED\n");
    6331             :         
    6332             :         return SUCCEED;
    6333             : }
    6334             : 
    6335             : /**
    6336             :  * \ingroup dblib_rpc
    6337             :  * \brief Get datatype of a stored procedure's return parameter.
    6338             :  * 
    6339             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6340             :  * \param retnum Nth return parameter, between 1 and \c dbnumrets().  
    6341             :  * \return SYB* datatype token, or -1 if \a retnum is out of range. 
    6342             :  * \sa dbnextrow(), dbnumrets(), dbprtype(), dbresults(), dbretdata(), dbretlen(), dbretname(), dbrpcinit(), dbrpcparam(). 
    6343             :  */
    6344             : int
    6345          66 : dbrettype(DBPROCESS * dbproc, int retnum)
    6346             : {
    6347             :         TDSCOLUMN *colinfo;
    6348             : 
    6349          66 :         tdsdump_log(TDS_DBG_FUNC, "dbrettype(%p, %d)\n", dbproc, retnum);
    6350          66 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    6351          66 :         assert(dbproc->tds_socket);
    6352          66 :         assert(dbproc->tds_socket->param_info);
    6353             : 
    6354          66 :         if (retnum < 1 || retnum > dbproc->tds_socket->param_info->num_cols)
    6355             :                 return -1;
    6356             : 
    6357          66 :         colinfo = dbproc->tds_socket->param_info->columns[retnum - 1];
    6358             : 
    6359          66 :         return tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    6360             : }
    6361             : 
    6362             : /**
    6363             :  * \ingroup dblib_core
    6364             :  * \brief Get size of the command buffer, in bytes. 
    6365             :  * 
    6366             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6367             :  * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbgetchar(), dbstrcpy(). 
    6368             :  */
    6369             : int
    6370           0 : dbstrlen(DBPROCESS * dbproc)
    6371             : {
    6372           0 :         tdsdump_log(TDS_DBG_FUNC, "dbstrlen(%p)\n", dbproc);
    6373           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    6374             :         
    6375           0 :         return dbproc->dbbufsz;
    6376             : }
    6377             : 
    6378             : 
    6379             : /**
    6380             :  * \ingroup dblib_core
    6381             :  * \brief Get address of a position in the command buffer.
    6382             :  * 
    6383             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6384             :  * \param pos offset within the command buffer, starting at \em 0.   
    6385             :  * \remarks A bit overspecialized, this one.  
    6386             :  * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbstrcpy(), dbstrlen(),
    6387             :  */
    6388             : char *
    6389         490 : dbgetchar(DBPROCESS * dbproc, int pos)
    6390             : {
    6391         490 :         tdsdump_log(TDS_DBG_FUNC, "dbgetchar(%p, %d)\n", dbproc, pos);
    6392         490 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    6393         490 :         tdsdump_log(TDS_DBG_FUNC, "dbgetchar() bufsz = %d, pos = %d\n", dbproc->dbbufsz, pos);
    6394             : 
    6395         490 :         if (dbproc->dbbufsz > 0) {
    6396         490 :                 if (pos >= 0 && pos < (dbproc->dbbufsz - 1))
    6397         480 :                         return (char *) &dbproc->dbbuf[pos];
    6398             :                 return NULL;
    6399             :         }
    6400             :         return NULL;
    6401             : }
    6402             : 
    6403             : /**
    6404             :  * \ingroup dblib_core
    6405             :  * \brief Get a copy of a chunk of the command buffer.
    6406             :  * 
    6407             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6408             :  * \param start position in the command buffer to start copying from, starting from \em 0.  
    6409             :  *      If start is past the end of the command buffer, dbstrcpy() inserts a null terminator at dest[0].
    6410             :  * \param numbytes number of bytes to copy. 
    6411             :         - If -1, dbstrcpy() copies the whole command buffer.  
    6412             :         - If  0 dbstrcpy() writes a \c NULL to dest[0]. 
    6413             :         - If the command buffer contains fewer than \a numbytes (taking \a start into account) dbstrcpy() 
    6414             :         copies the rest of it.  
    6415             :  * \param dest \em output: the buffer to write to.  Make sure it's big enough.  
    6416             :  * \retval SUCCEED the inputs were valid and \a dest was affected.  
    6417             :  * \retval FAIL \a start < 0 or \a numbytes < -1.  
    6418             :  * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbgetchar(), dbstrlen().  
    6419             :  */
    6420             : RETCODE
    6421           0 : dbstrcpy(DBPROCESS * dbproc, int start, int numbytes, char *dest)
    6422             : {
    6423           0 :         tdsdump_log(TDS_DBG_FUNC, "dbstrcpy(%p, %d, %d, %s)\n", dbproc, start, numbytes, dest);
    6424           0 :         CHECK_CONN(FAIL);
    6425           0 :         CHECK_NULP(dest, "dbstrcpy", 4, FAIL);
    6426             : 
    6427           0 :         if (start < 0) {
    6428           0 :                 dbperror(dbproc, SYBENSIP, 0);
    6429           0 :                 return FAIL;
    6430             :         }
    6431           0 :         if (numbytes < -1) {
    6432           0 :                 dbperror(dbproc, SYBEBNUM, 0);
    6433           0 :                 return FAIL;
    6434             :         }
    6435           0 :         dest[0] = 0;            /* start with empty string being returned */
    6436           0 :         if (dbproc->dbbufsz > 0 && start < dbproc->dbbufsz) {
    6437           0 :                 if (numbytes == -1)
    6438           0 :                         numbytes = dbproc->dbbufsz - start;
    6439           0 :                 if (start + numbytes > dbproc->dbbufsz)
    6440           0 :                         numbytes = dbproc->dbbufsz - start;
    6441           0 :                 memcpy(dest, (char *) &dbproc->dbbuf[start], numbytes);
    6442           0 :                 dest[numbytes] = '\0';
    6443             :         }
    6444             :         return SUCCEED;
    6445             : }
    6446             : 
    6447             : /**
    6448             :  * \ingroup dblib_core
    6449             :  * \brief safely quotes character values in SQL text.  
    6450             :  * 
    6451             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6452             :  * \param src input string.
    6453             :  * \param srclen length of \a src in bytes, or -1 to indicate it's null-terminated.  
    6454             :  * \param dest \em output: client-provided output buffer. 
    6455             :  * \param destlen size of \a dest in bytes, or -1 to indicate it's "big enough" and the data should be null-terminated.  
    6456             :  * \param quotetype
    6457             :         - \c DBSINGLE Doubles all single quotes (').
    6458             :         - \c DBDOUBLE Doubles all double quotes (").
    6459             :         - \c DBBOTH   Doubles all single and double quotes.
    6460             :  * \retval SUCCEED everything worked.
    6461             :  * \retval FAIL no such \a quotetype, or insufficient room in \a dest.
    6462             :  * \sa dbcmd(), dbfcmd(). 
    6463             :  */
    6464             : RETCODE
    6465          50 : dbsafestr(DBPROCESS * dbproc, const char *src, DBINT srclen, char *dest, DBINT destlen, int quotetype)
    6466             : {
    6467          50 :         int i, j = 0;
    6468          50 :         bool squote = false, dquote = false;
    6469             : 
    6470          50 :         tdsdump_log(TDS_DBG_FUNC, "dbsafestr(%p, %s, %d, %s, %d, %d)\n", dbproc, src, srclen, dest, destlen, quotetype);
    6471          50 :         CHECK_NULP(src, "dbsafestr", 2, FAIL);
    6472          50 :         CHECK_NULP(dest, "dbsafestr", 4, FAIL);
    6473             : 
    6474             :         /* check parameters */
    6475          50 :         if (srclen < -1 || destlen < -1)
    6476             :                 return FAIL;
    6477             : 
    6478          50 :         if (srclen == -1)
    6479          50 :                 srclen = (int)strlen(src);
    6480             : 
    6481          50 :         if (quotetype == DBSINGLE || quotetype == DBBOTH)
    6482          40 :                 squote = true;
    6483          50 :         if (quotetype == DBDOUBLE || quotetype == DBBOTH)
    6484          30 :                 dquote = true;
    6485             : 
    6486             :         /* return FAIL if invalid quotetype */
    6487          50 :         if (!dquote && !squote)
    6488             :                 return FAIL;
    6489             : 
    6490             : 
    6491        1790 :         for (i = 0; i < srclen; i++) {
    6492             : 
    6493             :                 /* dbsafestr returns fail if the deststr is not big enough */
    6494             :                 /* need one char + one for terminator */
    6495        1800 :                 if (destlen >= 0 && j >= destlen)
    6496             :                         return FAIL;
    6497             : 
    6498        1790 :                 if (squote && src[i] == '\'')
    6499          40 :                         dest[j++] = '\'';
    6500        1750 :                 else if (dquote && src[i] == '\"')
    6501          30 :                         dest[j++] = '\"';
    6502             : 
    6503        1790 :                 if (destlen >= 0 && j >= destlen)
    6504             :                         return FAIL;
    6505             : 
    6506        1790 :                 dest[j++] = src[i];
    6507             :         }
    6508             : 
    6509          40 :         if (destlen >= 0 && j >= destlen)
    6510             :                 return FAIL;
    6511             : 
    6512          30 :         dest[j] = '\0';
    6513          30 :         return SUCCEED;
    6514             : }
    6515             : 
    6516             : /**
    6517             :  * \ingroup dblib_core
    6518             :  * \brief Print a token value's name to a buffer
    6519             :  * 
    6520             :  * \param token server SYB* value, e.g. SYBINT.  
    6521             : 
    6522             :  * \return ASCII null-terminated string.  
    6523             :  * \sa dbaltop(), dbalttype(), dbcoltype(), dbrettype().
    6524             :  */
    6525             : const char *
    6526          24 : dbprtype(int token)
    6527             : {
    6528          24 :         tdsdump_log(TDS_DBG_FUNC, "dbprtype(%d)\n", token);
    6529          24 :         return tds_prtype(token);
    6530             : }
    6531             : 
    6532             : /**
    6533             :  * \ingroup dblib_core
    6534             :  * \brief describe table column attributes with a single call (Freetds-only API function modelled on dbcolinfo)
    6535             :  * 
    6536             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6537             :  * \param column Nth in the result set, starting from 1.
    6538             :  * \param pdbcol address of structure to be populated by this function.  
    6539             :  * \return SUCCEED or FAIL. 
    6540             :  * \sa dbcolinfo().
    6541             :  */
    6542             : RETCODE
    6543          30 : dbtablecolinfo(DBPROCESS *dbproc, DBINT column, DBCOL *pdbcol)
    6544             : {
    6545             :         TDSCOLUMN *colinfo;
    6546             : 
    6547          30 :         tdsdump_log(TDS_DBG_FUNC, "dbtablecolinfo(%p, %d, %p)\n", dbproc, column, pdbcol);
    6548          30 :         CHECK_CONN(FAIL);
    6549          30 :         CHECK_NULP(pdbcol, "dbtablecolinfo", 3, FAIL);
    6550          30 :         DBPERROR_RETURN(pdbcol->SizeOfStruct != sizeof(DBCOL)
    6551             :                         && pdbcol->SizeOfStruct != sizeof(DBCOL2), SYBECOLSIZE);
    6552             : 
    6553          30 :         colinfo = dbcolptr(dbproc, column);
    6554          30 :         if (!colinfo)
    6555             :                 return FAIL;
    6556             : 
    6557          60 :         strlcpy(pdbcol->Name, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->Name));
    6558             :         /* returns table_column_name if available */
    6559          60 :         if (tds_dstr_isempty(&colinfo->table_column_name))
    6560          16 :                 strlcpy(pdbcol->ActualName, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->ActualName));
    6561             :         else
    6562          44 :                 strlcpy(pdbcol->ActualName, tds_dstr_cstr(&colinfo->table_column_name), sizeof(pdbcol->ActualName));
    6563          60 :         strlcpy(pdbcol->TableName, tds_dstr_cstr(&colinfo->table_name), sizeof(pdbcol->TableName));
    6564             : 
    6565          30 :         pdbcol->Type = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    6566          30 :         pdbcol->UserType = colinfo->column_usertype;
    6567          30 :         pdbcol->MaxLength = colinfo->column_size;
    6568          30 :         if (colinfo->column_nullable)
    6569           0 :                 pdbcol->Null = TRUE;
    6570             :         else
    6571          30 :                 pdbcol->Null = FALSE;
    6572             : 
    6573          30 :         pdbcol->VarLength = FALSE;
    6574             : 
    6575          30 :         if (colinfo->column_nullable
    6576          30 :             || is_nullable_type(colinfo->column_type))
    6577           0 :                 pdbcol->VarLength = TRUE;
    6578             :         
    6579          30 :         pdbcol->Precision = colinfo->column_prec;
    6580          30 :         pdbcol->Scale = colinfo->column_scale;
    6581             : 
    6582          30 :         pdbcol->Updatable = colinfo->column_writeable ? TRUE : FALSE;
    6583          30 :         pdbcol->Identity = colinfo->column_identity ? TRUE : FALSE;
    6584             : 
    6585          30 :         if (pdbcol->SizeOfStruct >= sizeof(DBCOL2)) {
    6586          30 :                 DBCOL2 *col = (DBCOL2 *) pdbcol;
    6587             :                 TDSRET rc;
    6588             : 
    6589          30 :                 col->ServerType = colinfo->on_server.column_type;
    6590          30 :                 col->ServerMaxLength = colinfo->on_server.column_size;
    6591             : 
    6592          30 :                 rc = tds_get_column_declaration(dbproc->tds_socket, colinfo, col->ServerTypeDeclaration);
    6593          30 :                 if (TDS_FAILED(rc))
    6594             :                         return FAIL;
    6595             :         }
    6596             : 
    6597             :         return SUCCEED;
    6598             : }
    6599             : 
    6600             : /**
    6601             :  * \ingroup dblib_core
    6602             :  * \brief Get text timestamp for a column in the current row.
    6603             :  * 
    6604             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6605             :  * \param column number of the column in the \c SELECT statement, starting at 1.
    6606             :  * \return timestamp for \a column, may be NULL.
    6607             :  * \sa dbtxptr(), dbwritetext().  
    6608             :  */
    6609             : DBBINARY *
    6610          56 : dbtxtimestamp(DBPROCESS * dbproc, int column)
    6611             : {
    6612             :         TDSCOLUMN *colinfo;
    6613             :         TDSBLOB *blob;
    6614             : 
    6615          56 :         tdsdump_log(TDS_DBG_FUNC, "dbtxtimestamp(%p, %d)\n", dbproc, column);
    6616             : 
    6617          56 :         colinfo = dbcolptr(dbproc, column);
    6618          56 :         if (!colinfo || !is_blob_col(colinfo))
    6619             :                 return NULL;
    6620             : 
    6621          56 :         blob = (TDSBLOB *) colinfo->column_data;
    6622             : 
    6623             :         /* test if valid */
    6624          56 :         if (!blob->valid_ptr)
    6625             :                 return NULL;
    6626             : 
    6627          36 :         return (DBBINARY *) blob->timestamp;
    6628             : }
    6629             : 
    6630             : /**
    6631             :  * \ingroup dblib_core
    6632             :  * \brief Get text pointer for a column in the current row.
    6633             :  * 
    6634             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6635             :  * \param column number of the column in the \c SELECT statement, starting at 1.
    6636             :  * \return text pointer for \a column, may be NULL.
    6637             :  * \sa dbtxtimestamp(), dbwritetext(). 
    6638             :  */
    6639             : DBBINARY *
    6640          56 : dbtxptr(DBPROCESS * dbproc, int column)
    6641             : {
    6642             :         TDSCOLUMN *colinfo;
    6643             :         TDSBLOB *blob;
    6644             : 
    6645          56 :         tdsdump_log(TDS_DBG_FUNC, "dbtxptr(%p, %d)\n", dbproc, column);
    6646             : 
    6647          56 :         colinfo = dbcolptr(dbproc, column);
    6648          56 :         if (!colinfo || !is_blob_col(colinfo))
    6649             :                 return NULL;
    6650             : 
    6651          56 :         blob = (TDSBLOB *) colinfo->column_data;
    6652             : 
    6653             :         /* test if valid */
    6654          56 :         if (!blob->valid_ptr)
    6655             :                 return NULL;
    6656             : 
    6657          36 :         return (DBBINARY *) blob->textptr;
    6658             : }
    6659             : 
    6660             : /**
    6661             :  * \ingroup dblib_core
    6662             :  * \brief Send text or image data to the server.
    6663             :  * 
    6664             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6665             :  * \param objname table name
    6666             :  * \param textptr text pointer to be modified, obtained from dbtxptr(). 
    6667             :  * \param textptrlen \em Ignored.  Supposed to be \c DBTXPLEN.
    6668             :  * \param timestamp text timestamp to be modified, obtained from dbtxtimestamp() or dbtxtsnewval(), may be \c NULL.
    6669             :  * \param log \c TRUE if the operation is to be recorded in the transaction log.
    6670             :  * \param size overall size of the data (in total, not just for this call), in bytes.  A guideline, must not overstate the case.  
    6671             :  * \param text the chunk of data to write.  
    6672             :  * \retval SUCCEED everything worked.
    6673             :  * \retval FAIL not sent, possibly because \a timestamp is invalid or was changed in the database since it was fetched.  
    6674             :  * \sa dbmoretext(), dbtxptr(), dbtxtimestamp(), dbwritetext(), dbtxtsput().  
    6675             :  */
    6676             : RETCODE
    6677          36 : dbwritetext(DBPROCESS * dbproc, char *objname, DBBINARY * textptr, DBTINYINT textptrlen, DBBINARY * timestamp, DBBOOL log,
    6678             :             DBINT size, BYTE * text)
    6679             : {
    6680             :         char textptr_string[35];        /* 16 * 2 + 2 (0x) + 1 */
    6681             :         char timestamp_string[19];      /* 8 * 2 + 2 (0x) + 1 */
    6682             :         TDS_INT result_type;
    6683             : 
    6684          36 :         tdsdump_log(TDS_DBG_FUNC, "dbwritetext(%p, %s, %p, %d, %p, %d)\n", 
    6685             :                                   dbproc, objname, textptr, textptrlen, timestamp, log);
    6686          36 :         CHECK_CONN(FAIL);
    6687          36 :         CHECK_NULP(objname, "dbwritetext", 2, FAIL);
    6688          36 :         CHECK_NULP(textptr, "dbwritetext", 3, FAIL);
    6689          36 :         CHECK_NULP(timestamp, "dbwritetext", 5, FAIL);
    6690          36 :         CHECK_PARAMETER(size, SYBEZTXT, FAIL);
    6691             : 
    6692             :         if (IS_TDSDEAD(dbproc->tds_socket))
    6693             :                 return FAIL;
    6694             : 
    6695          36 :         if (textptrlen > DBTXPLEN)
    6696             :                 return FAIL;
    6697             : 
    6698          36 :         dbconvert(dbproc, SYBBINARY, (BYTE *) textptr, textptrlen, SYBCHAR, (BYTE *) textptr_string, -1);
    6699          36 :         dbconvert(dbproc, SYBBINARY, (BYTE *) timestamp, 8, SYBCHAR, (BYTE *) timestamp_string, -1);
    6700             : 
    6701          36 :         dbproc->dbresults_state = _DB_RES_INIT;
    6702             : 
    6703          36 :         if (dbproc->tds_socket->state == TDS_PENDING) {
    6704           0 :                 const TDSRET ret = tds_process_tokens(dbproc->tds_socket, &result_type, NULL, TDS_TOKEN_TRAILING);
    6705           0 :                 if (ret != TDS_NO_MORE_RESULTS) {
    6706           0 :                         dbperror(dbproc, SYBERPND, 0);
    6707           0 :                         dbproc->command_state = DBCMDSENT;
    6708           0 :                         return FAIL;
    6709             :                 }
    6710             :         }
    6711             :         
    6712          36 :         if (TDS_FAILED(tds_writetext_start(dbproc->tds_socket, objname, 
    6713             :                 textptr_string, timestamp_string, (log == TRUE), size)))
    6714             :                 return FAIL;
    6715             : 
    6716          36 :         if (!text) {
    6717          18 :                 dbproc->text_size = size;
    6718          18 :                 dbproc->text_sent = 0;
    6719          18 :                 return SUCCEED;
    6720             :         }
    6721             : 
    6722          18 :         tds_writetext_continue(dbproc->tds_socket, text, size);
    6723          18 :         tds_writetext_end(dbproc->tds_socket);
    6724          18 :         dbproc->text_sent = 0;
    6725             : 
    6726          18 :         if (dbsqlok(dbproc) == SUCCEED && dbresults(dbproc) == SUCCEED)
    6727             :                 return SUCCEED;
    6728             :         return FAIL;
    6729             : }
    6730             : 
    6731             : /**
    6732             :  * \ingroup dblib_core
    6733             :  * \brief Fetch part of a text or image value from the server.
    6734             :  * 
    6735             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6736             :  * \param buf \em output: buffer into which text will be placed.
    6737             :  * \param bufsize size of \a buf, in bytes. 
    6738             :  * \return 
    6739             :         - \c >0 count of bytes placed in \a buf.
    6740             :         - \c  0 end of row.  
    6741             :         - \c -1 \em error, no result set ready for \a dbproc.
    6742             :         - \c NO_MORE_ROWS all rows read, no further data. 
    6743             :  * \sa dbmoretext(), dbnextrow(), dbwritetext(). 
    6744             :  */
    6745             : STATUS
    6746         636 : dbreadtext(DBPROCESS * dbproc, void *buf, DBINT bufsize)
    6747             : {
    6748             :         TDSSOCKET *tds;
    6749             :         TDSCOLUMN *curcol;
    6750             :         int cpbytes, bytes_avail;
    6751             :         TDS_INT result_type;
    6752             :         TDSRESULTINFO *resinfo;
    6753             : 
    6754         636 :         tdsdump_log(TDS_DBG_FUNC, "dbreadtext(%p, %p, %d)\n", dbproc, buf, bufsize);
    6755         636 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    6756         636 :         CHECK_NULP(buf, "dbreadtext", 2, -1);
    6757             : 
    6758         636 :         tds = dbproc->tds_socket;
    6759             : 
    6760         636 :         if (!tds || !tds->res_info || !tds->res_info->columns[0])
    6761             :                 return -1;
    6762             : 
    6763         636 :         resinfo = tds->res_info;
    6764         636 :         curcol = resinfo->columns[0];
    6765             : 
    6766             :         /*
    6767             :          * if the current position is beyond the end of the text
    6768             :          * set pos to 0 and return 0 to denote the end of the 
    6769             :          * text 
    6770             :          */
    6771         636 :         if (curcol->column_textpos && curcol->column_textpos >= curcol->column_cur_size) {
    6772          36 :                 curcol->column_textpos = 0;
    6773          36 :                 return 0;
    6774             :         }
    6775             : 
    6776             :         /*
    6777             :          * if pos is 0 (first time through or last call exhausted the text)
    6778             :          * then read another row
    6779             :          */
    6780             : 
    6781         600 :         if (curcol->column_textpos == 0) {
    6782          96 :                 const int mask = TDS_STOPAT_ROWFMT|TDS_STOPAT_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE;
    6783          96 :                 buffer_save_row(dbproc);
    6784          96 :                 switch (tds_process_tokens(dbproc->tds_socket, &result_type, NULL, mask)) {
    6785          96 :                 case TDS_SUCCESS:
    6786          96 :                         if (result_type == TDS_ROW_RESULT || result_type == TDS_COMPUTE_RESULT)
    6787             :                                 break;
    6788             :                 case TDS_NO_MORE_RESULTS:
    6789             :                         return NO_MORE_ROWS;
    6790           0 :                 default:
    6791           0 :                         return -1;
    6792             :                 }
    6793         552 :         }
    6794             : 
    6795             :         /* find the number of bytes to return */
    6796         552 :         bytes_avail = TDS_MAX(curcol->column_cur_size, 0) - curcol->column_textpos;
    6797         552 :         cpbytes = bytes_avail > bufsize ? bufsize : bytes_avail;
    6798         552 :         memcpy(buf, &((TDSBLOB *) curcol->column_data)->textvalue[curcol->column_textpos], cpbytes);
    6799         552 :         curcol->column_textpos += cpbytes;
    6800         552 :         return cpbytes;
    6801             : }
    6802             : 
    6803             : /**
    6804             :  * \ingroup dblib_core
    6805             :  * \brief Send chunk of a text/image value to the server.
    6806             :  * 
    6807             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6808             :  * \param size count of bytes to send.  
    6809             :  * \param text textpointer, obtained from dbtxptr.
    6810             :  * \retval SUCCEED always.
    6811             :  * \sa dbtxptr(), dbtxtimestamp(), dbwritetext(). 
    6812             :  * \todo Check return value of called functions and return \c FAIL if appropriate. 
    6813             :  */
    6814             : RETCODE
    6815         270 : dbmoretext(DBPROCESS * dbproc, DBINT size, const BYTE text[])
    6816             : {
    6817         270 :         tdsdump_log(TDS_DBG_FUNC, "dbmoretext(%p, %d, %p)\n", dbproc, size, text);
    6818         270 :         CHECK_CONN(FAIL);
    6819         270 :         CHECK_NULP(text, "dbmoretext", 3, FAIL);
    6820             : 
    6821         270 :         assert(dbproc->text_size >= dbproc->text_sent);
    6822             : 
    6823             :         /* TODO this test should be inside tds_writetext_continue, currently not */
    6824         270 :         if (size < 0 || size > dbproc->text_size - dbproc->text_sent)
    6825             :                 return FAIL;
    6826             : 
    6827         270 :         if (size) {
    6828         270 :                 if (TDS_FAILED(tds_writetext_continue(dbproc->tds_socket, text, size)))
    6829             :                         return FAIL;
    6830         270 :                 dbproc->text_sent += size;
    6831             : 
    6832         270 :                 if (dbproc->text_sent == dbproc->text_size) {
    6833          18 :                         tds_writetext_end(dbproc->tds_socket);
    6834          18 :                         dbproc->text_sent = 0;
    6835             :                 }
    6836             :         }
    6837             : 
    6838             :         return SUCCEED;
    6839             : }
    6840             : 
    6841             : /**
    6842             :  * \ingroup dblib_core
    6843             :  * \brief Record to a file all SQL commands sent to the server
    6844             :  * 
    6845             :  * \param filename name of file to write to. 
    6846             :  * \remarks Files are named \em filename.n, where n is an integer, starting with 0, and incremented with each callto dbopen().  
    6847             :  * \sa dbopen(), TDSDUMP environment variable(). 
    6848             :  */
    6849             : void
    6850           0 : dbrecftos(const char filename[])
    6851             : {
    6852             :         char *f;
    6853             : 
    6854           0 :         tdsdump_log(TDS_DBG_FUNC, "dbrecftos(%s)\n", filename);
    6855           0 :         if (filename == NULL) { 
    6856           0 :                 dbperror(NULL, SYBENULP, 0); 
    6857           0 :                 return;
    6858             :         }
    6859             :         
    6860           0 :         f = strdup(filename);
    6861           0 :         if (!f) {
    6862           0 :                 dbperror(NULL, SYBEMEM, 0); 
    6863           0 :                 return;
    6864             :         }
    6865             :         
    6866           0 :         tds_mutex_lock(&dblib_mutex);
    6867           0 :         free(g_dblib_ctx.recftos_filename);
    6868           0 :         g_dblib_ctx.recftos_filename = f;
    6869           0 :         g_dblib_ctx.recftos_filenum = 0;
    6870           0 :         tds_mutex_unlock(&dblib_mutex);
    6871             : }
    6872             : 
    6873             : /** \internal
    6874             :  * \ingroup dblib_internal
    6875             :  * \brief Get the TDS version in use for \a dbproc.  
    6876             :  * 
    6877             :  * 
    6878             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6879             :  * \return a \c DBTDS* token.  
    6880             :  * \remarks The integer values of the constants are counterintuitive.  
    6881             :  * \sa DBTDS().
    6882             :  */
    6883             : int
    6884         464 : dbtds(DBPROCESS * dbproc)
    6885             : {
    6886         464 :         tdsdump_log(TDS_DBG_FUNC, "dbtds(%p)\n", dbproc);
    6887         464 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    6888             : 
    6889         464 :         if (dbproc->tds_socket) {
    6890         464 :                 switch (dbproc->tds_socket->conn->tds_version) {
    6891             :                 case 0x402:
    6892             :                         return DBTDS_4_2;
    6893           0 :                 case 0x406:
    6894           0 :                         return DBTDS_4_6;
    6895          78 :                 case 0x500:
    6896          78 :                         return DBTDS_5_0;
    6897           0 :                 case 0x700:
    6898           0 :                         return DBTDS_7_0;
    6899         184 :                 case 0x701:
    6900         184 :                         return DBTDS_7_1;
    6901           0 :                 case 0x702:
    6902           0 :                         return DBTDS_7_2;
    6903         100 :                 case 0x703:
    6904         100 :                         return DBTDS_7_3;
    6905         102 :                 case 0x704:
    6906         102 :                         return DBTDS_7_4;
    6907           0 :                 case 0x800:
    6908           0 :                         return DBTDS_8_0_;
    6909           0 :                 default:
    6910           0 :                         return DBTDS_UNKNOWN;
    6911             :                 }
    6912             :         }
    6913             :         return -1;
    6914             : }
    6915             : 
    6916             : /**
    6917             :  * \ingroup dblib_core
    6918             :  * \brief See which version of db-lib is in use.
    6919             :  * 
    6920             :  * \return null-terminated ASCII string representing the version of db-lib.  
    6921             :  * \remarks FreeTDS returns the CVS version string of dblib.c.  
    6922             :  * \sa 
    6923             :  */
    6924             : const char *
    6925           0 : dbversion(void)
    6926             : {
    6927           0 :         tdsdump_log(TDS_DBG_FUNC, "dbversion(void)\n");
    6928           0 :         return TDS_VERSION_NO;
    6929             : }
    6930             : 
    6931             : #if defined(DBLIB_UNIMPLEMENTED)
    6932             : /**
    6933             :  * \ingroup dblib_core
    6934             :  * \brief Set the default character set.  
    6935             :  * 
    6936             :  * \param charset null-terminated ASCII string, matching a row in master..syscharsets.  
    6937             :  * \sa dbsetdeflang(), dbsetdefcharset(), dblogin(), dbopen(). 
    6938             :  * \todo Unimplemented.
    6939             :  */
    6940             : RETCODE
    6941             : dbsetdefcharset(char *charset)
    6942             : {
    6943             :         tdsdump_log(TDS_DBG_FUNC, "dbsetdefcharset(%s)\n", charset);
    6944             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetdefcharset()\n");
    6945             :         return SUCCEED;
    6946             : }
    6947             : 
    6948             : /**
    6949             :  * \ingroup dblib_core
    6950             :  * \brief Ready execution of a registered procedure.
    6951             :  * 
    6952             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6953             :  * \param procedure_name to call.
    6954             :  * \param namelen size of \a procedure_name, in bytes.
    6955             :  * \sa dbregparam(), dbregexec(), dbregwatch(), dbreglist(), dbregwatchlist
    6956             :  * \todo Unimplemented.
    6957             :  */
    6958             : RETCODE
    6959             : dbreginit(DBPROCESS * dbproc, DBCHAR * procedure_name, DBSMALLINT namelen)
    6960             : {
    6961             :         tdsdump_log(TDS_DBG_FUNC, "dbreginit(%p, %s, %d)\n", dbproc, procedure_name, namelen);
    6962             :         CHECK_CONN(FAIL);
    6963             :         CHECK_NULP(procedure_name, "dbreginit", 2, FAIL);
    6964             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbreginit()\n");
    6965             :         return SUCCEED;
    6966             : }
    6967             : 
    6968             : 
    6969             : /**
    6970             :  * \ingroup dblib_core
    6971             :  * \brief Get names of Open Server registered procedures.
    6972             :  * 
    6973             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6974             :  * \sa dbregparam(), dbregexec(), dbregwatch(), dbreglist(), dbregwatchlist(). 
    6975             :  * \todo Unimplemented.
    6976             :  */
    6977             : RETCODE
    6978             : dbreglist(DBPROCESS * dbproc)
    6979             : {
    6980             :         tdsdump_log(TDS_DBG_FUNC, "dbreglist(%p)\n", dbproc);
    6981             :         CHECK_CONN(FAIL);
    6982             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbreglist()\n");
    6983             :         return SUCCEED;
    6984             : }
    6985             : 
    6986             : 
    6987             : /**
    6988             :  * \ingroup dblib_core
    6989             :  * \brief  Describe parameter of registered procedure .
    6990             :  * 
    6991             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6992             :  * \param param_name
    6993             :  * \param type \c SYB* datatype. 
    6994             :  * \param datalen size of \a data.
    6995             :  * \param data address of buffer holding value for the parameter.  
    6996             :  * \sa dbreginit(), dbregexec(), dbnpdefine(), dbnpcreate(), dbregwatch(). 
    6997             :  * \todo Unimplemented.
    6998             :  */
    6999             : RETCODE
    7000             : dbregparam(DBPROCESS * dbproc, char *param_name, int type, DBINT datalen, BYTE * data)
    7001             : {
    7002             :         tdsdump_log(TDS_DBG_FUNC, "dbregparam(%p, %s, %d, %d, %p)\n", dbproc, param_name, type, datalen, data);
    7003             :         CHECK_CONN(FAIL);
    7004             :         CHECK_NULP(param_name, "dbregparam", 2, FAIL);
    7005             :         CHECK_NULP(data, "dbregparam", 5, FAIL);
    7006             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbregparam()\n");
    7007             :         return SUCCEED;
    7008             : }
    7009             : 
    7010             : 
    7011             : /**
    7012             :  * \ingroup dblib_core
    7013             :  * \brief Execute a registered procedure.
    7014             :  * 
    7015             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7016             :  * \param options
    7017             :  * \sa dbreginit(), dbregparam(), dbregwatch(), dbregnowatch
    7018             :  * \todo Unimplemented.
    7019             :  */
    7020             : RETCODE
    7021             : dbregexec(DBPROCESS * dbproc, DBUSMALLINT options)
    7022             : {
    7023             :         tdsdump_log(TDS_DBG_FUNC, "dbregexec(%p, %d)\n", dbproc, options);
    7024             :         CHECK_CONN(FAIL);
    7025             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbregexec()\n");
    7026             :         return SUCCEED;
    7027             : }
    7028             : #endif
    7029             : 
    7030             : 
    7031             : /**
    7032             :  * \ingroup dblib_datetime
    7033             :  * \brief Get name of a month, in some human language.
    7034             :  * 
    7035             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7036             :  * \param language \em ignored.
    7037             :  * \param monthnum number of the month, starting with 1.
    7038             :  * \param shortform set to \c TRUE for a three letter output ("Jan" - "Dec"), else zero.  
    7039             :  * \return address of null-terminated ASCII string, or \c NULL on error. 
    7040             :  * \sa db12hour(), dbdateorder(), dbdayname(), DBSETLNATLANG(), dbsetopt().  
    7041             :  */
    7042             : const char *
    7043           0 : dbmonthname(DBPROCESS * dbproc, char *language, int monthnum, DBBOOL shortform)
    7044             : {
    7045             :         static const char shortmon[][4] = {
    7046             :                 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    7047             :         };
    7048             :         static const char longmon[][12] = {
    7049             :                 "January", "February", "March", "April", "May", "June",
    7050             :                 "July", "August", "September", "October", "November", "December"
    7051             :         };
    7052             : 
    7053           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmonthname(%p, %s, %d, %d)\n", dbproc, language, monthnum, shortform);
    7054           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    7055           0 :         CHECK_NULP(language, "dbmonthname", 2, NULL);
    7056             : 
    7057           0 :         if (monthnum < 1 || monthnum > 12)
    7058             :                 return NULL;
    7059           0 :         return (shortform) ? shortmon[monthnum - 1] : longmon[monthnum - 1];
    7060             : }
    7061             : 
    7062             : /**
    7063             :  * \ingroup dblib_core
    7064             :  * \brief See if a command caused the current database to change.
    7065             :  * 
    7066             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7067             :  * \return name of new database, if changed, as a null-terminated ASCII string, else \c NULL.
    7068             : 
    7069             :  * \sa dbname(), dbresults(), dbsqlexec(), dbsqlsend(), dbuse().  
    7070             :  */
    7071             : char *
    7072           0 : dbchange(DBPROCESS * dbproc)
    7073             : {
    7074           0 :         tdsdump_log(TDS_DBG_FUNC, "dbchange(%p)\n", dbproc);
    7075           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    7076             : 
    7077           0 :         if (dbproc->envchange_rcv & (1 << (TDS_ENV_DATABASE - 1))) {
    7078           0 :                 return dbproc->dbcurdb;
    7079             :         }
    7080             :         return NULL;
    7081             : }
    7082             : 
    7083             : /**
    7084             :  * \ingroup dblib_core
    7085             :  * \brief Get name of current database.
    7086             :  * 
    7087             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7088             :  * \return current database name, as null-terminated ASCII string.
    7089             :  * \sa dbchange(), dbuse().  
    7090             :  */
    7091             : char *
    7092           0 : dbname(DBPROCESS * dbproc)
    7093             : {
    7094           0 :         tdsdump_log(TDS_DBG_FUNC, "dbname(%p)\n", dbproc);
    7095           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    7096           0 :         return dbproc->dbcurdb;
    7097             : }
    7098             : 
    7099             : /**
    7100             :  * \ingroup dblib_core
    7101             :  * \brief Get \c syscharset name of the server character set.
    7102             :  * 
    7103             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7104             :  * \return name of server's charset, as null-terminated ASCII string.
    7105             :  * \sa dbcharsetconv(), dbgetcharset(), DBSETLCHARSET().  
    7106             :  */
    7107             : char *
    7108           0 : dbservcharset(DBPROCESS * dbproc)
    7109             : {
    7110             : 
    7111           0 :         tdsdump_log(TDS_DBG_FUNC, "dbservcharset(%p)\n", dbproc);
    7112           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    7113             : 
    7114           0 :         return dbproc->servcharset;
    7115             : }
    7116             : 
    7117             : /**
    7118             :  * \ingroup dblib_core
    7119             :  * \brief Transmit the command buffer to the server.  \em Non-blocking, does not wait for a response.
    7120             :  * 
    7121             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7122             :  * \retval SUCCEED SQL sent.
    7123             :  * \retval FAIL protocol problem, unless dbsqlsend() when it's not supposed to be (in which case a db-lib error
    7124             :  message will be emitted).  
    7125             :  * \sa dbcmd(), dbfcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), dbresults(), dbsettime(), dbsqlexec(), dbsqlok().  
    7126             :  */
    7127             : RETCODE
    7128       20484 : dbsqlsend(DBPROCESS * dbproc)
    7129             : {
    7130             :         TDSSOCKET *tds;
    7131             :         char *cmdstr;
    7132             :         TDSRET rc;
    7133             :         TDS_INT result_type;
    7134             :         char timestr[256];
    7135             : 
    7136       20484 :         tdsdump_log(TDS_DBG_FUNC, "dbsqlsend(%p)\n", dbproc);
    7137       20484 :         CHECK_CONN(FAIL);
    7138             : 
    7139       20484 :         tds = dbproc->tds_socket;
    7140             : 
    7141       20484 :         if (tds->state == TDS_PENDING) {
    7142             : 
    7143         130 :                 if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_TRAILING) != TDS_NO_MORE_RESULTS) {
    7144          70 :                         dbperror(dbproc, SYBERPND, 0);
    7145          70 :                         dbproc->command_state = DBCMDSENT;
    7146          70 :                         return FAIL;
    7147             :                 }
    7148             :         }
    7149             : 
    7150       20414 :         if (dbproc->dboptcmd) {
    7151           0 :                 if ((cmdstr = dbstring_get(dbproc->dboptcmd)) == NULL) {
    7152           0 :                         dbperror(dbproc, SYBEASEC, 0); /* Attempt to send an empty command buffer to the server */
    7153           0 :                         return FAIL;
    7154             :                 }
    7155           0 :                 rc = tds_submit_query(dbproc->tds_socket, cmdstr);
    7156           0 :                 free(cmdstr);
    7157           0 :                 dbstring_free(&(dbproc->dboptcmd));
    7158           0 :                 if (TDS_FAILED(rc)) {
    7159             :                         return FAIL;
    7160             :                 }
    7161           0 :                 dbproc->avail_flag = FALSE;
    7162           0 :                 dbproc->envchange_rcv = 0;
    7163           0 :                 dbproc->dbresults_state = _DB_RES_INIT;
    7164           0 :                 while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))
    7165             :                        == TDS_SUCCESS);
    7166           0 :                 if (rc != TDS_NO_MORE_RESULTS) {
    7167             :                         return FAIL;
    7168             :                 }
    7169             :         }
    7170       20414 :         dbproc->more_results = TRUE;
    7171             : 
    7172       20414 :         if (dbproc->ftos != NULL) {
    7173           0 :                 fprintf(dbproc->ftos, "%s\n", dbproc->dbbuf);
    7174           0 :                 fprintf(dbproc->ftos, "go /* %s */\n", _dbprdate(timestr));
    7175           0 :                 fflush(dbproc->ftos);
    7176             :         }
    7177             : 
    7178       20414 :         if (TDS_FAILED(tds_submit_query(dbproc->tds_socket, (char *) dbproc->dbbuf))) {
    7179             :                 return FAIL;
    7180             :         }
    7181       20404 :         dbproc->avail_flag = FALSE;
    7182       20404 :         dbproc->envchange_rcv = 0;
    7183       20404 :         dbproc->dbresults_state = _DB_RES_INIT;
    7184       20404 :         dbproc->command_state = DBCMDSENT;
    7185       20404 :         return SUCCEED;
    7186             : }
    7187             : 
    7188             : /**
    7189             :  * \ingroup dblib_core
    7190             :  * \brief Get user-defined datatype of a compute column.
    7191             :  * 
    7192             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7193             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    7194             :  * \param column Nth column in \a computeid, starting from 1.
    7195             :  * \returns user-defined datatype of compute column, else -1.
    7196             :  * \sa dbalttype(), dbcolutype().
    7197             :  */
    7198             : DBINT
    7199           0 : dbaltutype(DBPROCESS * dbproc, int computeid, int column)
    7200             : {
    7201             :         TDSCOLUMN *colinfo;
    7202             : 
    7203           0 :         tdsdump_log(TDS_DBG_FUNC, "dbaltutype(%p, %d, %d)\n", dbproc, computeid, column);
    7204             : 
    7205           0 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    7206           0 :         if (!colinfo)
    7207             :                 return -1;
    7208             : 
    7209           0 :         return colinfo->column_usertype;
    7210             : }
    7211             : 
    7212             : /**
    7213             :  * \ingroup dblib_core
    7214             :  * \brief Get size of data in compute column.
    7215             :  * 
    7216             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7217             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    7218             :  * \param column Nth column in \a computeid, starting from 1.
    7219             :  * \sa dbadata(), dbadlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
    7220             :  */
    7221             : DBINT
    7222           0 : dbaltlen(DBPROCESS * dbproc, int computeid, int column)
    7223             : {
    7224             :         TDSCOLUMN *colinfo;
    7225             : 
    7226           0 :         tdsdump_log(TDS_DBG_FUNC, "dbaltlen(%p, %d, %d)\n", dbproc, computeid, column);
    7227             : 
    7228           0 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    7229           0 :         if (!colinfo)
    7230             :                 return -1;
    7231             : 
    7232           0 :         return colinfo->column_size;
    7233             : 
    7234             : }
    7235             : 
    7236             : /**
    7237             :  * \ingroup dblib_core
    7238             :  * \brief See if a server response has arrived.
    7239             :  * 
    7240             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7241             :  * \param milliseconds how long to wait for the server before returning:
    7242             :         - \c  0 return immediately.
    7243             :         - \c -1 do not return until the server responds or a system interrupt occurs.
    7244             :  * \param ready_dbproc \em output: DBPROCESS for which a response arrived, of \c NULL. 
    7245             :  * \param return_reason \em output: 
    7246             :         - \c DBRESULT server responded.
    7247             :         - \c DBNOTIFICATION registered procedure notification has arrived. dbpoll() the registered handler, if
    7248             :                 any, before it returns.
    7249             :         - \c DBTIMEOUT \a milliseconds elapsed before the server responded.
    7250             :         - \c DBINTERRUPT operating-system interrupt occurred before the server responded.
    7251             :  * \retval SUCCEED everything worked.
    7252             :  * \retval FAIL a server connection died.
    7253             :  * \sa  DBIORDESC(), DBRBUF(), dbresults(), dbreghandle(), dbsqlok(). 
    7254             :  * \todo Unimplemented.
    7255             :  */
    7256             : #if defined(DBLIB_UNIMPLEMENTED)
    7257             : RETCODE
    7258             : dbpoll(DBPROCESS * dbproc, long milliseconds, DBPROCESS ** ready_dbproc, int *return_reason)
    7259             : {
    7260             :         tdsdump_log(TDS_DBG_FUNC, "dbpoll(%p, %ld, %p, %p)\n", dbproc, milliseconds, ready_dbproc, return_reason);
    7261             :         CHECK_CONN(FAIL);
    7262             :         CHECK_NULP(ready_dbproc, "dbpoll", 3, FAIL);
    7263             :         CHECK_NULP(return_reason, "dbpoll", 4, FAIL);
    7264             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbpoll()\n");
    7265             :         return SUCCEED;
    7266             : }
    7267             : #endif
    7268             : 
    7269             : /** \internal
    7270             :  * \ingroup dblib_internal
    7271             :  * \brief Get number of the first row in the row buffer.
    7272             :  * 
    7273             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7274             :  * \sa DBFIRSTROW(), dbclrbuf(), DBCURROW(), dbgetrow(), DBLASTROW(), dbnextrow(), dbsetopt(). 
    7275             :  */
    7276             : DBINT
    7277           0 : dbfirstrow(DBPROCESS * dbproc)
    7278             : {
    7279           0 :         tdsdump_log(TDS_DBG_FUNC, "dbfirstrow(%p)\n", dbproc);
    7280           0 :         CHECK_CONN(0);
    7281           0 :         return buffer_idx2row(&dbproc->row_buf, dbproc->row_buf.tail);
    7282             : }
    7283             : 
    7284             : /** \internal
    7285             :  * \ingroup dblib_internal
    7286             :  * \brief Get number of the last row in the row buffer.
    7287             :  * 
    7288             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7289             :  * \sa DBLASTROW(), dbclrbuf(), DBCURROW(), DBFIRSTROW(), dbgetrow(), dbnextrow(), dbsetopt(). 
    7290             :  */
    7291             : DBINT
    7292           0 : dblastrow(DBPROCESS * dbproc)
    7293             : {
    7294             :         int idx;
    7295             : 
    7296           0 :         tdsdump_log(TDS_DBG_FUNC, "dblastrow(%p)\n", dbproc);
    7297           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    7298           0 :         idx = dbproc->row_buf.head;
    7299           0 :         if (dbproc->row_buf.head != dbproc->row_buf.tail) {
    7300           0 :                 if (--idx < 0) 
    7301           0 :                         idx = dbproc->row_buf.capacity - 1;
    7302             :         }
    7303           0 :         assert(idx >= 0);
    7304           0 :         return buffer_idx2row(&dbproc->row_buf, idx);
    7305             : }
    7306             : 
    7307             : 
    7308             : /** \internal
    7309             :  * \ingroup dblib_internal
    7310             :  * \brief Get file descriptor of the socket used by a \c DBPROCESS to read data coming from the server. (!)
    7311             :  * 
    7312             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7313             :  * \sa dbcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), DBRBUF(), dbresults(), dbsqlok(), dbsqlsend().  
    7314             :  */
    7315             : int
    7316           0 : dbiordesc(DBPROCESS * dbproc)
    7317             : {
    7318           0 :         tdsdump_log(TDS_DBG_FUNC, "dbiordesc(%p)\n", dbproc);
    7319           0 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    7320           0 :         return (int) tds_get_s(dbproc->tds_socket);
    7321             : }
    7322             : 
    7323             : 
    7324             : /** \internal
    7325             :  * \ingroup dblib_internal
    7326             :  * \brief Get file descriptor of the socket used by a \c DBPROCESS to write data coming to the server. (!)
    7327             :  * 
    7328             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7329             :  * \sa dbcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), DBRBUF(), dbresults(), dbsqlok(), dbsqlsend().  
    7330             :  */
    7331             : int
    7332          20 : dbiowdesc(DBPROCESS * dbproc)
    7333             : {
    7334          20 :         tdsdump_log(TDS_DBG_FUNC, "dbiowdesc(%p)\n", dbproc);
    7335          20 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    7336             : 
    7337          20 :         return (int) tds_get_s(dbproc->tds_socket);
    7338             : }
    7339             : 
    7340             : DBBOOL
    7341           0 : dbisavail(DBPROCESS * dbproc)
    7342             : {
    7343           0 :         tdsdump_log(TDS_DBG_FUNC, "dbisavail(%p)\n", dbproc);
    7344           0 :         CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
    7345           0 :         return dbproc->avail_flag;
    7346             : }
    7347             : 
    7348             : 
    7349             : /** \internal
    7350             :  * \ingroup dblib_internal
    7351             :  * \brief Mark a \c DBPROCESS as "available".
    7352             :  * 
    7353             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7354             :  * \remarks Basically bogus.  \c FreeTDS behaves the way Sybase's implementation does, but so what?  
    7355             :         Many \c db-lib functions set the \c DBPROCESS to "not available", but only 
    7356             :         dbsetavail() resets it to "available".  
    7357             :  * \sa DBISAVAIL(). DBSETAVAIL().
    7358             :  */
    7359             : void
    7360           0 : dbsetavail(DBPROCESS * dbproc)
    7361             : {
    7362           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsetavail(%p)\n", dbproc);
    7363           0 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    7364           0 :         dbproc->avail_flag = TRUE;
    7365             : }
    7366             : 
    7367             : 
    7368             : /**
    7369             :  * \ingroup dblib_core
    7370             :  * \brief Build a printable string from text containing placeholders for variables.
    7371             :  * 
    7372             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7373             :  * \param charbuf \em output: buffer that will contain the ASCII null-terminated string built by \c dbstrbuild().
    7374             :  * \param bufsize size of \a charbuf, in bytes. 
    7375             :  * \param text null-terminated ASCII string, with \em placeholders for variables. \em A Placeholder is a
    7376             :  *      three-byte string, made up of: 
    7377             :         - '%' a percent sign
    7378             :         - 0-9 an integer (designates the argument number to use, starting with 1.)
    7379             :         - '!' an exclamation point
    7380             :  * \param formats null-terminated ASCII sprintf-style string.  Has one format specifier for each placeholder in \a text.
    7381             :  * \remarks Following \a formats are the arguments, the values to substitute for the placeholders.  
    7382             :  * \sa dbconvert(), dbdatename(), dbdatepart().  
    7383             :  */
    7384             : int
    7385          80 : dbstrbuild(DBPROCESS * dbproc, char *charbuf, int bufsize, char *text, char *formats, ...)
    7386             : {
    7387             :         va_list ap;
    7388             :         TDSRET rc;
    7389             :         int resultlen;
    7390             : 
    7391          80 :         tdsdump_log(TDS_DBG_FUNC, "dbstrbuild(%p, %s, %d, %s, %s, ...)\n", dbproc, charbuf, bufsize, text, formats);
    7392          80 :         CHECK_NULP(charbuf, "dbstrbuild", 2, FAIL);
    7393          80 :         CHECK_NULP(text, "dbstrbuild", 4, FAIL);
    7394          80 :         CHECK_NULP(formats, "dbstrbuild", 5, FAIL);
    7395             : 
    7396          80 :         va_start(ap, formats);
    7397          80 :         rc = tds_vstrbuild(charbuf, bufsize, &resultlen, text, TDS_NULLTERM, formats, TDS_NULLTERM, ap);
    7398          80 :         charbuf[resultlen] = '\0';
    7399          80 :         va_end(ap);
    7400          80 :         return TDS_SUCCEED(rc) ? resultlen : -1;
    7401             : }
    7402             : 
    7403             : static char *
    7404           0 : _dbprdate(char *timestr)
    7405             : {
    7406           0 :         time_t currtime = time(NULL);
    7407             :         
    7408           0 :         assert(timestr);
    7409             : 
    7410           0 :         strcpy(timestr, asctime(gmtime(&currtime)));
    7411           0 :         timestr[strlen(timestr) - 1] = '\0';    /* remove newline */
    7412           0 :         return timestr;
    7413             : 
    7414             : }
    7415             : 
    7416             : static DBINT
    7417           0 : _dbnullable(DBPROCESS * dbproc, int column)
    7418             : {
    7419             :         TDSCOLUMN *colinfo;
    7420             :         TDSRESULTINFO *resinfo;
    7421             : 
    7422           0 :         assert(dbproc && dbproc->tds_socket);
    7423           0 :         resinfo = dbproc->tds_socket->res_info;
    7424           0 :         if (!resinfo || column < 1 || column > resinfo->num_cols)
    7425             :                 return FALSE;
    7426           0 :         colinfo = resinfo->columns[column - 1];
    7427             : 
    7428           0 :         if (colinfo->column_nullable) 
    7429             :                 return TRUE;
    7430           0 :         return FALSE;
    7431             : }
    7432             : 
    7433             : /** Returns type in string. Used for debugging purpose */
    7434             : static const char *
    7435           0 : tds_prdatatype(int datatype_token)
    7436             : {
    7437           0 :         switch ((TDS_SERVER_TYPE) datatype_token) {
    7438             :         case SYBCHAR:           return "SYBCHAR";
    7439           0 :         case SYBVARCHAR:        return "SYBVARCHAR";
    7440           0 :         case SYBINTN:           return "SYBINTN";
    7441           0 :         case SYBINT1:           return "SYBINT1";
    7442           0 :         case SYBINT2:           return "SYBINT2";
    7443           0 :         case SYBINT4:           return "SYBINT4";
    7444           0 :         case SYBINT8:           return "SYBINT8";
    7445           0 :         case SYBFLT8:           return "SYBFLT8";
    7446           0 :         case SYBDATETIME:       return "SYBDATETIME";
    7447           0 :         case SYBBIT:            return "SYBBIT";
    7448           0 :         case SYBTEXT:           return "SYBTEXT";
    7449           0 :         case SYBNTEXT:          return "SYBNTEXT";
    7450           0 :         case SYBIMAGE:          return "SYBIMAGE";
    7451           0 :         case SYBMONEY4:         return "SYBMONEY4";
    7452           0 :         case SYBMONEY:          return "SYBMONEY";
    7453           0 :         case SYBDATETIME4:      return "SYBDATETIME4";
    7454           0 :         case SYBREAL:           return "SYBREAL";
    7455           0 :         case SYBBINARY:         return "SYBBINARY";
    7456           0 :         case SYBVOID:           return "SYBVOID";
    7457           0 :         case SYBVARBINARY:      return "SYBVARBINARY";
    7458           0 :         case SYBNVARCHAR:       return "SYBNVARCHAR";
    7459           0 :         case SYBBITN:           return "SYBBITN";
    7460           0 :         case SYBNUMERIC:        return "SYBNUMERIC";
    7461           0 :         case SYBDECIMAL:        return "SYBDECIMAL";
    7462           0 :         case SYBFLTN:           return "SYBFLTN";
    7463           0 :         case SYBMONEYN:         return "SYBMONEYN";
    7464           0 :         case SYBDATETIMN:       return "SYBDATETIMN";
    7465           0 :         case XSYBCHAR:          return "XSYBCHAR";
    7466           0 :         case XSYBVARCHAR:       return "XSYBVARCHAR";
    7467           0 :         case XSYBNVARCHAR:      return "XSYBNVARCHAR";
    7468           0 :         case XSYBNCHAR:         return "XSYBNCHAR";
    7469           0 :         case XSYBVARBINARY:     return "XSYBVARBINARY";
    7470           0 :         case XSYBBINARY:        return "XSYBBINARY";
    7471           0 :         case SYBLONGBINARY:     return "SYBLONGBINARY";
    7472           0 :         case SYBSINT1:          return "SYBSINT1";
    7473           0 :         case SYBUINT2:          return "SYBUINT2";
    7474           0 :         case SYBUINT4:          return "SYBUINT4";
    7475           0 :         case SYBUINT8:          return "SYBUINT8";
    7476           0 :         case SYBUNIQUE:         return "SYBUNIQUE";
    7477           0 :         case SYBVARIANT:        return "SYBVARIANT";
    7478           0 :         case SYBMSXML:          return "SYBMSXML";
    7479           0 :         case SYBMSDATE:         return "SYBMSDATE";
    7480           0 :         case SYBMSTIME:         return "SYBMSTIME";
    7481           0 :         case SYBMSDATETIME2:    return "SYBMSDATETIME2";
    7482           0 :         case SYBMSDATETIMEOFFSET: return "SYBMSDATETIMEOFFSET";
    7483           0 :         case SYBDATE:           return "SYBDATE";
    7484           0 :         case SYBTIME:           return "SYBTIME";
    7485           0 :         case SYB5BIGDATETIME:   return "SYBBIGDATETIME";
    7486           0 :         case SYB5BIGTIME:       return "SYBBIGTIME";
    7487           0 :         case SYBMSUDT:          return "SYBMSUDT";
    7488           0 :         case SYBUINT1:          return "SYBUINT1";
    7489           0 :         case SYBDATEN:          return "SYBDATEN";
    7490           0 :         case SYB5INT8:          return "SYB5INT8";
    7491           0 :         case SYBINTERVAL:       return "SYBINTERVAL";
    7492           0 :         case SYBTIMEN:          return "SYBTIMEN";
    7493           0 :         case SYBUINTN:          return "SYBUINTN";
    7494           0 :         case SYBUNITEXT:        return "SYBUNITEXT";
    7495           0 :         case SYBXML:            return "SYBXML";
    7496           0 :         case SYBMSTABLE:        return "SYBMSTABLE";
    7497             :         }
    7498           0 :         return "(unknown)";
    7499             : }
    7500             : #if 1
    7501             : void
    7502      410744 : copy_data_to_host_var(DBPROCESS * dbproc, TDS_SERVER_TYPE srctype, const BYTE * src, DBINT srclen, 
    7503             :                       BYTE * dest, DBINT destlen,
    7504             :                       int bindtype, DBINT *indicator)
    7505             : {
    7506             :         CONV_RESULT dres;
    7507             :         DBINT ret;
    7508             :         int len;
    7509      410744 :         DBINT indicator_value = 0;
    7510             : 
    7511      410744 :         bool limited_dest_space = false;
    7512      410744 :         TDS_SERVER_TYPE desttype = dblib_bound_type(bindtype);
    7513             : 
    7514      410744 :         tdsdump_log(TDS_DBG_FUNC, "copy_data_to_host_var(%d [%s] len %d => %d [%s] len %d)\n", 
    7515             :                      srctype, tds_prdatatype(srctype), srclen, desttype, tds_prdatatype(desttype), destlen);
    7516      820990 :         CHECK_NULP(src, "copy_data_to_host_var", 3, );
    7517      410744 :         CHECK_NULP(dest, "copy_data_to_host_var", 6, );
    7518      410744 :         if (desttype == TDS_INVALID_TYPE)
    7519             :                 return;
    7520             :         /* indicator can be NULL */
    7521             : 
    7522      410744 :         assert(srclen >= 0);
    7523             : 
    7524      410744 :         if (destlen > 0) {
    7525         290 :                 limited_dest_space = true;
    7526             :         }
    7527             : 
    7528             :         /* oft times we are asked to convert a data type to itself */
    7529             : 
    7530      410744 :         if (desttype == SYBNUMERIC) {
    7531         240 :                 DBNUMERIC *num = NULL;          /* num->scale is unsigned */
    7532             : 
    7533             :                 /* only MS, use always source */
    7534         240 :                 if (bindtype == SRCNUMERICBIND || bindtype == SRCDECIMALBIND) {
    7535         120 :                         if (is_numeric_type(srctype))
    7536             :                                 num = (DBNUMERIC*) src;
    7537             :                         else
    7538          40 :                                 num = (DBNUMERIC*) dest;
    7539         120 :                 } else if (dbproc->msdblib) {
    7540             :                         /* MS by default use only destination information */
    7541             :                         num = (DBNUMERIC*) dest;
    7542             :                 } else {
    7543             :                         /* Sybase, dbbind means source or default */
    7544             :                         /* TODO if dbbind_ps is used is more complicated */
    7545          60 :                         if (is_numeric_type(srctype))
    7546             :                                 num = (DBNUMERIC*) src;
    7547             :                 }
    7548             :                 if (!num) {
    7549          20 :                         dres.n.precision = 18;
    7550          20 :                         dres.n.scale = 0;
    7551             :                 } else {
    7552         220 :                         dres.n.precision = num->precision;
    7553         220 :                         dres.n.scale = num->scale;
    7554             :                 }
    7555      411850 :         } else if ((srctype == desttype) ||
    7556        2434 :                 (is_similar_type(srctype, desttype))) {
    7557             : 
    7558      410246 :                 tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var() srctype == desttype\n");
    7559      410246 :                 switch (desttype) {
    7560             : 
    7561          10 :                 case SYBVARBINARY:
    7562             :                 case SYBBINARY:
    7563             :                 case SYBIMAGE:
    7564          10 :                         if (bindtype == VARYBINBIND) {
    7565          10 :                                 DBVARYBIN *bin = (DBVARYBIN*) dest;
    7566          10 :                                 if (limited_dest_space) {
    7567          10 :                                         if (srclen > sizeof(bin->array)) {
    7568           0 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7569           0 :                                                 indicator_value = srclen;
    7570           0 :                                                 srclen = sizeof(bin->array);
    7571             :                                         }
    7572             :                                 }
    7573          10 :                                 memcpy(bin->array, src, srclen);
    7574          10 :                                 bin->len = srclen;
    7575          10 :                                 break;
    7576             :                         }
    7577             : 
    7578           0 :                         if (srclen > destlen && destlen >= 0) {
    7579           0 :                                 dbperror(dbproc, SYBECOFL, 0);
    7580             :                         } else {
    7581           0 :                                 memcpy(dest, src, srclen);
    7582           0 :                                 if (srclen < destlen)
    7583           0 :                                         memset(dest + srclen, 0, destlen - srclen);
    7584             :                         }
    7585             :                         break;
    7586             : 
    7587      205622 :                 case SYBCHAR:
    7588             :                 case SYBVARCHAR:
    7589             :                 case SYBTEXT:
    7590             : 
    7591      205622 :                         switch (bindtype) {
    7592             :                                 case NTBSTRINGBIND: /* strip trailing blanks, null term */
    7593         574 :                                         while (srclen && src[srclen - 1] == ' ') {
    7594         252 :                                                 --srclen;
    7595             :                                         }
    7596         322 :                                         if (limited_dest_space) {
    7597          40 :                                                 if (srclen + 1 > destlen) {
    7598          10 :                                                         dbperror(dbproc, SYBECOFL, 0);
    7599          10 :                                                         indicator_value = srclen + 1;
    7600          10 :                                                         srclen = destlen - 1;
    7601             :                                                 }
    7602             :                                         }
    7603         322 :                                         memcpy(dest, src, srclen);
    7604         322 :                                         dest[srclen] = '\0';
    7605         322 :                                         break;
    7606      205240 :                                 case STRINGBIND:   /* pad with blanks, NUL term */
    7607      205240 :                                         if (limited_dest_space) {
    7608          30 :                                                 if (srclen + 1 > destlen) {
    7609          20 :                                                         dbperror(dbproc, SYBECOFL, 0);
    7610          20 :                                                         indicator_value = srclen + 1;
    7611          20 :                                                         srclen = destlen - 1;
    7612             :                                                 }
    7613             :                                         } else {
    7614      205210 :                                                 destlen = srclen + 1;
    7615             :                                         }
    7616      205240 :                                         memcpy(dest, src, srclen);
    7617      205240 :                                         if (srclen < destlen)
    7618      205240 :                                                 memset(dest + srclen, ' ', destlen - srclen - 1);
    7619      205240 :                                         dest[destlen - 1] = '\0';
    7620      205240 :                                         break;
    7621          40 :                                 case CHARBIND:   /* pad with blanks, NO NUL term */
    7622          40 :                                         if (limited_dest_space) {
    7623          30 :                                                 if (srclen > destlen) {
    7624          10 :                                                         dbperror(dbproc, SYBECOFL, 0);
    7625          10 :                                                         indicator_value = srclen;
    7626          10 :                                                         srclen = destlen;
    7627             :                                                 }
    7628             :                                         } else {
    7629             :                                                 destlen = srclen; 
    7630             :                                         }
    7631          40 :                                         memcpy(dest, src, srclen);
    7632          40 :                                         if (srclen < destlen)
    7633          10 :                                                 memset(dest + srclen, ' ', destlen - srclen);
    7634             :                                         break;
    7635          20 :                                 case VARYCHARBIND: /* strip trailing blanks, NO NUL term */
    7636          20 :                                         if (limited_dest_space) {
    7637          20 :                                                 if (srclen > destlen) {
    7638           0 :                                                         dbperror(dbproc, SYBECOFL, 0);
    7639           0 :                                                         indicator_value = srclen;
    7640           0 :                                                         srclen = destlen;
    7641             :                                                 }
    7642             :                                         }
    7643          20 :                                         memcpy(((DBVARYCHAR *)dest)->str, src, srclen);
    7644          20 :                                         ((DBVARYCHAR *)dest)->len = srclen;
    7645          20 :                                         break;
    7646             :                         } 
    7647             :                         break;
    7648      204614 :                 case SYBINT1:
    7649             :                 case SYBINT2:
    7650             :                 case SYBINT4:
    7651             :                 case SYBINT8:
    7652             :                 case SYBFLT8:
    7653             :                 case SYBREAL:
    7654             :                 case SYBBIT:
    7655             :                 case SYBBITN:
    7656             :                 case SYBMONEY:
    7657             :                 case SYBMONEY4:
    7658             :                 case SYBDATETIME:
    7659             :                 case SYBDATETIME4:
    7660             :                 case SYBDATE:
    7661             :                 case SYBTIME:
    7662             :                 case SYB5BIGDATETIME:
    7663             :                 case SYB5BIGTIME:
    7664             :                 case SYBUNIQUE:
    7665      204614 :                         ret = tds_get_size_by_type(desttype);
    7666      204614 :                         memcpy(dest, src, ret);
    7667      204614 :                         break;
    7668             : 
    7669           0 :                 case SYBMSDATE:
    7670             :                 case SYBMSTIME:
    7671             :                 case SYBMSDATETIME2:
    7672             :                 case SYBMSDATETIMEOFFSET:
    7673           0 :                         ret = sizeof(TDS_DATETIMEALL);
    7674           0 :                         memcpy(dest, src, ret);
    7675           0 :                         break;
    7676             : 
    7677             :                 default:
    7678             :                         break;
    7679             :                 }
    7680      410246 :                 if (indicator)
    7681         136 :                         *indicator = indicator_value;
    7682             : 
    7683             :                 return;
    7684             : 
    7685             :         } /* end srctype == desttype */
    7686             : 
    7687         498 :         len = tds_convert(g_dblib_ctx.tds_ctx, srctype, src, srclen, desttype, &dres);
    7688             : 
    7689         498 :         tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var(): tds_convert returned %d\n", len);
    7690             : 
    7691         498 :         if (len < 0) {
    7692           0 :                 _dblib_convert_err(dbproc, len);
    7693           0 :                 return;
    7694             :         }
    7695             : 
    7696         498 :         switch (desttype) {
    7697          60 :         case SYBVARBINARY:
    7698             :         case SYBBINARY:
    7699             :         case SYBIMAGE:
    7700          60 :                 if (bindtype == VARYBINBIND) {
    7701          30 :                         if (limited_dest_space) {
    7702          30 :                                 if (len > sizeof(((DBVARYBIN *)dest)->array)) {
    7703           0 :                                         dbperror(dbproc, SYBECOFL, 0);
    7704           0 :                                         indicator_value = len;
    7705           0 :                                         len = sizeof(((DBVARYBIN *)dest)->array);
    7706             :                                 }
    7707             :                         }
    7708          30 :                         memcpy(((DBVARYBIN *)dest)->array, dres.c, len);
    7709          30 :                         ((DBVARYBIN *)dest)->len = len;
    7710             :                 } else {
    7711          30 :                         if (len > destlen && destlen >= 0) {
    7712           0 :                                 dbperror(dbproc, SYBECOFL, 0);
    7713             :                         } else {
    7714          30 :                                 memcpy(dest, dres.ib, len);
    7715          30 :                                 if (len < destlen)
    7716          10 :                                         memset(dest + len, 0, destlen - len);
    7717             :                         }
    7718             :                 }
    7719          60 :                 TDS_ZERO_FREE(dres.ib);
    7720          60 :                 break;
    7721         250 :         case SYBINT1:
    7722             :         case SYBINT2:
    7723             :         case SYBINT4:
    7724             :         case SYBINT8:
    7725             :         case SYBFLT8:
    7726             :         case SYBREAL:
    7727             :         case SYBBIT:
    7728             :         case SYBBITN:
    7729             :         case SYBMONEY:
    7730             :         case SYBMONEY4:
    7731             :         case SYBDATETIME:
    7732             :         case SYBDATETIME4:
    7733             :         case SYBDATE:
    7734             :         case SYBTIME:
    7735             :         case SYBNUMERIC:
    7736             :         case SYBDECIMAL:
    7737             :         case SYBUNIQUE:
    7738             :         case SYBMSDATE:
    7739             :         case SYBMSTIME:
    7740             :         case SYB5BIGDATETIME:
    7741             :         case SYB5BIGTIME:
    7742             :         case SYBMSDATETIME2:
    7743             :         case SYBMSDATETIMEOFFSET:
    7744         250 :                 memcpy(dest, &(dres.ti), len);
    7745         250 :                 break;
    7746         188 :         case SYBCHAR:
    7747             :         case SYBVARCHAR:
    7748             :         case SYBTEXT:
    7749         188 :                 tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var() outputs %d bytes char data destlen = %d \n", len, destlen);
    7750         188 :                 switch (bindtype) {
    7751             :                         case NTBSTRINGBIND: /* strip trailing blanks, null term */
    7752          40 :                                 while (len && dres.c[len - 1] == ' ') {
    7753           0 :                                         --len;
    7754             :                                 }
    7755          40 :                                 if (limited_dest_space) {
    7756          30 :                                         if (len + 1 > destlen) {
    7757          10 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7758          10 :                                                 len = destlen - 1;
    7759             :                                         }
    7760             :                                 }
    7761          40 :                                 memcpy(dest, dres.c, len);
    7762          40 :                                 dest[len] = '\0';
    7763          40 :                                 break;
    7764          98 :                         case STRINGBIND:   /* pad with blanks, null term */
    7765          98 :                                 if (limited_dest_space) {
    7766          30 :                                         if (len + 1 > destlen) {
    7767          10 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7768          10 :                                                 len = destlen - 1;
    7769             :                                         }
    7770             :                                 } else {
    7771          68 :                                         destlen = len + 1;
    7772             :                                 }
    7773          98 :                                 memcpy(dest, dres.c, len);
    7774          98 :                                 if (len < destlen)
    7775          98 :                                         memset(dest + len, ' ', destlen - len - 1);
    7776          98 :                                 dest[destlen - 1] = '\0';
    7777          98 :                                 break;
    7778          50 :                         case CHARBIND:   /* pad with blanks, NO null term */
    7779          50 :                                 if (limited_dest_space) {
    7780          40 :                                         if (len > destlen) {
    7781          10 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7782          10 :                                                 indicator_value = len;
    7783          10 :                                                 len = destlen;
    7784             :                                         }
    7785             :                                 } else {
    7786             :                                         destlen = len; 
    7787             :                                 }
    7788          50 :                                 memcpy(dest, dres.c, len);
    7789          50 :                                 if (len < destlen)
    7790          20 :                                         memset(dest + len, ' ', destlen - len);
    7791             :                                 break;
    7792           0 :                         case VARYCHARBIND: /* strip trailing blanks, NO null term */
    7793           0 :                                 if (limited_dest_space) {
    7794           0 :                                         if (len > sizeof(((DBVARYCHAR *)dest)->str)) {
    7795           0 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7796           0 :                                                 indicator_value = len;
    7797           0 :                                                 len = sizeof(((DBVARYCHAR *)dest)->str);
    7798             :                                         }
    7799             :                                 } 
    7800           0 :                                 memcpy(((DBVARYCHAR *)dest)->str, dres.c, len);
    7801           0 :                                 ((DBVARYCHAR *)dest)->len = len;
    7802           0 :                                 break;
    7803             :                 } 
    7804             : 
    7805         188 :                 free(dres.c);
    7806         188 :                 break;
    7807           0 :         default:
    7808           0 :                 tdsdump_log(TDS_DBG_INFO1, "error: copy_data_to_host_var(): unrecognized desttype %d \n", desttype);
    7809             :                 break;
    7810             : 
    7811             :         }
    7812         498 :         if (indicator)
    7813           0 :                 *indicator = indicator_value;
    7814             : }
    7815             : #endif
    7816             : 
    7817             : /** \internal
    7818             :  * \ingroup dblib_internal
    7819             :  * \remarks member msgno Vendor-defined message number
    7820             :  * \remarks member severity Is passed to the error handler 
    7821             :  * \remarks member msgtext Text of message
    7822             :  */
    7823             : typedef struct _dblib_error_message 
    7824             : {
    7825             :         DBINT msgno;
    7826             :         int severity;
    7827             :         const char *msgtext;
    7828             : } DBLIB_ERROR_MESSAGE;
    7829             : 
    7830             : /*
    7831             :  * The msgtext member holds up to two strings.  The first one is the message text, which may contain placeholders.  
    7832             :  * The second one, if it exists, is the format string for dbstrbuild().  Messages containing no placeholders still need
    7833             :  * an extra NULL to indicate a zero-length format string. 
    7834             :  */
    7835             : static const DBLIB_ERROR_MESSAGE dblib_error_messages[] = 
    7836             :         { { SYBEVERDOWN,           EXINFO,      "TDS version downgraded to 7.1!\0" }
    7837             :         , { SYBEICONVIU,     EXCONVERSION,      "Some character(s) could not be converted into client's character set\0" }
    7838             :         , { SYBEICONVAVAIL,  EXCONVERSION,      "Character set conversion is not available between client character set '%1!' and "
    7839             :                                                 "server character set '%2!'\0%s %s" }
    7840             :         , { SYBEICONVO,      EXCONVERSION,      "Error converting characters into server's character set. Some character(s) could "
    7841             :                                                 "not be converted\0" }
    7842             :         , { SYBEICONVI,      EXCONVERSION,      "Some character(s) could not be converted into client's character set.  Unconverted "
    7843             :                                                 "bytes were changed to question marks ('?')\0" }
    7844             :         , { SYBEICONV2BIG,   EXCONVERSION,      "Buffer overflow converting characters from client into server's character set\0" }
    7845             :         
    7846             :         
    7847             :         , { SYBEPORT,              EXUSER,      "Both port and instance specified\0" }
    7848             :         , { SYBETDSVER,            EXUSER,      "Cannot bcp with TDSVER < 5.0\0" }
    7849             :         , { SYBEAAMT,           EXPROGRAM,      "User attempted a dbaltbind with mismatched column and variable types\0" }
    7850             :         , { SYBEABMT,           EXPROGRAM,      "User attempted a dbbind with mismatched column and variable types\0" }
    7851             :         , { SYBEABNC,           EXPROGRAM,      "Attempt to bind to a non-existent column\0" }
    7852             :         , { SYBEABNP,           EXPROGRAM,      "Attempt to bind using NULL pointers\0" }
    7853             :         , { SYBEABNV,           EXPROGRAM,      "Attempt to bind to a NULL program variable\0" }
    7854             :         , { SYBEACNV,        EXCONVERSION,      "Attempt to do data-conversion with NULL destination variable.\0" }
    7855             :         , { SYBEADST,       EXCONSISTENCY,      "International Release: Error in attempting to determine the size of a pair of "
    7856             :                                                 "translation tables\0" }
    7857             :         , { SYBEAICF,       EXCONSISTENCY,      "International Release: Error in attempting to install custom format\0" }
    7858             :         , { SYBEALTT,       EXCONSISTENCY,      "International Release: Error in attempting to load a pair of translation tables\0" }
    7859             :         , { SYBEAOLF,          EXRESOURCE,      "International Release: Error in attempting to open a localization file\0" }
    7860             :         , { SYBEAPCT,       EXCONSISTENCY,      "International Release: Error in attempting to perform a character set translation\0" }
    7861             :         , { SYBEAPUT,           EXPROGRAM,      "Attempt to print unknown token\0" }
    7862             :         , { SYBEARDI,          EXRESOURCE,      "International Release: Error in attempting to read datetime information from a "
    7863             :                                                 "localization file\0" }
    7864             :         , { SYBEARDL,          EXRESOURCE,      "International Release: Error in attempting to read the dblib.loc localization file\0" }
    7865             :         , { SYBEASEC,           EXPROGRAM,      "Attempt to send an empty command buffer to the server\0" }
    7866             :         , { SYBEASNL,           EXPROGRAM,      "Attempt to set fields in a null LOGINREC\0" }
    7867             :         , { SYBEASTL,           EXPROGRAM,      "Synchronous I/O attempted at AST level\0" }
    7868             :         , { SYBEASUL,           EXPROGRAM,      "Attempt to set unknown LOGINREC field\0" }
    7869             :         , { SYBEAUTN,           EXPROGRAM,      "Attempt to update the timestamp of a table that has no timestamp column\0" }
    7870             :         , { SYBEBADPK,             EXINFO,      "Packet size of %1! not supported -- size of %2! used instead!\0%d %d" }
    7871             :         , { SYBEBBCI,              EXINFO,      "Batch successfully bulk copied to the server\0" }
    7872             :         , { SYBEBBL,            EXPROGRAM,      "Bad bindlen parameter passed to dbsetnull\0" }
    7873             :         , { SYBEBCBC,           EXPROGRAM,      "bcp_columns must be called before bcp_colfmt and bcp_colfmt_ps\0" }
    7874             :         , { SYBEBCBNPR,         EXPROGRAM,      "bcp_bind: if varaddr is NULL, prefixlen must be 0 "
    7875             :                                                 "and no terminator should be specified\0" }
    7876             :         , { SYBEBCBNTYP,        EXPROGRAM,      "bcp_bind: if varaddr is NULL and varlen greater than 0, the table column type "
    7877             :                                                 "must be SYBTEXT or SYBIMAGE and the program variable type must be SYBTEXT, SYBCHAR, "
    7878             :                                                 "SYBIMAGE or SYBBINARY\0" }
    7879             :         , { SYBEBCBPREF,        EXPROGRAM,      "Illegal prefix length. Legal values are 0, 1, 2 or 4\0" }
    7880             :         , { SYBEBCFO,              EXUSER,      "bcp host files must contain at least one column\0" }
    7881             :         , { SYBEBCHLEN,         EXPROGRAM,      "host_collen should be greater than or equal to -1\0" }
    7882             :         , { SYBEBCIS,       EXCONSISTENCY,      "Attempt to bulk copy an illegally-sized column value to the server\0" }
    7883             :         , { SYBEBCIT,           EXPROGRAM,      "It is illegal to use BCP terminators with program variables other than SYBCHAR, "
    7884             :                                                 "SYBBINARY, SYBTEXT, or SYBIMAGE\0" }
    7885             :         , { SYBEBCITBLEN,       EXPROGRAM,      "bcp_init: tblname parameter is too long\0" }
    7886             :         , { SYBEBCITBNM,        EXPROGRAM,      "bcp_init: tblname parameter cannot be NULL\0" }
    7887             :         , { SYBEBCMTXT,         EXPROGRAM,      "bcp_moretext may be used only when there is at least one text or image column in "
    7888             :                                                 "the server table\0" }
    7889             :         , { SYBEBCNL,          EXNONFATAL,      "Negative length-prefix found in BCP data-file\0" }
    7890             :         , { SYBEBCNN,              EXUSER,      "Attempt to bulk copy a NULL value into a Server column "
    7891             :                                                 "which does not accept null values\0" }
    7892             :         , { SYBEBCNT,              EXUSER,      "Attempt to use Bulk Copy with a non-existent Server table\0" }
    7893             :         , { SYBEBCOR,       EXCONSISTENCY,      "Attempt to bulk copy an oversized row to the server\0" }
    7894             :         , { SYBEBCPB,           EXPROGRAM,      "bcp_bind, bcp_moretext and bcp_sendrow may not be used after bcp_init has been "
    7895             :                                                 "passed a non-NULL input file name\0" }
    7896             :         , { SYBEBCPCTYP,        EXPROGRAM,      "bcp_colfmt: If table_colnum is 0, host_type cannot be 0\0" }
    7897             :         , { SYBEBCPI,           EXPROGRAM,      "bcp_init must be called before any other bcp routines\0" }
    7898             :         , { SYBEBCPN,           EXPROGRAM,      "bcp_bind, bcp_collen, bcp_colptr, bcp_moretext and bcp_sendrow may be used only "
    7899             :                                                 "after bcp_init has been called with the copy direction set to DB_IN\0" }
    7900             :         , { SYBEBCPREC,        EXNONFATAL,      "Column %1!: Illegal precision value encountered\0%d" }
    7901             :         , { SYBEBCPREF,         EXPROGRAM,      "Illegal prefix length. Legal values are -1, 0, 1, 2 or 4\0" }
    7902             :         , { SYBEBCRE,          EXNONFATAL,      "I/O error while reading bcp datafile\0" }
    7903             :         , { SYBEBCRO,              EXINFO,      "The BCP hostfile '%1!' contains only %2! rows. It was impossible to read the "
    7904             :                                                 "requested %3! rows\0%s %d %d" }
    7905             :         , { SYBEBCSA,              EXUSER,      "The BCP hostfile '%1!' contains only %2! rows. "
    7906             :                                                 "Skipping all of these rows is not allowed\0%s %d" }
    7907             :         , { SYBEBCSET,      EXCONSISTENCY,      "Unknown character-set encountered\0" }
    7908             :         , { SYBEBCSI,           EXPROGRAM,      "Host-file columns may be skipped only when copying into the Server\0" }
    7909             :         , { SYBEBCSNDROW,       EXPROGRAM,      "bcp_sendrow may not be called unless all text data for the previous row has been "
    7910             :                                                 "sent using bcp_moretext\0" }
    7911             :         , { SYBEBCSNTYP,        EXPROGRAM,      "column number %1!: if varaddr is NULL and varlen greater than 0, the table column "
    7912             :                                                 "type must be SYBTEXT or SYBIMAGE and the program variable type must be SYBTEXT, "
    7913             :                                                 "SYBCHAR, SYBIMAGE or SYBBINARY\0%d" }
    7914             :         , { SYBEBCUC,          EXRESOURCE,      "bcp: Unable to close host datafile\0" }
    7915             :         , { SYBEBCUO,          EXRESOURCE,      "bcp: Unable to open host datafile\0" }
    7916             :         , { SYBEBCVH,           EXPROGRAM,      "bcp_exec may be called only after bcp_init has been passed a valid host file\0" }
    7917             :         , { SYBEBCVLEN,         EXPROGRAM,      "varlen should be greater than or equal to -1\0" }
    7918             :         , { SYBEBCWE,          EXNONFATAL,      "I/O error while writing bcp datafile\0" }
    7919             :         , { SYBEBDIO,           EXPROGRAM,      "Bad bulk copy direction. Must be either IN or OUT\0" }
    7920             :         , { SYBEBEOF,          EXNONFATAL,      "Unexpected EOF encountered in bcp datafile\0" }
    7921             :         , { SYBEBIHC,           EXPROGRAM,      "Incorrect host-column number found in bcp format file\0" }
    7922             :         , { SYBEBIVI,           EXPROGRAM,      "bcp_columns, bcp_colfmt and bcp_colfmt_ps may be used only after bcp_init has been "
    7923             :                                                 "passed a valid input file\0" }
    7924             :         , { SYBEBNCR,           EXPROGRAM,      "Attempt to bind user variable to a non-existent compute row\0" }
    7925             :         , { SYBEBNUM,           EXPROGRAM,      "Bad numbytes parameter passed to dbstrcpy\0" }
    7926             :         , { SYBEBPKS,           EXPROGRAM,      "In DBSETLPACKET, the packet size parameter must be between 0 and 999999\0" }
    7927             :         , { SYBEBPREC,          EXPROGRAM,      "Illegal precision specified\0" }
    7928             :         , { SYBEBPROBADDEF, EXCONSISTENCY,      "bcp protocol error: illegal default column id received\0" }
    7929             :         , { SYBEBPROCOL,    EXCONSISTENCY,      "bcp protocol error: returned column count differs from the actual number of "
    7930             :                                                 "columns received\0" }
    7931             :         , { SYBEBPRODEF,    EXCONSISTENCY,      "bcp protocol error: expected default information and got none\0" }
    7932             :         , { SYBEBPRODEFID,  EXCONSISTENCY,      "bcp protocol error: default column id and actual column id are not same\0" }
    7933             :         , { SYBEBPRODEFTYP, EXCONSISTENCY,      "bcp protocol error: default value datatype differs from column datatype\0" }
    7934             :         , { SYBEBPROEXTDEF, EXCONSISTENCY,      "bcp protocol error: more than one row of default information received\0" }
    7935             :         , { SYBEBPROEXTRES, EXCONSISTENCY,      "bcp protocol error: unexpected set of results received\0" }
    7936             :         , { SYBEBPRONODEF,  EXCONSISTENCY,      "bcp protocol error: default value received for column that does not have default\0" }
    7937             :         , { SYBEBPRONUMDEF, EXCONSISTENCY,      "bcp protocol error: expected number of defaults differs from the actual number of "
    7938             :                                                 "defaults received\0" }
    7939             :         , { SYBEBRFF,          EXRESOURCE,      "I/O error while reading bcp format file\0" }
    7940             :         , { SYBEBSCALE,         EXPROGRAM,      "Illegal scale specified\0" }
    7941             :         , { SYBEBTMT,           EXPROGRAM,      "Attempt to send too much text data via the bcp_moretext call\0" }
    7942             :         , { SYBEBTOK,              EXCOMM,      "Bad token from the server: Datastream processing out of sync\0" }
    7943             :         , { SYBEBTYP,           EXPROGRAM,      "Unknown bind type passed to DB-Library function\0" }
    7944             :         , { SYBEBTYPSRV,        EXPROGRAM,      "Datatype is not supported by the server\0" }
    7945             :         , { SYBEBUCE,          EXRESOURCE,      "bcp: Unable to close error file\0" }
    7946             :         , { SYBEBUCF,           EXPROGRAM,      "bcp: Unable to close format file\0" }
    7947             :         , { SYBEBUDF,           EXPROGRAM,      "bcp: Unrecognized datatype found in format file\0" }
    7948             :         , { SYBEBUFF,           EXPROGRAM,      "bcp: Unable to create format file\0" }
    7949             :         , { SYBEBUFL,       EXCONSISTENCY,      "DB-Library internal error-send buffer length corrupted\0" }
    7950             :         , { SYBEBUOE,          EXRESOURCE,      "bcp: Unable to open error file\0" }
    7951             :         , { SYBEBUOF,           EXPROGRAM,      "bcp: Unable to open format file\0" }
    7952             :         , { SYBEBWEF,          EXNONFATAL,      "I/O error while writing bcp error file\0" }
    7953             :         , { SYBEBWFF,          EXRESOURCE,      "I/O error while writing bcp format file\0" }
    7954             :         , { SYBECAP,               EXCOMM,      "DB-Library capabilities not accepted by the Server\0" }
    7955             :         , { SYBECAPTYP,            EXCOMM,      "Unexpected capability type in CAPABILITY datastream\0" }
    7956             :         , { SYBECDNS,       EXCONSISTENCY,      "Datastream indicates that a compute column is derived from a non-existent select "
    7957             :                                                 "list member\0" }
    7958             :         , { SYBECDOMAIN,     EXCONVERSION,      "Source field value is not within the domain of legal values\0" }
    7959             :         , { SYBECINTERNAL,   EXCONVERSION,      "Internal Conversion error\0" }
    7960             :         , { SYBECLOS,              EXCOMM,      "Error in closing network connection\0" }
    7961             :         , { SYBECLPR,        EXCONVERSION,      "Data conversion resulted in loss of precision\0" }
    7962             :         , { SYBECNOR,           EXPROGRAM,      "Column number out of range\0" }
    7963             :         , { SYBECNOV,        EXCONVERSION,      "Attempt to set variable to NULL resulted in overflow\0" }
    7964             :         , { SYBECOFL,        EXCONVERSION,      "Data conversion resulted in overflow\0" }
    7965             :         , { SYBECONN,              EXCOMM,      "Unable to connect: TDS server is unavailable or does not exist\0" }
    7966             :         , { SYBECRNC,           EXPROGRAM,      "The current row is not a result of compute clause %1!, so it is illegal to attempt "
    7967             :                                                 "to extract that data from this row\0%d" }
    7968             :         , { SYBECRSAGR,         EXPROGRAM,      "Aggregate functions are not allowed in a cursor statement\0" }
    7969             :         , { SYBECRSBROL,        EXPROGRAM,      "Backward scrolling cannot be used in a forward scrolling cursor\0" }
    7970             :         , { SYBECRSBSKEY,       EXPROGRAM,      "Keyset cannot be scrolled backward in mixed cursors with a previous fetch type\0" }
    7971             :         , { SYBECRSBUFR,        EXPROGRAM,      "Row buffering should not be turned on when using cursor APIs\0" }
    7972             :         , { SYBECRSDIS,         EXPROGRAM,      "Cursor statement contains one of the disallowed phrases compute, union, for "
    7973             :                                                 "browse, or select into\0" }
    7974             :         , { SYBECRSFLAST,       EXPROGRAM,      "Fetch type LAST requires fully keyset driven cursors\0" }
    7975             :         , { SYBECRSFRAND,       EXPROGRAM,      "Fetch types RANDOM and RELATIVE can only be used within the keyset of keyset "
    7976             :                                                 "driven cursors\0" }
    7977             :         , { SYBECRSFROWN,       EXPROGRAM,      "Row number to be fetched is outside valid range\0" }
    7978             :         , { SYBECRSFTYPE,      EXRESOURCE,      "Unknown fetch type\0" }
    7979             :         , { SYBECRSINV,         EXPROGRAM,      "Invalid cursor statement\0" }
    7980             :         , { SYBECRSINVALID,    EXRESOURCE,      "The cursor handle is invalid\0" }
    7981             :         , { SYBECRSMROWS,      EXRESOURCE,      "Multiple rows are returned, only one is expected while retrieving dbname\0" }
    7982             :         , { SYBECRSNOBIND,      EXPROGRAM,      "Cursor bind must be called prior to dbcursor invocation\0" }
    7983             :         , { SYBECRSNOCOUNT,     EXPROGRAM,      "The DBNOCOUNT option should not be turned on "
    7984             :                                                 "when doing updates or deletes with dbcursor\0" }
    7985             :         , { SYBECRSNOFREE,      EXPROGRAM,      "The DBNOAUTOFREE option should not be turned on when using cursor APIs\0" }
    7986             :         , { SYBECRSNOIND,       EXPROGRAM,      "One of the tables involved in the cursor statement does not have a unique index\0" }
    7987             :         , { SYBECRSNOKEYS,     EXRESOURCE,      "The entire keyset must be defined for KEYSET type cursors\0" }
    7988             :         , { SYBECRSNOLEN,      EXRESOURCE,      "No unique index found\0" }
    7989             :         , { SYBECRSNOPTCC,     EXRESOURCE,      "No OPTCC was found\0" }
    7990             :         , { SYBECRSNORDER,     EXRESOURCE,      "The order of clauses must be from, where, and order by\0" }
    7991             :         , { SYBECRSNORES,       EXPROGRAM,      "Cursor statement generated no results\0" }
    7992             :         , { SYBECRSNROWS,      EXRESOURCE,      "No rows returned, at least one is expected\0" }
    7993             :         , { SYBECRSNOTABLE,    EXRESOURCE,      "Table name is NULL\0" }
    7994             :         , { SYBECRSNOUPD,       EXPROGRAM,      "Update or delete operation did not affect any rows\0" }
    7995             :         , { SYBECRSNOWHERE,     EXPROGRAM,      "A WHERE clause is not allowed in a cursor update or insert\0" }
    7996             :         , { SYBECRSNUNIQUE,    EXRESOURCE,      "No unique keys associated with this view\0" }
    7997             :         , { SYBECRSORD,         EXPROGRAM,      "Only fully keyset driven cursors can have order by, group by, or having phrases\0" }
    7998             :         , { SYBECRSRO,          EXPROGRAM,      "Data locking or modifications cannot be made in a READONLY cursor\0" }
    7999             :         , { SYBECRSSET,         EXPROGRAM,      "A SET clause is required for a cursor update or insert\0" }
    8000             :         , { SYBECRSTAB,         EXPROGRAM,      "Table name must be determined in operations involving data locking or modifications\0" }
    8001             :         , { SYBECRSVAR,        EXRESOURCE,      "There is no valid address associated with this bind\0" }
    8002             :         , { SYBECRSVIEW,        EXPROGRAM,      "A view cannot be joined with another table or a view in a cursor statement\0" }
    8003             :         , { SYBECRSVIIND,       EXPROGRAM,      "The view used in the cursor statement does not include all the unique index "
    8004             :                                                 "columns of the underlying tables\0" }
    8005             :         , { SYBECRSUPDNB,       EXPROGRAM,      "Update or insert operations cannot use bind variables when binding type is NOBIND\0" }
    8006             :         , { SYBECRSUPDTAB,      EXPROGRAM,      "Update or insert operations using bind variables require single table cursors\0" }
    8007             :         , { SYBECSYN,        EXCONVERSION,      "Attempt to convert data stopped by syntax error in source field\0" }
    8008             :         , { SYBECUFL,        EXCONVERSION,      "Data conversion resulted in underflow\0" }
    8009             :         , { SYBECWLL,           EXPROGRAM,      "Attempt to set column width less than 1\0" }
    8010             :         , { SYBEDBPS,          EXRESOURCE,      "Maximum number of DBPROCESSes already allocated\0" }
    8011             :         , { SYBEDDNE,              EXCOMM,      "DBPROCESS is dead or not enabled\0" }
    8012             :         , { SYBEDIVZ,              EXUSER,      "Attempt to divide by $0.00 in function %1!\0%s" }
    8013             :         , { SYBEDNTI,           EXPROGRAM,      "Attempt to use dbtxtsput to put a new text timestamp into a column whose datatype "
    8014             :                                                 "is neither SYBTEXT nor SYBIMAGE\0" }
    8015             :         , { SYBEDPOR,           EXPROGRAM,      "Out-of-range datepart constant\0" }
    8016             :         , { SYBEDVOR,           EXPROGRAM,      "Day values must be between 1 and 7\0" }
    8017             :         , { SYBEECAN,              EXINFO,      "Attempted to cancel unrequested event notification\0" }
    8018             :         , { SYBEEINI,              EXINFO,      "Must call dbreginit before dbregexec\0" }
    8019             :         , { SYBEETD,            EXPROGRAM,      "Failure to send the expected amount of TEXT or IMAGE data via dbmoretext\0" }
    8020             :         , { SYBEEUNR,              EXCOMM,      "Unsolicited event notification received\0" }
    8021             :         , { SYBEEVOP,              EXINFO,      "Called dbregwatch with a bad options parameter\0" }
    8022             :         , { SYBEEVST,              EXINFO,      "Must initiate a transaction before calling dbregparam\0" }
    8023             :         , { SYBEFCON,              EXCOMM,      "TDS server connection failed\0" }
    8024             :         , { SYBEFRES,             EXFATAL,      "Challenge-Response function failed\0" }
    8025             :         , { SYBEFSHD,          EXRESOURCE,      "Error in attempting to find the Sybase home directory\0" }
    8026             :         , { SYBEFUNC,           EXPROGRAM,      "Functionality not supported at the specified version level\0" }
    8027             :         , { SYBEICN,            EXPROGRAM,      "Invalid computeid or compute column number\0" }
    8028             :         , { SYBEIDCL,       EXCONSISTENCY,      "Illegal datetime column length returned by TDS server. Legal datetime lengths "
    8029             :                                                 "are 4 and 8 bytes\0" }
    8030             :         , { SYBEIDECCL,     EXCONSISTENCY,      "Invalid decimal column length returned by the server\0" }
    8031             :         , { SYBEIFCL,       EXCONSISTENCY,      "Illegal floating-point column length returned by TDS server. Legal "
    8032             :                                                 "floating-point lengths are 4 and 8 bytes\0" }
    8033             :         , { SYBEIFNB,           EXPROGRAM,      "Illegal field number passed to bcp_control\0" }
    8034             :         , { SYBEIICL,       EXCONSISTENCY,      "Illegal integer column length returned by TDS server. Legal integer lengths "
    8035             :                                                 "are 1, 2, and 4 bytes\0" }
    8036             :         , { SYBEIMCL,       EXCONSISTENCY,      "Illegal money column length returned by TDS server. Legal money lengths are 4 "
    8037             :                                                 "and 8 bytes\0" }
    8038             :         , { SYBEINLN,              EXUSER,      "Interface file: unexpected end-of-line\0" }
    8039             :         , { SYBEINTF,              EXUSER,      "Server name not found in configuration files\0" }
    8040             :         , { SYBEINUMCL,     EXCONSISTENCY,      "Invalid numeric column length returned by the server\0" }
    8041             :         , { SYBEIPV,               EXINFO,      "%1! is an illegal value for the %2! parameter of %3!\0%d %s %s" }
    8042             :         , { SYBEISOI,       EXCONSISTENCY,      "International Release: Invalid sort-order information found\0" }
    8043             :         , { SYBEISRVPREC,   EXCONSISTENCY,      "Illegal precision value returned by the server\0" }
    8044             :         , { SYBEISRVSCL,    EXCONSISTENCY,      "Illegal scale value returned by the server\0" }
    8045             :         , { SYBEITIM,           EXPROGRAM,      "Illegal timeout value specified\0" }
    8046             :         , { SYBEIVERS,          EXPROGRAM,      "Illegal version level specified\0" }
    8047             :         , { SYBEKBCI,              EXINFO,      "1000 rows sent to the server\0" }
    8048             :         , { SYBEKBCO,              EXINFO,      "1000 rows successfully bulk copied to host file\0" }
    8049             :         , { SYBEMEM,           EXRESOURCE,      "Unable to allocate sufficient memory\0" }
    8050             :         , { SYBEMOV,               EXUSER,      "Money arithmetic resulted in overflow in function %1!\0%s" }
    8051             :         , { SYBEMPLL,              EXUSER,      "Attempt to set maximum number of DBPROCESSes lower than 1\0" }
    8052             :         , { SYBEMVOR,           EXPROGRAM,      "Month values must be between 1 and 12\0" }
    8053             :         , { SYBENBUF,              EXINFO,      "Called dbsendpassthru with a NULL buf parameter\0" }
    8054             :         , { SYBENBVP,           EXPROGRAM,      "Cannot pass dbsetnull a NULL bindval pointer\0" }
    8055             :         , { SYBENDC,            EXPROGRAM,      "Cannot have negative component in date in numeric form\0" }
    8056             :         , { SYBENDTP,           EXPROGRAM,      "Called dbdatecrack with NULL datetime parameter\0" }
    8057             :         , { SYBENEG,               EXCOMM,      "Negotiated login attempt failed\0" }
    8058             :         , { SYBENHAN,              EXINFO,      "Called dbrecvpassthru with a NULL handle parameter\0" }
    8059             :         , { SYBENMOB,           EXPROGRAM,      "No such member of order by clause\0" }
    8060             :         , { SYBENOEV,              EXINFO,      "DBPOLL can not be called when registered procedure notifications have been disabled\0" }
    8061             :         , { SYBENPRM,           EXPROGRAM,      "NULL parameter not allowed for this dboption\0" }
    8062             :         , { SYBENSIP,           EXPROGRAM,      "Negative starting index passed to dbstrcpy\0" }
    8063             :         , { SYBENTLL,              EXUSER,      "Name too long for LOGINREC field\0" }
    8064             :         , { SYBENTTN,           EXPROGRAM,      "Attempt to use dbtxtsput to put a new text timestamp into a non-existent data row\0" }
    8065             :         , { SYBENULL,              EXINFO,      "NULL DBPROCESS pointer passed to DB-Library\0" }
    8066             :         , { SYBENULP,           EXPROGRAM,      "Called %1! with parameter %2! NULL\0%s %d" }
    8067             :         , { SYBENXID,          EXNONFATAL,      "The Server did not grant us a distributed-transaction ID\0" }
    8068             :         , { SYBEONCE,           EXPROGRAM,      "Function can be called only once\0" }
    8069             :         , { SYBEOOB,               EXCOMM,      "Error in sending out-of-band data to the server\0" }
    8070             :         , { SYBEOPIN,          EXNONFATAL,      "Could not open interface file\0" }
    8071             :         , { SYBEOPNA,          EXNONFATAL,      "Option is not available with current server\0" }
    8072             :         , { SYBEOREN,              EXINFO,      "International Release: Warning: an out-of-range error-number was encountered in "
    8073             :                                                 "dblib.loc. The maximum permissible error-number is defined as DBERRCOUNT in sybdb.h\0" }
    8074             :         , { SYBEORPF,              EXUSER,      "Attempt to set remote password would overflow "
    8075             :                                                 "the login record's remote password field\0" }
    8076             :         , { SYBEPOLL,              EXINFO,      "There is already an active dbpoll\0" }
    8077             :         , { SYBEPRTF,              EXINFO,      "dbtracestring may only be called from a printfunc\0" }
    8078             :         , { SYBEPWD,               EXUSER,      "Login incorrect\0" }
    8079             :         , { SYBERDCN,        EXCONVERSION,      "Requested data conversion does not exist\0" }
    8080             :         , { SYBERDNR,           EXPROGRAM,      "Attempt to retrieve data from a non-existent row\0" }
    8081             :         , { SYBEREAD,              EXCOMM,      "Read from the server failed\0" }
    8082             :         , { SYBERESP,           EXPROGRAM,      "Response function address passed to dbresponse must be non-NULL\0" }
    8083             :         , { SYBERPCS,              EXINFO,      "Must call dbrpcinit before dbrpcparam or dbrpcsend\0" }
    8084             :         , { SYBERPIL,           EXPROGRAM,      "It is illegal to pass -1 to dbrpcparam for the datalen of parameters which are of "
    8085             :                                                 "type SYBCHAR, SYBVARCHAR, SYBBINARY, or SYBVARBINARY\0" }
    8086             :         , { SYBERPNA,          EXNONFATAL,      "The RPC facility is available only when using a server whose version number is 4.0 "
    8087             :                                                 "or later\0" }
    8088             :         , { SYBERPND,           EXPROGRAM,      "Attempt to initiate a new TDS server operation with results pending\0" }
    8089             :         , { SYBERPNULL,         EXPROGRAM,      "value parameter for dbrpcparam can be NULL, only if the datalen parameter is 0\0" }
    8090             :         , { SYBERPTXTIM,        EXPROGRAM,      "RPC parameters cannot be of type text or image\0" }
    8091             :         , { SYBERPUL,           EXPROGRAM,      "When passing a SYBINTN, SYBDATETIMN, SYBMONEYN, or SYBFLTN parameter via "
    8092             :                                                 "dbrpcparam, it is necessary to specify the parameter's maximum or actual length so "
    8093             :                                                 "that DB-Library can recognize it as a SYINT1, SYBINT2, SYBINT4, SYBMONEY, SYBMONEY4, "
    8094             :                                                 "and so on\0" }
    8095             :         , { SYBERTCC,           EXPROGRAM,      "dbreadtext may not be used to receive the results of a query that contains a "
    8096             :                                                 "COMPUTE clause\0" }
    8097             :         , { SYBERTSC,           EXPROGRAM,      "dbreadtext may be used only to receive the results of a query that contains a "
    8098             :                                                 "single result column\0" }
    8099             :         , { SYBERXID,          EXNONFATAL,      "The Server did not recognize our distributed-transaction ID\0" }
    8100             :         , { SYBESECURE,         EXPROGRAM,      "Secure SQL Server function not supported in this version\0" }
    8101             :         , { SYBESEFA,           EXPROGRAM,      "DBSETNOTIFS cannot be called if connections are present\0" }
    8102             :         , { SYBESEOF,              EXCOMM,      "Unexpected EOF from the server\0" }
    8103             :         , { SYBESFOV,           EXPROGRAM,      "International Release: dbsafestr overflowed its destination buffer\0" }
    8104             :         , { SYBESMSG,            EXSERVER,      "General TDS server error: Check messages from the server\0" }
    8105             :         , { SYBESOCK,              EXCOMM,      "Unable to open socket\0" }
    8106             :         , { SYBESPID,           EXPROGRAM,      "Called dbspid with a NULL dbproc\0" }
    8107             :         , { SYBESYNC,              EXCOMM,      "Read attempted while out of synchronization with TDS server\0" }
    8108             :         , { SYBETEXS,              EXINFO,      "Called dbmoretext with a bad size parameter\0" }
    8109             :         , { SYBETIME,              EXTIME,      "TDS server connection timed out\0" }
    8110             :         , { SYBETMCF,           EXPROGRAM,      "Attempt to install too many custom formats via dbfmtinstall\0" }
    8111             :         , { SYBETMTD,           EXPROGRAM,      "Attempt to send too much TEXT data via the dbmoretext call\0" }
    8112             :         , { SYBETPAR,           EXPROGRAM,      "No SYBTEXT or SYBIMAGE parameters were defined\0" }
    8113             :         , { SYBETPTN,              EXUSER,      "Syntax error: only two periods are permitted in table names\0" }
    8114             :         , { SYBETRAC,              EXINFO,      "Attempted to turn off a trace flag that was not on\0" }
    8115             :         , { SYBETRAN,              EXINFO,      "DBPROCESS is being used for another transaction\0" }
    8116             :         , { SYBETRAS,              EXINFO,      "DB-Library internal error - trace structure not found\0" }
    8117             :         , { SYBETRSN,              EXINFO,      "Bad numbytes parameter passed to dbtracestring\0" }
    8118             :         , { SYBETSIT,              EXINFO,      "Attempt to call dbtsput with an invalid timestamp\0" }
    8119             :         , { SYBETTS,               EXUSER,      "The table which bulk copy is attempting to copy to a host file is shorter than the "
    8120             :                                                 "number of rows which bulk copy was instructed to skip\0" }
    8121             :         , { SYBETYPE,              EXINFO,      "Invalid argument type given to Hyper/DB-Library\0" }
    8122             :         , { SYBEUCPT,              EXUSER,      "Unrecognized custom-format parameter-type encountered in dbstrbuild\0" }
    8123             :         , { SYBEUCRR,       EXCONSISTENCY,      "Internal software error: Unknown connection result reported by dbpasswd\0" }
    8124             :         , { SYBEUDTY,       EXCONSISTENCY,      "Unknown datatype encountered\0" }
    8125             :         , { SYBEUFDS,              EXUSER,      "Unrecognized format encountered in dbstrbuild\0" }
    8126             :         , { SYBEUFDT,       EXCONSISTENCY,      "Unknown fixed-length datatype encountered\0" }
    8127             :         , { SYBEUHST,              EXUSER,      "Unknown host machine name\0" }
    8128             :         , { SYBEUMSG,              EXCOMM,      "Unknown message-id in MSG datastream\0" }
    8129             :         , { SYBEUNAM,             EXFATAL,      "Unable to get current user name from operating system\0" }
    8130             :         , { SYBEUNOP,          EXNONFATAL,      "Unknown option passed to dbsetopt\0" }
    8131             :         , { SYBEUNT,               EXUSER,      "Unknown network type found in interface file\0" }
    8132             :         , { SYBEURCI,          EXRESOURCE,      "International Release: Unable to read copyright information from the DB-Library "
    8133             :                                                 "localization file\0" }
    8134             :         , { SYBEUREI,          EXRESOURCE,      "International Release: Unable to read error information from the DB-Library "
    8135             :                                                 "localization file\0" }
    8136             :         , { SYBEUREM,          EXRESOURCE,      "International Release: Unable to read error mnemonic from the DB-Library "
    8137             :                                                 "localization file\0" }
    8138             :         , { SYBEURES,          EXRESOURCE,      "International Release: Unable to read error string from the DB-Library "
    8139             :                                                 "localization file. 401 Error severities\0" }
    8140             :         , { SYBEURMI,          EXRESOURCE,      "International Release: Unable to read money-format information from the DB-Library "
    8141             :                                                 "localization file\0" }
    8142             :         , { SYBEUSCT,              EXCOMM,      "Unable to set communications timer\0" }
    8143             :         , { SYBEUTDS,              EXCOMM,      "Unrecognized TDS version received from the server\0" }
    8144             :         , { SYBEUVBF,           EXPROGRAM,      "Attempt to read an unknown version of bcp format file\0" }
    8145             :         , { SYBEUVDT,       EXCONSISTENCY,      "Unknown variable-length datatype encountered\0" }
    8146             :         , { SYBEVDPT,              EXUSER,      "For bulk copy, all variable-length data must have either a length-prefix or a "
    8147             :                                                 "terminator specified\0" }
    8148             :         , { SYBEWAID,       EXCONSISTENCY,      "DB-Library internal error: ALTFMT following ALTNAME has wrong id\0" }
    8149             :         , { SYBEWRIT,              EXCOMM,      "Write to the server failed\0" }
    8150             :         , { SYBEXOCI,          EXNONFATAL,      "International Release: A character-set translation overflowed its destination "
    8151             :                                                 "buffer while using bcp to copy data from a host-file to the server\0" }
    8152             :         , { SYBEXTDN,           EXPROGRAM,      "Warning: the xlt_todisp parameter to dbfree_xlate was NULL. The space associated "
    8153             :                                                 "with the xlt_tosrv parameter has been freed\0" }
    8154             :         , { SYBEXTN,            EXPROGRAM,      "The xlt_tosrv and xlt_todisp parameters to dbfree_xlate were NULL\0" }
    8155             :         , { SYBEXTSN,           EXPROGRAM,      "Warning: the xlt_tosrv parameter to dbfree_xlate was NULL. The space associated "
    8156             :                                                 "with the xlt_todisp parameter has been freed\0" }
    8157             :         , { SYBEZTXT,              EXINFO,      "Attempt to send zero length TEXT or IMAGE to dataserver via dbwritetext\0" }
    8158             :         , { SYBECOLSIZE,           EXINFO,      "Invalid column information structure size\0" }
    8159             :         };
    8160             : 
    8161             : /**  \internal
    8162             :  * \ingroup dblib_internal
    8163             :  * \brief Call client-installed error handler
    8164             :  * 
    8165             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    8166             :  * \param msgno identifies the error message to be passed to the client's handler.
    8167             :  * \param errnum identifies the OS error (errno), if any.  Use 0 if not applicable.  
    8168             :  * \returns the handler's return code, subject to correction and adjustment for vendor style:
    8169             :  *      - INT_CANCEL    The db-lib function that encountered the error will return FAIL.  
    8170             :  *      - INT_TIMEOUT   The db-lib function will cancel the operation and return FAIL.  \a dbproc remains useable.  
    8171             :  *      - INT_CONTINUE  The db-lib function will retry the operation.  
    8172             :  * \remarks 
    8173             :  *      The client-installed handler may also return INT_EXIT.  If Sybase semantics are used, this function notifies
    8174             :  *      the user and calls exit(3).  If Microsoft semantics are used, this function returns INT_CANCEL.  
    8175             :  *
    8176             :  *      If the client-installed handler returns something other than these four INT_* values, or returns timeout-related
    8177             :  *      value for anything but SYBETIME, it's treated here as INT_EXIT (see above).  
    8178             :  *
    8179             :  * Instead of sprinkling error text all over db-lib, we consolidate it here, 
    8180             :  * where it can be translated (one day), and where it can be mapped to the TDS error number.  
    8181             :  * The libraries don't use consistent error numbers or messages, so when libtds has to emit 
    8182             :  * an error message, it can't include the text.  It can pass its error number to a client-library
    8183             :  * function, which will interpret it, add the text, call the application's installed handler
    8184             :  * (if any) and return the handler's return code back to the caller.  
    8185             :  * 
    8186             :  * The call stack may look something like this:
    8187             :  *
    8188             :  * -#   application
    8189             :  * -#           db-lib function (encounters error)
    8190             :  * -#           dbperror
    8191             :  * -#   error handler (installed by application)
    8192             :  *
    8193             :  * The error handling in this case is unambiguous: the caller invokes this function, the client's handler returns its 
    8194             :  * instruction, which the caller receives.  Quite often the caller will get INT_CANCEL, in which case it should put its 
    8195             :  * house in order and return FAIL.  
    8196             :  *
    8197             :  * The call stack may otherwise look something like this:
    8198             :  *                      
    8199             :  * -#   application
    8200             :  * -#           db-lib function
    8201             :  * -#                   libtds function (encounters error)
    8202             :  * -#           _dblib_handle_err_message
    8203             :  * -#           dbperror
    8204             :  * -#   error handler (installed by application)
    8205             :  *
    8206             :  * Because different client libraries specify their handler semantics differently, 
    8207             :  * and because libtds doesn't know which client library is in charge of any given connection, it cannot interpret the 
    8208             :  * raw return code from a db-lib error handler.  For these reasons, 
    8209             :  * libtds calls _dblib_handle_err_message, which translates between libtds and db-lib semantics.  
    8210             :  * \sa dberrhandle(), _dblib_handle_err_message().
    8211             :  */
    8212             : int
    8213         260 : dbperror(DBPROCESS *dbproc, DBINT msgno, long errnum, ...)
    8214             : {
    8215             :         static const char int_exit_text[] = "FreeTDS: db-lib: exiting because client error handler returned %s for msgno %d\n";
    8216             :         static const char int_invalid_text[] = "%s (%d) received from client-installed error handler for nontimeout for error %d."
    8217             :                                                "  Treating as INT_EXIT\n";
    8218             :         static const DBLIB_ERROR_MESSAGE default_message = { 0, EXCONSISTENCY, "unrecognized msgno" };
    8219         260 :         DBLIB_ERROR_MESSAGE constructed_message = { 0, EXCONSISTENCY, NULL };
    8220         260 :         const DBLIB_ERROR_MESSAGE *msg = &default_message;
    8221             :         
    8222         260 :         int i, rc = INT_CANCEL;
    8223         260 :         const char *os_msgtext = strerror((int) errnum);
    8224         260 :         const char *rc_name = "logic error";
    8225             :         char rc_buf[16];
    8226             : 
    8227         260 :         tdsdump_log(TDS_DBG_FUNC, "dbperror(%p, %d, %ld)\n", dbproc, msgno, errnum);  /* dbproc can be NULL */
    8228             : 
    8229             : #ifdef _WIN32
    8230             :         /*
    8231             :          * Unfortunately MinGW uses the "old" msvcrt.dll (Visual C++ 2005 uses
    8232             :          * a newer version) which does not set errno when allocation functions
    8233             :          * cannot allocate memory, so we do it for them.
    8234             :          */
    8235             :         if (msgno == SYBEMEM)
    8236             :                 errnum = ENOMEM;
    8237             : #endif
    8238             : 
    8239         260 :         if (os_msgtext == NULL)
    8240           0 :                 os_msgtext = "no OS error";
    8241             :         
    8242         260 :         assert(_dblib_err_handler != NULL);     /* always installed by dbinit() or dberrhandle() */
    8243             : 
    8244             :         /* look up the error message */
    8245       89840 :         for (i = 0; i < TDS_VECTOR_SIZE(dblib_error_messages); i++) {
    8246             :                 const char *ptext, *pformats;
    8247             : 
    8248       45050 :                 if (dblib_error_messages[i].msgno != msgno)
    8249       44790 :                         continue;
    8250             : 
    8251             :                 /*
    8252             :                  * See if the message has placeholders.  If so, build a message string on the heap.
    8253             :                  * The presence of placeholders is indicated by the existence of a "string after the string",
    8254             :                  * i.e., a format string (for dbstrbuild) after a null "terminator" in the message.
    8255             :                  * On error -- can't allocate, can't build the string -- give up and call the client handler anyway.
    8256             :                  */
    8257         260 :                 ptext = dblib_error_messages[i].msgtext;
    8258         260 :                 pformats = ptext + strlen(ptext) + 1;
    8259         260 :                 msg = &dblib_error_messages[i];
    8260         260 :                 assert(*(pformats - 1) == '\0');
    8261         260 :                 if (*pformats != '\0') {
    8262             :                         va_list ap;
    8263          10 :                         int result_len, len = 2 * (int) strlen(ptext);
    8264          10 :                         char *buffer = tds_new0(char, len);
    8265             : 
    8266          10 :                         if (buffer == NULL)
    8267             :                                 break;
    8268          10 :                         va_start(ap, errnum);
    8269          10 :                         rc = tds_vstrbuild(buffer, len, &result_len, ptext, TDS_NULLTERM, pformats, TDS_NULLTERM, ap);
    8270          10 :                         buffer[result_len] = '\0';
    8271          10 :                         va_end(ap);
    8272          10 :                         if (TDS_FAILED(rc)) {
    8273           0 :                                 free(buffer);
    8274           0 :                                 break;
    8275             :                         }
    8276          10 :                         constructed_message.msgtext = buffer;
    8277          10 :                         constructed_message.severity = msg->severity;
    8278          10 :                         msg = &constructed_message;
    8279             :                 }
    8280             :                 break;
    8281             :         }
    8282             : 
    8283         260 :         if (dbproc && dbproc->tds_socket && dbproc->tds_socket->login) {
    8284           0 :                 DSTR server_name_dstr = dbproc->tds_socket->login->server_name;
    8285           0 :                 if (!tds_dstr_isempty(&server_name_dstr)) {
    8286           0 :                         char * buffer = NULL;
    8287           0 :                         if (asprintf(&buffer, "%s (%s)", msg->msgtext,
    8288             :                                      tds_dstr_cstr(&server_name_dstr)) >= 0) {
    8289           0 :                                 free((char*) constructed_message.msgtext);
    8290           0 :                                 constructed_message.msgtext = buffer;
    8291           0 :                                 constructed_message.severity = msg->severity;
    8292           0 :                                 msg = &constructed_message;
    8293             :                         }
    8294             :                 }
    8295             :         }
    8296             : 
    8297         260 :         tdsdump_log(TDS_DBG_FUNC, "dbperror: Calling dblib_err_handler with msgno = %d; msg->msgtext = \"%s\"\n",
    8298             :                 msgno, msg->msgtext);
    8299             : 
    8300             :         /* call the error handler */
    8301         260 :         rc = (*_dblib_err_handler)(dbproc, msg->severity, msgno, (int) errnum, (char*) msg->msgtext, (char*) os_msgtext);
    8302         260 :         switch (rc) {
    8303             :         case INT_EXIT:
    8304             :                 rc_name = "INT_EXIT"; 
    8305             :                 break;
    8306          40 :         case INT_CONTINUE:      
    8307          40 :                 rc_name = "INT_CONTINUE";
    8308          40 :                 break;
    8309         200 :         case INT_CANCEL:
    8310         200 :                 rc_name = "INT_CANCEL";
    8311         200 :                 break;
    8312          20 :         case INT_TIMEOUT:
    8313          20 :                 rc_name = "INT_TIMEOUT";
    8314          20 :                 break;
    8315           0 :         default:
    8316           0 :                 rc_name = "invalid";
    8317           0 :                 break;
    8318             :         }
    8319         260 :         tdsdump_log(TDS_DBG_FUNC, "dbperror: dblib_err_handler for msgno = %d; msg->msgtext = \"%s\" -- returns %d (%s)\n",
    8320             :                 msgno, msg->msgtext, rc, rc_name);
    8321             : 
    8322             :         /* we're done with the dynamic string now. */
    8323         260 :         free((char*) constructed_message.msgtext);
    8324             :         
    8325             :         /* Timeout return codes are errors for non-timeout conditions. */
    8326         260 :         if (msgno != SYBETIME) {
    8327         190 :                 switch (rc) {
    8328           0 :                 case INT_CONTINUE:
    8329           0 :                         tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "INT_CONTINUE", rc, msgno);
    8330             :                         rc = INT_EXIT;
    8331             :                         break;
    8332           0 :                 case INT_TIMEOUT:
    8333           0 :                         tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "INT_TIMEOUT", rc, msgno);
    8334             :                         rc = INT_EXIT;
    8335             :                         break;
    8336             :                 default:
    8337             :                         break;
    8338             :                 }
    8339          70 :         }
    8340             : 
    8341             :         /* 
    8342             :          * Sybase exits on INT_EXIT; Microsoft converts to INT_CANCEL.
    8343             :          * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dblibc/dbc_pdc04c_6v39.asp
    8344             :          */
    8345         260 :         switch (rc) {
    8346          40 :         case INT_CONTINUE:
    8347             :                 /* Microsoft does not define INT_TIMEOUT.  Instead, two consecutive INT_CONTINUEs yield INT_CANCEL. */
    8348          40 :                 if (dbproc && dbproc->msdblib && ++dbproc->ntimeouts >=2) {
    8349           0 :                         dbproc->ntimeouts = 0;
    8350           0 :                         rc = INT_CANCEL;
    8351             :                 }       /* fall through */
    8352             :         case INT_CANCEL:
    8353             :         case INT_TIMEOUT:
    8354             :                 return rc;      /* normal case */
    8355             :                 break;
    8356           0 :         default:
    8357           0 :                 sprintf(rc_buf, "%d", rc);
    8358           0 :                 rc_name = rc_buf;
    8359           0 :                 tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "Invalid return code", rc, msgno);
    8360             :                 /* fall through */
    8361             :         case INT_EXIT:
    8362           0 :                 if (dbproc && dbproc->msdblib) {
    8363             :                         /* Microsoft behavior */
    8364             :                         return INT_CANCEL;
    8365             :                 }
    8366             :                 /* fprintf(stderr, int_exit_text, rc_name, msgno); */
    8367           0 :                 tdsdump_log(TDS_DBG_SEVERE, int_exit_text, rc_name, msgno);
    8368             :                 break;
    8369             :         }
    8370           0 :         exit(EXIT_FAILURE);
    8371             :         return rc; /* not reached */
    8372             : }
    8373             : 

Generated by: LCOV version 1.13