LCOV - code coverage report
Current view: top level - src/dblib - dblib.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 1389 2596 53.5 %
Date: 2025-02-21 09:36:06 Functions: 97 161 60.2 %

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

Generated by: LCOV version 1.13