LCOV - code coverage report
Current view: top level - src/dblib - dblib.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 1391 2599 53.5 %
Date: 2025-04-15 09:57:00 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, (int) 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, ptrdiff_t 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      220486 : dbnextrow(DBPROCESS * dbproc)
    2090             : {
    2091             :         TDSRESULTINFO *resinfo;
    2092             :         TDSSOCKET *tds;
    2093      220486 :         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      220486 :         tdsdump_log(TDS_DBG_FUNC, "dbnextrow(%p)\n", dbproc);
    2100      220486 :         CHECK_CONN(FAIL);
    2101             : 
    2102      220486 :         tds = dbproc->tds_socket;
    2103      220486 :         resinfo = tds->res_info;
    2104             : 
    2105      220486 :         tdsdump_log(TDS_DBG_FUNC, "dbnextrow() dbresults_state = %d (%s)\n", 
    2106           0 :                                         dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
    2107             : 
    2108      220486 :         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      219726 :         dbproc->row_type = NO_MORE_ROWS; 
    2120      219726 :         computeid = REG_ROW;
    2121      219726 :         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      219716 :         } else if (buffer_is_full(&dbproc->row_buf)) {
    2129             :                 
    2130         120 :                 result = BUF_FULL;
    2131         120 :                 res_type = TDS_ROWFMT_RESULT;
    2132             :         
    2133      219596 :         } 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      219596 :                 const int mask = TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE;
    2140      219596 :                 TDS_INT8 row_count = TDS_NO_COUNT;
    2141      219596 :                 bool rows_set = false;
    2142      219596 :                 buffer_save_row(dbproc);
    2143             : 
    2144             :                 /* Get the row from the TDS stream.  */
    2145         718 : again:
    2146      220314 :                 switch (tds_process_tokens(tds, &res_type, NULL, mask)) {
    2147      220284 :                 case TDS_SUCCESS:
    2148      220284 :                         if (res_type == TDS_ROW_RESULT || res_type == TDS_COMPUTE_RESULT) {
    2149      207696 :                                 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      207696 :                                 resinfo = tds->current_results;
    2153      207696 :                                 idx = buffer_add_row(dbproc, resinfo);
    2154      207696 :                                 assert(idx != -1);
    2155      207696 :                                 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      207696 :                                 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      219596 :                 if (rows_set)
    2179         462 :                         tds->rows_affected = row_count;
    2180             :         }
    2181             : 
    2182      219726 :         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      207706 :                 buffer_transfer_bound_data(&dbproc->row_buf, res_type, computeid, dbproc, idx);
    2187             :         }
    2188             :         
    2189      219726 :         if (res_type == TDS_COMPUTE_RESULT) {
    2190          32 :                 tdsdump_log(TDS_DBG_FUNC, "leaving dbnextrow() returning compute_id %d\n", result);
    2191             :         } else {
    2192      219694 :                 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 = TDS_MAX(colinfo->column_cur_size, 0);
    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 = (int) tds_dstr_len(&colinfo->column_name);
    3452             : 
    3453           0 :                 len += TDS_MAX(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             :                 size_t 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 = TDS_MAX(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;
    3564             :         size_t collen, namlen, len;
    3565             :         char dest[8192];
    3566             :         int desttype, srctype;
    3567             :         TDSDATEREC when;
    3568             :         STATUS status;
    3569             :         ptrdiff_t padlen;
    3570             :         int c;
    3571             :         int selcol;
    3572             :         int linechar;
    3573             :         int op;
    3574             :         const char *opname, *p;
    3575             : 
    3576             :         /* these are for compute rows */
    3577             :         DBINT computeid, num_cols, colid;
    3578          60 :         TDS_SMALLINT *col_printlens = NULL;
    3579             : 
    3580          60 :         tdsdump_log(TDS_DBG_FUNC, "dbprrow(%p)\n", dbproc);
    3581          60 :         CHECK_CONN(FAIL);
    3582             : 
    3583             :         tds = dbproc->tds_socket;
    3584             : 
    3585         410 :         while ((status = dbnextrow(dbproc)) != NO_MORE_ROWS) {
    3586             : 
    3587         350 :                 if (status == FAIL) {
    3588           0 :                         free(col_printlens);
    3589           0 :                         return FAIL;
    3590             :                 }
    3591             : 
    3592         350 :                 if (status == REG_ROW) {
    3593             : 
    3594         350 :                         resinfo = tds->res_info;
    3595             : 
    3596         350 :                         if (col_printlens == NULL) {
    3597          20 :                                 if ((col_printlens = tds_new0(TDS_SMALLINT, resinfo->num_cols)) == NULL) {
    3598           0 :                                         dbperror(dbproc, SYBEMEM, errno);
    3599           0 :                                         return FAIL;
    3600             :                                 }
    3601             :                         }
    3602             : 
    3603        1750 :                         for (col = 0; col < resinfo->num_cols; col++) {
    3604        1400 :                                 colinfo = resinfo->columns[col];
    3605        1400 :                                 if (colinfo->column_cur_size < 0) {
    3606          60 :                                         len = 4;
    3607          60 :                                         strcpy(dest, "NULL");
    3608             :                                 } else {
    3609        1340 :                                         desttype = dblib_bound_type(STRINGBIND);
    3610        1340 :                                         srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    3611        1340 :                                         if (is_datetime_type(srctype)) {
    3612           0 :                                                 tds_datecrack(srctype, dbdata(dbproc, col + 1), &when);
    3613           0 :                                                 len = (int)tds_strftime(dest, sizeof(dest), STD_DATETIME_FMT, &when, 3);
    3614             :                                         } else {
    3615        1340 :                                                 len = dbconvert(dbproc, srctype, dbdata(dbproc, col + 1), dbdatlen(dbproc, col + 1),
    3616             :                                                                 desttype, (BYTE *) dest, sizeof(dest));
    3617             :                                         }
    3618             :                                 }
    3619             : 
    3620        1400 :                                 p = memchr(dest, '\0', len);
    3621        1400 :                                 fwrite(dest, 1, p == NULL ? len : (p - dest), stdout);
    3622        1400 :                                 collen = _get_printable_size(colinfo);
    3623        2800 :                                 namlen = tds_dstr_len(&colinfo->column_name);
    3624        1400 :                                 padlen = TDS_MAX(collen, namlen) - len;
    3625             : 
    3626        2800 :                                 c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0);
    3627       10618 :                                 for (; c > -1 && padlen > 0; padlen--) {
    3628        9218 :                                         putchar(c);
    3629             :                                 }
    3630             : 
    3631        1400 :                                 if ((col + 1) < resinfo->num_cols) {
    3632             :                                         i = 0;
    3633        4200 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3634             :                                                 putchar(c);
    3635             :                                         }
    3636             :                                 }
    3637        1400 :                                 col_printlens[col] = collen;
    3638             :                         }
    3639             :                         i = 0;
    3640        1400 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3641             :                                 putchar(c);
    3642             :                         }
    3643             : 
    3644           0 :                 } else if (col_printlens == NULL) {
    3645             :                         return FAIL;
    3646             :                 } else {
    3647             : 
    3648             :                         computeid = status;
    3649             : 
    3650           0 :                         for (i = 0;; ++i) {
    3651           0 :                                 if ((TDS_UINT) i >= tds->num_comp_info) {
    3652           0 :                                         free(col_printlens);
    3653           0 :                                         return FAIL;
    3654             :                                 }
    3655           0 :                                 resinfo = tds->comp_info[i];
    3656           0 :                                 if (resinfo->computeid == computeid)
    3657             :                                         break;
    3658             :                         }
    3659             : 
    3660           0 :                         num_cols = dbnumalts(dbproc, computeid);
    3661           0 :                         tdsdump_log(TDS_DBG_FUNC, "dbprrow num compute cols = %d\n", num_cols);
    3662             : 
    3663             :                         i = 0;
    3664           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3665             :                                 putchar(c);
    3666             :                         }
    3667           0 :                         for (selcol = col = 1; col <= num_cols; col++) {
    3668           0 :                                 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
    3669           0 :                                 colid = dbaltcolid(dbproc, computeid, col);
    3670             :                                 /*
    3671             :                                  * The pad character is pointed to by dbopts[DBPRPAD].param.  If that pointer 
    3672             :                                  * is NULL -- meaning padding is turned off -- dbstring_getchar returns -1. 
    3673             :                                  */
    3674           0 :                                 while (selcol < colid) {
    3675           0 :                                         for (i = 0; i < col_printlens[selcol - 1]; i++) {
    3676           0 :                                                 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) >= 0)
    3677             :                                                         putchar(c); 
    3678             :                                         }
    3679           0 :                                         selcol++;
    3680           0 :                                         i = 0;
    3681           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3682             :                                                 putchar(c);
    3683             :                                         }
    3684             :                                 }
    3685           0 :                                 op = dbaltop(dbproc, computeid, col);
    3686           0 :                                 opname = dbprtype(op);
    3687           0 :                                 printf("%s", opname);
    3688           0 :                                 for (i = 0; i < (col_printlens[selcol - 1] - (int) strlen(opname)); i++) {
    3689           0 :                                         if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) >= 0)
    3690             :                                                 putchar(c); 
    3691             :                                 }
    3692           0 :                                 selcol++;
    3693           0 :                                 if ((colid + 1) < num_cols) {
    3694             :                                         i = 0;
    3695           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3696             :                                                 putchar(c);
    3697             :                                         }
    3698             :                                 }
    3699             :                         }
    3700             :                         i = 0;
    3701           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3702             :                                 putchar(c);
    3703             :                         }
    3704             : 
    3705           0 :                         for (selcol = col = 1; col <= num_cols; col++) {
    3706           0 :                                 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
    3707           0 :                                 colid = dbaltcolid(dbproc, computeid, col);
    3708           0 :                                 while (selcol < colid) {
    3709           0 :                                         for (i = 0; i < col_printlens[selcol - 1]; i++) {
    3710           0 :                                                 putchar(' ');
    3711             :                                         }
    3712           0 :                                         selcol++;
    3713           0 :                                         i = 0;
    3714           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3715             :                                                 putchar(c);
    3716             :                                         }
    3717             :                                 }
    3718           0 :                                 if (resinfo->by_cols > 0) {
    3719             :                                         linechar = '-';
    3720             :                                 } else {
    3721           0 :                                         linechar = '=';
    3722             :                                 }
    3723           0 :                                 for (i = 0; i < col_printlens[colid - 1]; i++)
    3724           0 :                                         putchar(linechar);
    3725           0 :                                 selcol++;
    3726           0 :                                 if ((colid + 1) < num_cols) {
    3727             :                                         i = 0;
    3728           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3729             :                                                 putchar(c);
    3730             :                                         }
    3731             :                                 }
    3732             :                         }
    3733             :                         i = 0;
    3734           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3735             :                                 putchar(c);
    3736             :                         }
    3737             : 
    3738           0 :                         for (selcol = col = 1; col <= num_cols; col++) {
    3739           0 :                                 colinfo = resinfo->columns[col - 1];
    3740             : 
    3741           0 :                                 desttype = dblib_bound_type(STRINGBIND);
    3742           0 :                                 srctype = dbalttype(dbproc, computeid, col);
    3743             : 
    3744           0 :                                 if (is_datetime_type(srctype)) {
    3745           0 :                                         tds_datecrack(srctype, dbadata(dbproc, computeid, col), &when);
    3746           0 :                                         len = (int)tds_strftime(dest, sizeof(dest), STD_DATETIME_FMT, &when, 3);
    3747             :                                 } else {
    3748           0 :                                         len = dbconvert(dbproc, srctype, dbadata(dbproc, computeid, col), -1, desttype,
    3749             :                                                         (BYTE *) dest, sizeof(dest));
    3750             :                                 }
    3751             : 
    3752           0 :                                 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
    3753           0 :                                 colid = dbaltcolid(dbproc, computeid, col);
    3754           0 :                                 tdsdump_log(TDS_DBG_FUNC, "dbprrow select column = %d\n", colid);
    3755             : 
    3756           0 :                                 while (selcol < colid) {
    3757           0 :                                         for (i = 0; i < col_printlens[selcol - 1]; i++) {
    3758           0 :                                                 putchar(' ');
    3759             :                                         }
    3760           0 :                                         selcol++;
    3761           0 :                                         i = 0;
    3762           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3763             :                                                 putchar(c);
    3764             :                                         }
    3765             :                                 }
    3766           0 :                                 p = memchr(dest, '\0', len);
    3767           0 :                                 fwrite(dest, 1, p == NULL ? len : (p - dest), stdout);
    3768           0 :                                 collen = _get_printable_size(colinfo);
    3769           0 :                                 namlen = tds_dstr_len(&colinfo->column_name);
    3770           0 :                                 padlen = TDS_MAX(collen, namlen) - len;
    3771           0 :                                 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) == -1) {
    3772           0 :                                         c = ' ';
    3773             :                                 }
    3774           0 :                                 for (; padlen > 0; padlen--) {
    3775           0 :                                         putchar(c);
    3776             :                                 }
    3777           0 :                                 selcol++;
    3778           0 :                                 if ((colid + 1) < num_cols) {
    3779             :                                         i = 0;
    3780           0 :                                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
    3781             :                                                 putchar(c);
    3782             :                                         }
    3783             :                                 }
    3784             :                         }
    3785             :                         i = 0;
    3786           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
    3787             :                                 putchar(c);
    3788             :                         }
    3789             :                 }
    3790             :         }
    3791             : 
    3792          60 :         free(col_printlens);
    3793             : 
    3794          60 :         return SUCCEED;
    3795             : }
    3796             : 
    3797             : static int
    3798        1560 : _get_printable_size(TDSCOLUMN * colinfo)
    3799             : {
    3800        1560 :         switch (tds_get_conversion_type(colinfo->column_type, colinfo->column_size)) {
    3801             :         case SYBUINT1:
    3802             :         case SYBINT1:
    3803             :                 return 3;
    3804           0 :         case SYBSINT1:
    3805           0 :                 return 4;
    3806           0 :         case SYBUINT2:
    3807           0 :                 return 5;
    3808         522 :         case SYBINT2:
    3809         522 :                 return 6;
    3810           0 :         case SYBUINT4:
    3811           0 :                 return 10;
    3812         258 :         case SYBINT4:
    3813         258 :                 return 11;
    3814           0 :         case SYBUINT8:
    3815           0 :                 return 20;
    3816           0 :         case SYBINT8:
    3817           0 :                 return 21;
    3818         780 :         case SYBVARCHAR:
    3819             :         case SYBCHAR:
    3820             :         case SYBTEXT:
    3821             :         case SYBNTEXT:
    3822             :         case SYBUNITEXT:
    3823             :         case SYBNVARCHAR:
    3824             :         case SYBLONGCHAR:
    3825         780 :                 return colinfo->column_size;
    3826           0 :         case SYBBINARY:
    3827             :         case SYBIMAGE:
    3828             :         case SYBLONGBINARY:
    3829             :         case SYBVARBINARY:
    3830           0 :                 return colinfo->column_size * 2u;
    3831           0 :         case SYBFLT8:
    3832             :         case SYBREAL:
    3833           0 :                 return 11;      /* FIX ME -- we do not track precision */
    3834           0 :         case SYBMONEY4:
    3835           0 :                 return 12;
    3836           0 :         case SYBMONEY:
    3837           0 :                 return 22;
    3838           0 :         case SYB5BIGDATETIME:
    3839             :         case SYBDATETIME:
    3840             :         case SYBDATETIME4:
    3841           0 :                 return 26;
    3842           0 :         case SYBTIME:
    3843             :         case SYB5BIGTIME:
    3844           0 :                 return 15;
    3845           0 :         case SYBMSTIME:
    3846           0 :                 return 16;
    3847           0 :         case SYBDATE:
    3848             :         case SYBMSDATE:
    3849           0 :                 return 10;
    3850           0 :         case SYBMSDATETIME2:
    3851           0 :                 return 27;
    3852           0 :         case SYBMSDATETIMEOFFSET:
    3853           0 :                 return 33;
    3854           0 :         case SYBUNIQUE:
    3855           0 :                 return 36;
    3856           0 :         case SYBBIT:
    3857           0 :                 return 1;
    3858           0 :         case SYBNUMERIC:
    3859             :         case SYBDECIMAL:
    3860           0 :                 return colinfo->column_prec + 2;
    3861             :                 /* FIX ME -- not all types present */
    3862           0 :         case SYBVOID: /* or 4 bytes for "null" ? */
    3863             :         default:
    3864           0 :                 return 0;
    3865             :         }
    3866             : }
    3867             : 
    3868             : /**
    3869             :  * \ingroup dblib_core
    3870             :  * \brief Get formatted string for underlining dbsprhead() column names.  
    3871             :  * 
    3872             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3873             :  * \param buffer output buffer
    3874             :  * \param buf_len size of \a buffer
    3875             :  * \param line_char character to use to represent underlining.
    3876             :  * \retval SUCCEED \a buffer filled.
    3877             :  * \retval FAIL insufficient space in \a buffer, usually.
    3878             :  * \sa dbprhead(), dbprrow(), dbspr1row(), dbspr1rowlen(), dbsprhead(). 
    3879             :  */
    3880             : RETCODE
    3881           0 : dbsprline(DBPROCESS * dbproc, char *buffer, DBINT buf_len, DBCHAR line_char)
    3882             : {
    3883             :         TDSCOLUMN *colinfo;
    3884             :         TDSRESULTINFO *resinfo;
    3885             :         TDSSOCKET *tds;
    3886             :         size_t i, col, len, collen, namlen;
    3887             :         int c;
    3888             : 
    3889           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsprline(%p, %s, %d, '%c')\n", dbproc, buffer, buf_len, line_char);
    3890           0 :         CHECK_CONN(FAIL);
    3891           0 :         CHECK_NULP(buffer, "dbsprline", 2, FAIL);
    3892             : 
    3893           0 :         tds = dbproc->tds_socket;
    3894           0 :         resinfo = tds->res_info;
    3895             : 
    3896           0 :         for (col = 0; col < resinfo->num_cols; col++) {
    3897           0 :                 colinfo = resinfo->columns[col];
    3898           0 :                 collen = _get_printable_size(colinfo);
    3899           0 :                 namlen = tds_dstr_len(&colinfo->column_name);
    3900           0 :                 len = TDS_MAX(collen, namlen);
    3901           0 :                 for (i = 0; i < len; i++) {
    3902           0 :                         if (buf_len < 1) {
    3903             :                                 return FAIL;
    3904             :                         }
    3905           0 :                         *buffer++ = line_char;
    3906           0 :                         buf_len--;
    3907             :                 }
    3908           0 :                 if ((col + 1) < resinfo->num_cols) {
    3909             :                         i = 0;
    3910           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    3911           0 :                                 if (buf_len < 1) {
    3912             :                                         return FAIL;
    3913             :                                 }
    3914           0 :                                 *buffer++ = c;
    3915           0 :                                 buf_len--;
    3916           0 :                                 i++;
    3917             :                         }
    3918             :                 }
    3919             :         }
    3920           0 :         if (buf_len < 1) {
    3921             :                 return FAIL;
    3922             :         }
    3923           0 :         *buffer = '\0';
    3924           0 :         return SUCCEED;
    3925             : }
    3926             : 
    3927             : /**
    3928             :  * \ingroup dblib_core
    3929             :  * \brief Print result set headings to a buffer.
    3930             :  * 
    3931             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    3932             :  * \param buffer output buffer
    3933             :  * \param buf_len size of \a buffer
    3934             :  * \retval SUCCEED \a buffer filled.
    3935             :  * \retval FAIL insufficient space in \a buffer, usually.
    3936             :  * \sa dbprhead(), dbprrow(), dbsetopt(), dbspr1row(), dbspr1rowlen(), dbsprline().
    3937             :  */
    3938             : RETCODE
    3939           0 : dbsprhead(DBPROCESS * dbproc, char *buffer, DBINT buf_len)
    3940             : {
    3941             :         TDSCOLUMN *colinfo;
    3942             :         TDSRESULTINFO *resinfo;
    3943             :         TDSSOCKET *tds;
    3944             :         int i, collen, namlen;
    3945             :         TDS_USMALLINT col;
    3946             :         int padlen;
    3947             :         int c;
    3948             : 
    3949           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsprhead(%p, %p, %d)\n", dbproc, buffer, buf_len);
    3950           0 :         CHECK_CONN(FAIL);
    3951           0 :         CHECK_NULP(buffer, "dbsprhead", 2, FAIL);
    3952             : 
    3953           0 :         tds = dbproc->tds_socket;
    3954           0 :         resinfo = tds->res_info;
    3955             : 
    3956           0 :         for (col = 0; col < resinfo->num_cols; col++) {
    3957           0 :                 colinfo = resinfo->columns[col];
    3958           0 :                 collen = _get_printable_size(colinfo);
    3959           0 :                 namlen = (int) tds_dstr_len(&colinfo->column_name);
    3960           0 :                 padlen = TDS_MAX(collen, namlen) - namlen;
    3961           0 :                 if (buf_len < namlen) {
    3962             :                         return FAIL;
    3963             :                 }
    3964           0 :                 memcpy(buffer, tds_dstr_cstr(&colinfo->column_name), namlen);
    3965           0 :                 buffer += namlen;
    3966           0 :                 buf_len -= namlen;
    3967           0 :                 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) == -1) {
    3968           0 :                         c = ' ';
    3969             :                 }
    3970           0 :                 for (; padlen > 0; padlen--) {
    3971           0 :                         if (buf_len < 1) {
    3972             :                                 return FAIL;
    3973             :                         }
    3974           0 :                         *buffer++ = c;
    3975           0 :                         buf_len--;
    3976             :                 }
    3977           0 :                 if ((col + 1) < resinfo->num_cols) {
    3978             :                         i = 0;
    3979           0 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    3980           0 :                                 if (buf_len < 1) {
    3981             :                                         return FAIL;
    3982             :                                 }
    3983           0 :                                 *buffer++ = c;
    3984           0 :                                 buf_len--;
    3985           0 :                                 i++;
    3986             :                         }
    3987             :                 }
    3988             :         }
    3989           0 :         if (buf_len < 1) {
    3990             :                 return FAIL;
    3991             :         }
    3992           0 :         *buffer = '\0';
    3993           0 :         return SUCCEED;
    3994             : }
    3995             : 
    3996             : /**
    3997             :  * \ingroup dblib_core
    3998             :  * \brief Print result set headings to stdout.
    3999             :  * 
    4000             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4001             :  * \sa 
    4002             :  */
    4003             : void
    4004          60 : dbprhead(DBPROCESS * dbproc)
    4005             : {
    4006             :         TDSCOLUMN *colinfo;
    4007             :         TDSRESULTINFO *resinfo;
    4008             :         TDSSOCKET *tds;
    4009             :         size_t i, col, len, collen, namlen;
    4010             :         size_t padlen;
    4011             :         int c;
    4012             : 
    4013          60 :         tdsdump_log(TDS_DBG_FUNC, "dbprhead(%p)\n", dbproc);
    4014          60 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    4015             : 
    4016          60 :         tds = dbproc->tds_socket;
    4017          60 :         resinfo = tds->res_info;
    4018          60 :         if (resinfo == NULL) {
    4019             :                 return;
    4020             :         }
    4021         100 :         for (col = 0; col < resinfo->num_cols; col++) {
    4022          80 :                 colinfo = resinfo->columns[col];
    4023          80 :                 collen = _get_printable_size(colinfo);
    4024         160 :                 namlen = tds_dstr_len(&colinfo->column_name);
    4025          80 :                 padlen = TDS_MAX(collen, namlen) - namlen;
    4026         160 :                 printf("%s", tds_dstr_cstr(&colinfo->column_name));
    4027             : 
    4028         160 :                 c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0);
    4029          80 :                 if (c == -1) {
    4030           0 :                         c = ' ';
    4031             :                 }
    4032         600 :                 for (; padlen > 0; padlen--) {
    4033         600 :                         putchar(c);
    4034             :                 }
    4035             : 
    4036          80 :                 if ((col + 1) < resinfo->num_cols) {
    4037             :                         i = 0;
    4038         240 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    4039          60 :                                 putchar(c);
    4040          60 :                                 i++;
    4041             :                         }
    4042             :                 }
    4043             :         }
    4044             :         i = 0;
    4045          80 :         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i)) != -1) {
    4046          20 :                 putchar(c);
    4047          20 :                 i++;
    4048             :         }
    4049         100 :         for (col = 0; col < resinfo->num_cols; col++) {
    4050          80 :                 colinfo = resinfo->columns[col];
    4051          80 :                 collen = _get_printable_size(colinfo);
    4052         160 :                 namlen = tds_dstr_len(&colinfo->column_name);
    4053          80 :                 len = TDS_MAX(collen, namlen);
    4054        1040 :                 for (i = 0; i < len; i++)
    4055         960 :                         putchar('-');
    4056          80 :                 if ((col + 1) < resinfo->num_cols) {
    4057             :                         i = 0;
    4058         240 :                         while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
    4059          60 :                                 putchar(c);
    4060          60 :                                 i++;
    4061             :                         }
    4062             :                 }
    4063             :         }
    4064             :         i = 0;
    4065          80 :         while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i)) != -1) {
    4066          20 :                 putchar(c);
    4067          20 :                 i++;
    4068             :         }
    4069             : }
    4070             : 
    4071             : /** \internal
    4072             :  * \ingroup dblib_internal
    4073             :  * \brief Indicate whether a query returned rows.
    4074             :  * 
    4075             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4076             :  * \sa DBROWS(), DBCMDROW(), dbnextrow(), dbresults(), DBROWTYPE().
    4077             :  */
    4078             : RETCODE
    4079         522 : dbrows(DBPROCESS * dbproc)
    4080             : {
    4081             :         TDSSOCKET *tds;
    4082             : 
    4083         522 :         tdsdump_log(TDS_DBG_FUNC, "dbrows(%p)\n", dbproc);
    4084         522 :         CHECK_CONN(FAIL);
    4085             : 
    4086         522 :         if (!(tds=dbproc->tds_socket))
    4087             :                 return FAIL;
    4088             : 
    4089         522 :         return (tds->res_info && tds->res_info->rows_exist)? SUCCEED : FAIL;
    4090             : }
    4091             : 
    4092             : #if defined(DBLIB_UNIMPLEMENTED)
    4093             : /**
    4094             :  * \ingroup dblib_core
    4095             :  * \brief Set the default character set for an application.
    4096             :  * 
    4097             :  * \param language ASCII null-terminated string.  
    4098             :  * \sa dbsetdeflang(), dbsetdefcharset(), dblogin(), dbopen().
    4099             :  * \retval SUCCEED Always.  
    4100             :  * \todo Unimplemented.
    4101             :  */
    4102             : RETCODE
    4103             : dbsetdeflang(char *language)
    4104             : {
    4105             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetdeflang(%s)\n", language);
    4106             :         CHECK_PARAMETER_NOPROC(language, SYBENULP);
    4107             :         return SUCCEED;
    4108             : }
    4109             : #endif
    4110             : 
    4111             : /**
    4112             :  * \ingroup dblib_core
    4113             :  * \brief Get TDS packet size for the connection.
    4114             :  * 
    4115             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4116             :  * \return TDS packet size, in bytes.  
    4117             :  * \sa DBSETLPACKET()
    4118             :  */
    4119             : int
    4120           0 : dbgetpacket(DBPROCESS * dbproc)
    4121             : {
    4122             :         TDSSOCKET *tds;
    4123             : 
    4124           0 :         tdsdump_log(TDS_DBG_FUNC, "dbgetpacket(%p)\n", dbproc);
    4125           0 :         CHECK_PARAMETER(dbproc, SYBENULL, TDS_DEF_BLKSZ);
    4126             : 
    4127           0 :         tds = dbproc->tds_socket;
    4128           0 :         if (!tds) {
    4129             :                 return TDS_DEF_BLKSZ;
    4130             :         } else {
    4131           0 :                 return tds->conn->env.block_size;
    4132             :         }
    4133             : }
    4134             : 
    4135             : /**
    4136             :  * \ingroup dblib_core
    4137             :  * \brief Set maximum simultaneous connections db-lib will open to the server.
    4138             :  * 
    4139             :  * \param maxprocs Limit for process.
    4140             :  * \retval SUCCEED Always.  
    4141             :  * \sa dbgetmaxprocs(), dbopen()
    4142             :  */
    4143             : RETCODE
    4144         250 : dbsetmaxprocs(int maxprocs)
    4145             : {
    4146             :         int i, j;
    4147             :         TDSSOCKET **old_list;
    4148             : 
    4149         250 :         tdsdump_log(TDS_DBG_FUNC, "UNTESTED dbsetmaxprocs(%d)\n", maxprocs);
    4150             : 
    4151             :         /* not too few elements */
    4152         250 :         if (maxprocs <= 0)
    4153             :                 return FAIL;
    4154             : 
    4155         250 :         tds_mutex_lock(&dblib_mutex);
    4156             : 
    4157         250 :         old_list = g_dblib_ctx.connection_list;
    4158             : 
    4159             :         /* "compress" array */
    4160     1024250 :         for (i = 0, j = 0; i < g_dblib_ctx.connection_list_size; ++i) {
    4161     1024000 :                 if (!old_list[i])
    4162     1024000 :                         continue;
    4163           0 :                 if (i != j) {
    4164           0 :                         old_list[j] = old_list[i];
    4165           0 :                         old_list[i] = NULL;
    4166             :                 }
    4167           0 :                 ++j;
    4168             :         }
    4169             :         /* do not restrict too much, j here contains minimun size */
    4170         250 :         if (maxprocs < j)
    4171           0 :                 maxprocs = j;
    4172             : 
    4173             :         /*
    4174             :          * Don't reallocate less memory.  
    4175             :          * If maxprocs is less than was initially allocated, just reduce the represented list size.  
    4176             :          * If larger, reallocate and copy.
    4177             :          * We probably should check for valid connections beyond the new max.
    4178             :          */
    4179         250 :         if (maxprocs <= g_dblib_ctx.connection_list_size) {
    4180         250 :                 g_dblib_ctx.connection_list_size_represented = maxprocs;
    4181         250 :                 tds_mutex_unlock(&dblib_mutex);
    4182         250 :                 return SUCCEED;
    4183             :         }
    4184             : 
    4185           0 :         g_dblib_ctx.connection_list = tds_new0(TDSSOCKET *, maxprocs);
    4186             : 
    4187           0 :         if (g_dblib_ctx.connection_list == NULL) {
    4188           0 :                 g_dblib_ctx.connection_list = old_list;
    4189           0 :                 tds_mutex_unlock(&dblib_mutex);
    4190           0 :                 dbperror(NULL, SYBEMEM, errno);
    4191           0 :                 return FAIL;
    4192             :         }
    4193             : 
    4194           0 :         for (i = 0; i < g_dblib_ctx.connection_list_size; i++) {
    4195           0 :                 g_dblib_ctx.connection_list[i] = old_list[i];
    4196             :         }
    4197             : 
    4198           0 :         g_dblib_ctx.connection_list_size = maxprocs;
    4199           0 :         g_dblib_ctx.connection_list_size_represented = maxprocs;
    4200             : 
    4201           0 :         tds_mutex_unlock(&dblib_mutex);
    4202             : 
    4203           0 :         free(old_list);
    4204             : 
    4205           0 :         return SUCCEED;
    4206             : }
    4207             : 
    4208             : /**
    4209             :  * \ingroup dblib_core
    4210             :  * \brief get maximum simultaneous connections db-lib will open to the server.
    4211             :  * 
    4212             :  * \return Current maximum.  
    4213             :  * \sa dbsetmaxprocs(), dbopen()
    4214             :  */
    4215             : int
    4216           0 : dbgetmaxprocs(void)
    4217             : {
    4218             :         int r;
    4219             : 
    4220           0 :         tdsdump_log(TDS_DBG_FUNC, "dbgetmaxprocs(void)\n");
    4221             : 
    4222           0 :         tds_mutex_lock(&dblib_mutex);
    4223           0 :         r = g_dblib_ctx.connection_list_size_represented;
    4224           0 :         tds_mutex_unlock(&dblib_mutex);
    4225           0 :         return r;
    4226             : }
    4227             : 
    4228             : /**
    4229             :  * \ingroup dblib_core
    4230             :  * \brief Set maximum seconds db-lib waits for a server response to query.  
    4231             :  * 
    4232             :  * \param seconds New limit for application.  
    4233             :  * \retval SUCCEED Always.  
    4234             :  * \sa dberrhandle(), DBGETTIME(), dbsetlogintime(), dbsqlexec(), dbsqlok(), dbsqlsend().
    4235             :  */
    4236             : RETCODE
    4237          70 : dbsettime(int seconds)
    4238             : {
    4239             :         TDSSOCKET **tds;
    4240             :         int i;
    4241             :         DBPROCESS *dbproc;
    4242          70 :         tdsdump_log(TDS_DBG_FUNC, "dbsettime(%d)\n", seconds);
    4243             : 
    4244          70 :         tds_mutex_lock(&dblib_mutex);
    4245          70 :         g_dblib_ctx.query_timeout = seconds;
    4246             :         
    4247          70 :         tds = g_dblib_ctx.connection_list;
    4248      286790 :         for (i = 0; i <  TDS_MAX_CONN; i++) {
    4249      286720 :                 if (tds[i]) {
    4250         100 :                         dbproc = (DBPROCESS *) tds_get_parent(tds[i]);
    4251         100 :                         if (!dbisopt(dbproc, DBSETTIME, 0))
    4252          80 :                                 tds[i]->query_timeout = seconds;
    4253             :                 }
    4254             :         }
    4255             :         
    4256          70 :         tds_mutex_unlock(&dblib_mutex);
    4257          70 :         return SUCCEED;
    4258             : }
    4259             : 
    4260             : /**
    4261             :  * \ingroup dblib_core
    4262             :  * \brief Get maximum seconds db-lib waits for a server response to query.  
    4263             :  * 
    4264             :  * \retval query timeout limit, in seconds
    4265             :  * \sa dberrhandle(), DBSETTIME(), dbsetlogintime(), dbsqlexec(), dbsqlok(), dbsqlsend().
    4266             :  */
    4267             : int
    4268           0 : dbgettime(void)
    4269             : {
    4270           0 :         tdsdump_log(TDS_DBG_FUNC, "dbgettime()\n");
    4271             : 
    4272           0 :         return g_dblib_ctx.query_timeout;
    4273             : }
    4274             : 
    4275             : /**
    4276             :  * \ingroup dblib_core
    4277             :  * \brief Set maximum seconds db-lib waits for a server response to a login attempt.  
    4278             :  * 
    4279             :  * \param seconds New limit for application.  
    4280             :  * \retval SUCCEED Always.  
    4281             :  * \sa dberrhandle(), dbsettime()
    4282             :  */
    4283             : RETCODE
    4284          20 : dbsetlogintime(int seconds)
    4285             : {
    4286          20 :         tdsdump_log(TDS_DBG_FUNC, "dbsetlogintime(%d)\n", seconds);
    4287             : 
    4288          20 :         tds_mutex_lock(&dblib_mutex);
    4289          20 :         g_dblib_ctx.login_timeout = seconds;
    4290          20 :         tds_mutex_unlock(&dblib_mutex);
    4291          20 :         return SUCCEED;
    4292             : }
    4293             : 
    4294             : /** \internal
    4295             :  * \ingroup dblib_internal
    4296             :  * \brief See if the current command can return rows.
    4297             :  * 
    4298             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4299             :  * \retval SUCCEED Yes, it can.  
    4300             :  * \retval FAIL No, it can't.
    4301             :  * \remarks Use   DBCMDROW() macro instead.  
    4302             :  * \sa DBCMDROW(), dbnextrow(), dbresults(), DBROWS(), DBROWTYPE().
    4303             :  */
    4304             : RETCODE
    4305           0 : dbcmdrow(DBPROCESS * dbproc)
    4306             : {
    4307             :         TDSSOCKET *tds;
    4308             : 
    4309           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcmdrow(%p)\n", dbproc);
    4310           0 :         CHECK_CONN(FAIL);
    4311             : 
    4312           0 :         tds = dbproc->tds_socket;
    4313           0 :         if (tds->res_info)
    4314             :                 return SUCCEED;
    4315           0 :         return FAIL;
    4316             : }
    4317             : 
    4318             : /**
    4319             :  * \ingroup dblib_core
    4320             :  * \brief Get column ID of a compute column.
    4321             :  * 
    4322             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4323             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4324             :  * \param column Nth column in \a computeid, starting from 1.
    4325             :  * \return Nth column in the base result set, on which \a column was computed.  
    4326             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbgetrow(), dbnextrow(), dbnumalts(), dbprtype(). 
    4327             :  */
    4328             : int
    4329          16 : dbaltcolid(DBPROCESS * dbproc, int computeid, int column)
    4330             : {
    4331             :         TDSCOLUMN *curcol;
    4332             : 
    4333          16 :         tdsdump_log(TDS_DBG_FUNC, "dbaltcolid(%p, %d, %d)\n", dbproc, computeid, column);
    4334             : 
    4335          16 :         curcol = dbacolptr(dbproc, computeid, column, false);
    4336          16 :         if (!curcol)
    4337             :                 return -1;
    4338             : 
    4339          16 :         return curcol->column_operand;
    4340             : }
    4341             : 
    4342             : /**
    4343             :  * \ingroup dblib_core
    4344             :  * \brief Get size of data in a compute column.
    4345             :  * 
    4346             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4347             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4348             :  * \param column Nth column in \a computeid, starting from 1.
    4349             :  * \return size of the data, in bytes.
    4350             :  * \retval -1 no such \a column or \a computeid. 
    4351             :  * \retval 0 data are NULL.
    4352             :  * \sa dbadata(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
    4353             :  */
    4354             : DBINT
    4355           0 : dbadlen(DBPROCESS * dbproc, int computeid, int column)
    4356             : {
    4357             :         TDSCOLUMN *colinfo;
    4358             :         DBINT len;
    4359             : 
    4360           0 :         tdsdump_log(TDS_DBG_FUNC, "dbadlen(%p, %d, %d)\n", dbproc, computeid, column);
    4361             : 
    4362           0 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    4363           0 :         if (!colinfo)
    4364             :                 return -1;
    4365             : 
    4366           0 :         len = TDS_MAX(colinfo->column_cur_size, 0);
    4367             : 
    4368           0 :         tdsdump_log(TDS_DBG_FUNC, "leaving dbadlen() type = %d, returning %d\n", colinfo->column_type, len);
    4369             : 
    4370             :         return len;
    4371             : }
    4372             : 
    4373             : /**
    4374             :  * \ingroup dblib_core
    4375             :  * \brief Get datatype for a compute column.
    4376             :  * 
    4377             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4378             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4379             :  * \param column Nth column in \a computeid, starting from 1.
    4380             :  * \return \c SYB* dataype token.
    4381             :  * \retval -1 no such \a column or \a computeid. 
    4382             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbnextrow(), dbnumalts(), dbprtype().
    4383             :  */
    4384             : int
    4385          16 : dbalttype(DBPROCESS * dbproc, int computeid, int column)
    4386             : {
    4387             :         TDSCOLUMN *colinfo;
    4388             : 
    4389          16 :         tdsdump_log(TDS_DBG_FUNC, "dbalttype(%p, %d, %d)\n", dbproc, computeid, column);
    4390             : 
    4391          16 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    4392          16 :         if (!colinfo)
    4393             :                 return -1;
    4394             : 
    4395          16 :         return dblib_coltype(colinfo);
    4396             : }
    4397             : 
    4398             : /**
    4399             :  * \ingroup dblib_core
    4400             :  * \brief Bind a compute column to a program variable.
    4401             :  * 
    4402             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4403             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4404             :  * \param column Nth column in \a computeid, starting from 1.
    4405             :  * \param vartype datatype of the host variable that will receive the data
    4406             :  * \param varlen size of host variable pointed to \a varaddr
    4407             :  * \param varaddr address of host variable
    4408             :  * \retval SUCCEED everything worked.
    4409             :  * \retval FAIL no such \a computeid or \a column, or no such conversion possible, or target buffer too small.
    4410             :  * \sa dbadata(), dbaltbind_ps(), dbanullbind(), dbbind(), dbbind_ps(), dbconvert(),
    4411             :  *      dbconvert_ps(), dbnullbind(), dbsetnull(), dbsetversion(), dbwillconvert().
    4412             :  */
    4413             : RETCODE
    4414          16 : dbaltbind(DBPROCESS * dbproc, int computeid, int column, int vartype, DBINT varlen, BYTE * varaddr)
    4415             : {
    4416             :         TDS_SERVER_TYPE srctype, desttype;
    4417          16 :         TDSCOLUMN *colinfo = NULL;
    4418             : 
    4419          16 :         tdsdump_log(TDS_DBG_FUNC, "dbaltbind(%p, %d, %d, %d, %d, %p)\n", dbproc, computeid, column, vartype, varlen, varaddr);
    4420             : 
    4421          16 :         colinfo = dbacolptr(dbproc, computeid, column, true);
    4422          16 :         if (!colinfo)
    4423             :                 return FAIL;
    4424          16 :         CHECK_PARAMETER(varaddr, SYBEABNV, FAIL);
    4425             : 
    4426          16 :         dbproc->avail_flag = FALSE;
    4427             : 
    4428          16 :         srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    4429          16 :         desttype = dblib_bound_type(vartype);
    4430          16 :         if (desttype == TDS_INVALID_TYPE) {
    4431           0 :                 dbperror(dbproc, SYBEBTYP, 0);
    4432           0 :                 return FAIL;
    4433             :         }
    4434             : 
    4435          16 :         if (!dbwillconvert(srctype, desttype)) {
    4436           0 :                 dbperror(dbproc, SYBEAAMT, 0);
    4437           0 :                 return FAIL;
    4438             :         }
    4439             : 
    4440          16 :         colinfo->column_varaddr = (char *) varaddr;
    4441          16 :         colinfo->column_bindtype = vartype;
    4442          16 :         colinfo->column_bindlen = varlen;
    4443             : 
    4444          16 :         return SUCCEED;
    4445             : }
    4446             : 
    4447             : 
    4448             : /**
    4449             :  * \ingroup dblib_core
    4450             :  * \brief Get address of compute column data.  
    4451             :  * 
    4452             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4453             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4454             :  * \param column Nth column in \a computeid, starting from 1.
    4455             :  * \return pointer to columns's data buffer.  
    4456             :  * \retval NULL no such \a computeid or \a column.  
    4457             :  * \sa dbadlen(), dbaltbind(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
    4458             :  */
    4459             : BYTE *
    4460           0 : dbadata(DBPROCESS * dbproc, int computeid, int column)
    4461             : {
    4462             :         TDSCOLUMN *colinfo;
    4463             : 
    4464           0 :         tdsdump_log(TDS_DBG_FUNC, "dbadata(%p, %d, %d)\n", dbproc, computeid, column);
    4465             : 
    4466           0 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    4467           0 :         if (!colinfo)
    4468             :                 return NULL;
    4469             : 
    4470           0 :         if (is_blob_col(colinfo)) {
    4471           0 :                 return (BYTE *) ((TDSBLOB *) colinfo->column_data)->textvalue;
    4472             :         }
    4473             : 
    4474           0 :         return (BYTE *) colinfo->column_data;
    4475             : }
    4476             : 
    4477             : /**
    4478             :  * \ingroup dblib_core
    4479             :  * \brief Get aggregation operator for a compute column.
    4480             :  * 
    4481             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4482             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4483             :  * \param column Nth column in \a computeid, starting from 1.
    4484             :  * \return token value for the type of the compute column's aggregation operator.
    4485             :  * \retval -1 no such \a computeid or \a column.  
    4486             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbnextrow(), dbnumalts(), dbprtype().
    4487             :  */
    4488             : int
    4489          16 : dbaltop(DBPROCESS * dbproc, int computeid, int column)
    4490             : {
    4491             :         TDSCOLUMN *curcol;
    4492             : 
    4493          16 :         tdsdump_log(TDS_DBG_FUNC, "dbaltop(%p, %d, %d)\n", dbproc, computeid, column);
    4494             : 
    4495          16 :         if ((curcol=dbacolptr(dbproc, computeid, column, false)) == NULL)
    4496             :                 return -1;
    4497             : 
    4498          16 :         return curcol->column_operator;
    4499             : }
    4500             : 
    4501             : /**
    4502             :  * \ingroup dblib_core
    4503             :  * \brief Set db-lib or server option.
    4504             :  * 
    4505             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4506             :  * \param option option to set.  
    4507             :  * \param char_param value to set \a option to, if it wants a null-teminated ASCII string.  
    4508             :  * \param int_param  value to set \a option to, if it wants an integer value.  
    4509             :  * \retval SUCCEED everything worked.
    4510             :  * \retval FAIL no such \a option, or insufficient memory, or unimplemented.  
    4511             :  * \remarks Many are unimplemented.
    4512             :  * \sa dbclropt(), dbisopt().
    4513             :  * \todo Implement more options.  
    4514             :  */
    4515             : RETCODE
    4516          90 : dbsetopt(DBPROCESS * dbproc, int option, const char *char_param, int int_param)
    4517             : {
    4518             :         char *cmd;
    4519             :         RETCODE rc;
    4520             :         int i;
    4521             : 
    4522          90 :         tdsdump_log(TDS_DBG_FUNC, "dbsetopt(%p, %d, %s, %d)\n", dbproc, option, char_param, int_param);
    4523          90 :         CHECK_CONN(FAIL);
    4524          90 :         CHECK_NULP(char_param, "dbsetopt", 3, FAIL);
    4525             : 
    4526          90 :         if ((option < 0) || (option >= DBNUMOPTIONS)) {
    4527           0 :                 dbperror(dbproc, SYBEUNOP, 0);
    4528           0 :                 return FAIL;
    4529             :         }
    4530             : 
    4531          90 :         rc = FAIL;
    4532          90 :         switch (option) {
    4533           0 :         case DBARITHABORT:
    4534             :         case DBARITHIGNORE:
    4535             :         case DBCHAINXACTS:
    4536             :         case DBFIPSFLAG:
    4537             :         case DBISOLATION:
    4538             :         case DBNOCOUNT:
    4539             :         case DBNOEXEC:
    4540             :         case DBPARSEONLY:
    4541             :         case DBSHOWPLAN:
    4542             :         case DBSTORPROCID:
    4543             :         case DBQUOTEDIDENT:
    4544             :                 /* server options (on/off) */
    4545           0 :                 if (asprintf(&cmd, "set %s on\n", dbproc->dbopts[option].text) < 0) {
    4546             :                         return FAIL;
    4547             :                 }
    4548           0 :                 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
    4549           0 :                 free(cmd);
    4550           0 :                 break;
    4551           0 :         case DBNATLANG:
    4552             :         case DBDATEFIRST:
    4553             :         case DBDATEFORMAT:
    4554             :                 /* server options (char_param) */
    4555           0 :                 if (asprintf(&cmd, "set %s %s\n", dbproc->dbopts[option].text, char_param) < 0) {
    4556             :                         return FAIL;
    4557             :                 }
    4558           0 :                 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
    4559           0 :                 free(cmd);
    4560           0 :                 break;
    4561             :         case DBOFFSET:
    4562             :                 /* server option */
    4563             :                 /* requires param
    4564             :                  * "select", "from", "table", "order", "compute",
    4565             :                  * "statement", "procedure", "execute", or "param"
    4566             :                  */
    4567             :                 rc = SUCCEED;
    4568             :                 break;
    4569             :         case DBROWCOUNT:
    4570             :                 /* server option */
    4571             :                 /* requires param "0" to "2147483647" */
    4572             :                 rc = SUCCEED;
    4573             :                 break;
    4574             :         case DBSTAT:
    4575             :                 /* server option */
    4576             :                 /* requires param "io" or "time" */
    4577             :                 rc = SUCCEED;
    4578             :                 break;
    4579             :         case DBTEXTLIMIT:
    4580             :                 /* dblib option */
    4581             :                 /* requires param "0" to "2147483647" */
    4582             :                 /* dblib do not return more than this length from text/image */
    4583             :                 /* TODO required for PHP */
    4584             :                 rc = SUCCEED;
    4585             :                 break;
    4586             :         case DBTEXTSIZE:
    4587             :                 /* server option */
    4588             :                 /* requires param "0" to "2147483647" */
    4589             :                 /* limit text/image from network */
    4590             :                 if (!char_param)
    4591             :                         char_param = "0";
    4592           0 :                 i = atoi(char_param);
    4593           0 :                 if (i < 0 || i > 2147483647)
    4594             :                         return FAIL;
    4595           0 :                 if (asprintf(&cmd, "set textsize %d\n", i) < 0)
    4596             :                         return FAIL;
    4597           0 :                 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
    4598           0 :                 free(cmd);
    4599           0 :                 break;
    4600             :         case DBAUTH:
    4601             :                 /* ??? */
    4602             :                 rc = SUCCEED;
    4603             :                 break;
    4604             :         case DBNOAUTOFREE:
    4605             :                 /* dblib option */
    4606             :                 rc = SUCCEED;
    4607             :                 break;
    4608             :         case DBBUFFER:
    4609             :                 /* 
    4610             :                  * Requires param "2" to "2147483647" 
    4611             :                  * (0 or 1 is an error, < 0 yields the default 100) 
    4612             :                  */
    4613             :                 {
    4614             :                         int nrows; 
    4615             :                         
    4616             :                         /* 100 is the default, according to Microsoft */
    4617             :                         if( !char_param )
    4618             :                                 char_param = "100";
    4619             : 
    4620          60 :                         nrows = atoi(char_param);
    4621             : 
    4622          60 :                         nrows = (nrows < 0 )? 100 : nrows;
    4623             : 
    4624          60 :                         if( 1 < nrows && nrows <= 2147483647 ) {
    4625          60 :                                 buffer_set_capacity(dbproc, nrows);
    4626          60 :                                 rc = SUCCEED;
    4627             :                         }
    4628             :                 }
    4629             :                 break;
    4630           0 :         case DBPRCOLSEP:
    4631             :         case DBPRLINELEN:
    4632             :         case DBPRLINESEP:
    4633           0 :                 rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param);
    4634           0 :                 break;
    4635           0 :         case DBPRPAD:
    4636             :                 /*
    4637             :                  * "If the character is not specified, the ASCII space character is used." 
    4638             :                  * A NULL pointer to the pad character signifies that padding is turned off. 
    4639             :                  */
    4640           0 :                 if (int_param) {
    4641           0 :                         rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param? char_param : " ");
    4642             :                 } else {
    4643           0 :                         rc = dbstring_assign(&(dbproc->dbopts[option].param), NULL);
    4644             :                 }
    4645             :                 break;
    4646             :         case DBSETTIME:
    4647             :                 if (char_param) {
    4648          30 :                         i = atoi(char_param);
    4649          30 :                         if (0 < i) {
    4650          60 :                                 rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param);
    4651          30 :                                 if (rc == SUCCEED) {
    4652          30 :                                         dbproc->tds_socket->query_timeout = i;
    4653             :                                 }
    4654             :                         }
    4655             :                 }
    4656             :                 break;
    4657           0 :         default:
    4658           0 :                 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetopt(option = %d)\n", option);
    4659             :                 return FAIL;
    4660             :         }
    4661          30 :         if (rc == SUCCEED)
    4662          90 :                 dbproc->dbopts[option].factive = 1;
    4663             :         return rc;
    4664             : }
    4665             : 
    4666             : /**
    4667             :  * \ingroup dblib_core
    4668             :  * \brief Set interrupt handler for db-lib to use while blocked against a read from the server.
    4669             :  * 
    4670             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4671             :  * \param chkintr
    4672             :  * \param hndlintr
    4673             :  * \sa dbcancel(), dbgetuserdata(), dbsetuserdata(), dbsetbusy(), dbsetidle().
    4674             :  */
    4675             : void
    4676          20 : dbsetinterrupt(DBPROCESS * dbproc, DB_DBCHKINTR_FUNC chkintr, DB_DBHNDLINTR_FUNC hndlintr)
    4677             : {
    4678          20 :         tdsdump_log(TDS_DBG_FUNC, "dbsetinterrupt(%p, %p, %p)\n", dbproc, chkintr, hndlintr);
    4679          20 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    4680             : 
    4681          20 :         dbproc->chkintr = chkintr;
    4682          20 :         dbproc->hndlintr = hndlintr;
    4683             : }
    4684             : 
    4685             : /**
    4686             :  * \ingroup dblib_rpc
    4687             :  * \brief Determine if query generated a return status number.
    4688             :  * 
    4689             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4690             :  * \retval TRUE fetch return status with dbretstatus().  
    4691             :  * \retval FALSE no return status.  
    4692             :  * \sa dbnextrow(), dbresults(), dbretdata(), dbretstatus(), dbrpcinit(), dbrpcparam(), dbrpcsend().
    4693             :  */
    4694             : DBBOOL
    4695          62 : dbhasretstat(DBPROCESS * dbproc)
    4696             : {
    4697             :         TDSSOCKET *tds;
    4698             : 
    4699          62 :         tdsdump_log(TDS_DBG_FUNC, "dbhasretstat(%p)\n", dbproc);
    4700          62 :         CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
    4701             : 
    4702          62 :         tds = dbproc->tds_socket;
    4703          62 :         if (tds->has_status) {
    4704             :                 return TRUE;
    4705             :         } else {
    4706          30 :                 return FALSE;
    4707             :         }
    4708             : }
    4709             : 
    4710             : /**
    4711             :  * \ingroup dblib_rpc
    4712             :  * \brief Fetch status value returned by query or remote procedure call.
    4713             :  * 
    4714             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4715             :  * \return return value 
    4716             :  * \sa dbhasretstat(), dbnextrow(), dbresults(), dbretdata(), dbrpcinit(), dbrpcparam(), dbrpcsend().
    4717             :  */
    4718             : DBINT
    4719         518 : dbretstatus(DBPROCESS * dbproc)
    4720             : {
    4721         518 :         tdsdump_log(TDS_DBG_FUNC, "dbretstatus(%p)\n", dbproc);
    4722         518 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    4723             : 
    4724         518 :         return dbproc->tds_socket->ret_status;
    4725             : }
    4726             : 
    4727             : /**
    4728             :  * \ingroup dblib_rpc
    4729             :  * \brief Get count of output parameters filled by a stored procedure.
    4730             :  * 
    4731             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4732             :  * \return How many, possibly zero.  
    4733             :  * \remarks This name sounds funny.  
    4734             :  * \sa 
    4735             :  */
    4736             : int
    4737         806 : dbnumrets(DBPROCESS * dbproc)
    4738             : {
    4739             :         TDSSOCKET *tds;
    4740             :         TDS_INT result_type;
    4741             : 
    4742         806 :         tdsdump_log(TDS_DBG_FUNC, "dbnumrets(%p)\n", dbproc);
    4743         806 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    4744             : 
    4745         806 :         tds = dbproc->tds_socket;
    4746             : 
    4747         806 :         tdsdump_log(TDS_DBG_FUNC, "dbnumrets() finds %d columns\n", (tds->param_info? tds->param_info->num_cols : 0));
    4748             : 
    4749             :         /* try to fetch output parameters and return status, if we have not already done so */
    4750         806 :         if (!tds->param_info) 
    4751         508 :                 tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_TRAILING);
    4752             :                 
    4753         806 :         if (!tds->param_info)
    4754             :                 return 0;
    4755             : 
    4756         300 :         return tds->param_info->num_cols;
    4757             : }
    4758             : 
    4759             : /**
    4760             :  * \ingroup dblib_rpc
    4761             :  * \brief Get name of an output parameter filled by a stored procedure.
    4762             :  * 
    4763             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4764             :  * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
    4765             :  * \returns ASCII null-terminated string, \c NULL if no such \a retnum.  
    4766             :  * \sa dbnextrow(), dbnumrets(), dbresults(), dbretdata(), dbretlen(), dbrettype(), dbrpcinit(), dbrpcparam().
    4767             :  */
    4768             : char *
    4769          66 : dbretname(DBPROCESS * dbproc, int retnum)
    4770             : {
    4771             :         TDSPARAMINFO *param_info;
    4772             : 
    4773          66 :         tdsdump_log(TDS_DBG_FUNC, "dbretname(%p, %d)\n", dbproc, retnum);
    4774          66 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    4775             : 
    4776          66 :         if (!dbproc->tds_socket)
    4777             :                 return NULL;
    4778             : 
    4779          66 :         dbnumrets(dbproc);
    4780             : 
    4781          66 :         param_info = dbproc->tds_socket->param_info;
    4782          66 :         if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
    4783             :                 return NULL;
    4784         132 :         return tds_dstr_buf(&param_info->columns[retnum - 1]->column_name);
    4785             : }
    4786             : 
    4787             : /**
    4788             :  * \ingroup dblib_rpc
    4789             :  * \brief Get value of an output parameter filled by a stored procedure.
    4790             :  * 
    4791             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4792             :  * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
    4793             :  * \returns Address of a return parameter value, or \c NULL if no such \a retnum.  
    4794             :  * \sa dbnextrow(), dbnumrets(), dbresults(), dbretlen(), dbretname(), dbrettype(), dbrpcinit(), dbrpcparam().
    4795             :  * \todo Handle blobs.
    4796             :  */
    4797             : BYTE *
    4798          70 : dbretdata(DBPROCESS * dbproc, int retnum)
    4799             : {
    4800             :         TDSPARAMINFO *param_info;
    4801             : 
    4802          70 :         tdsdump_log(TDS_DBG_FUNC, "dbretdata(%p, %d)\n", dbproc, retnum);
    4803          70 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    4804             : 
    4805          70 :         dbnumrets(dbproc);
    4806             : 
    4807          70 :         param_info = dbproc->tds_socket->param_info;
    4808          70 :         if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
    4809             :                 return NULL;
    4810             : 
    4811          70 :         return _dbcoldata(param_info->columns[retnum - 1]);
    4812             : }
    4813             : 
    4814             : /**
    4815             :  * \ingroup dblib_rpc
    4816             :  * \brief Get size of an output parameter filled by a stored procedure.
    4817             :  * 
    4818             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4819             :  * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
    4820             :  * \returns Size of a return parameter value, or \c NULL if no such \a retnum.  
    4821             :  * \sa dbnextrow(), dbnumrets(), dbresults(), dbretdata(), dbretname(), dbrettype(), dbrpcinit(), dbrpcparam().
    4822             :  */
    4823             : int
    4824          66 : dbretlen(DBPROCESS * dbproc, int retnum)
    4825             : {
    4826             :         TDSCOLUMN *column;
    4827             :         TDSPARAMINFO *param_info;
    4828             :         TDSSOCKET *tds;
    4829             : 
    4830          66 :         tdsdump_log(TDS_DBG_FUNC, "dbretlen(%p, %d)\n", dbproc, retnum);
    4831          66 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    4832             : 
    4833          66 :         dbnumrets(dbproc);
    4834             : 
    4835          66 :         tds = dbproc->tds_socket;
    4836          66 :         param_info = tds->param_info;
    4837          66 :         if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
    4838             :                 return -1;
    4839             : 
    4840          66 :         column = param_info->columns[retnum - 1];
    4841          66 :         if (column->column_cur_size < 0)
    4842             :                 return 0;
    4843             : 
    4844          52 :         return column->column_cur_size;
    4845             : }
    4846             : 
    4847             : /**
    4848             :  * \ingroup dblib_core
    4849             :  * \brief Wait for results of a query from the server.  
    4850             :  * 
    4851             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4852             :  * \retval SUCCEED everything worked, fetch results with \c dbnextresults().
    4853             :  * \retval FAIL SQL syntax error, typically.  
    4854             :  * \sa dbcmd(), dbfcmd(), DBIORDESC(), DBIOWDESC(), dbmoretext(), dbnextrow(),
    4855             :         dbpoll(), DBRBUF(), dbresults(), dbretstatus(), dbrpcsend(), dbsettime(), dbsqlexec(),
    4856             :         dbsqlsend(), dbwritetext().
    4857             :  */
    4858             : RETCODE
    4859       20586 : dbsqlok(DBPROCESS * dbproc)
    4860             : {
    4861             :         TDSSOCKET *tds;
    4862             :         TDS_INT result_type;
    4863       20586 :         RETCODE return_code = SUCCEED;
    4864             : 
    4865       20586 :         tdsdump_log(TDS_DBG_FUNC, "dbsqlok(%p)\n", dbproc);
    4866       20586 :         CHECK_CONN(FAIL);
    4867             : 
    4868       20586 :         tds = dbproc->tds_socket;
    4869             : 
    4870             :         /*
    4871             :          * dbsqlok has been called after dbmoretext()
    4872             :          * This is the trigger to send the text data.
    4873             :          */
    4874             : 
    4875       20586 :         if (dbproc->text_sent) {
    4876           0 :                 tds_flush_packet(tds);
    4877           0 :                 dbproc->text_sent = 0;
    4878             :         }
    4879             : 
    4880             :         /* 
    4881             :          * See what the next packet from the server is.
    4882             :          * We want to skip any messages which are not processable. 
    4883             :          * We're looking for a result token or a done token.
    4884             :          */
    4885         297 :         for (;;) {
    4886             :                 TDSRET tds_code;
    4887       20883 :                 int done_flags = 0;
    4888             : 
    4889             :                 /* 
    4890             :                  * If we hit an end token -- e.g. if the command
    4891             :                  * submitted returned no data (like an insert) -- then
    4892             :                  * we process the end token to extract the status code. 
    4893             :                  */
    4894       20883 :                 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() not done, calling tds_process_tokens()\n");
    4895             : 
    4896       20883 :                 tds_code = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS);
    4897             : 
    4898             :                 /* 
    4899             :                  * The error flag may be set for any intervening DONEINPROC packet, in particular
    4900             :                  * by a RAISERROR statement.  Microsoft db-lib returns FAIL in that case. 
    4901             :                  */
    4902       20883 :                 if (done_flags & TDS_DONE_ERROR) {
    4903          66 :                         return_code = FAIL;
    4904             :                 }
    4905             :                 
    4906       20883 :                 switch (tds_code) {
    4907             :                 case TDS_NO_MORE_RESULTS:
    4908       20586 :                         return SUCCEED;
    4909             :                         break;
    4910             : 
    4911       20819 :                 case TDS_SUCCESS:
    4912       20819 :                         switch (result_type) {
    4913       12130 :                         case TDS_ROWFMT_RESULT:
    4914       12130 :                                 buffer_free(&dbproc->row_buf);
    4915       12130 :                                 buffer_alloc(dbproc);
    4916       12130 :                         case TDS_COMPUTEFMT_RESULT:
    4917       12130 :                                 dbproc->dbresults_state = _DB_RES_RESULTSET_EMPTY;
    4918       12130 :                         case TDS_COMPUTE_RESULT:
    4919             :                         case TDS_ROW_RESULT:
    4920       12130 :                                 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() found result token\n");
    4921             :                                 return SUCCEED;
    4922             :                                 break;
    4923             :                         case TDS_DONEINPROC_RESULT:
    4924             :                                 break;
    4925        8392 :                         case TDS_DONE_RESULT:
    4926             :                         case TDS_DONEPROC_RESULT:
    4927        8392 :                                 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() end status is %d (%s)\n", return_code, prdbretcode(return_code));
    4928             : #if 1
    4929        8392 :                                 if (done_flags & TDS_DONE_ERROR) {
    4930             : 
    4931          66 :                                         if (done_flags & TDS_DONE_MORE_RESULTS) {
    4932           0 :                                                 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    4933             :                                         } else {
    4934          66 :                                                 dbproc->dbresults_state = _DB_RES_NO_MORE_RESULTS;
    4935             :                                         }
    4936             : 
    4937             :                                 } else {
    4938        8326 :                                         tdsdump_log(TDS_DBG_FUNC, "dbsqlok() end status was success\n");
    4939             : 
    4940        8326 :                                         dbproc->dbresults_state = _DB_RES_SUCCEED;
    4941             :                                 }
    4942             : 
    4943             :                                 return return_code;
    4944             :                                 break;
    4945             : #else
    4946             :                                 int retcode = (done_flags & TDS_DONE_ERROR)? FAIL : SUCCEED;
    4947             :                                 dbproc->dbresults_state = (done_flags & TDS_DONE_MORE_RESULTS)?
    4948             :                                         _DB_RES_NEXT_RESULT : _DB_RES_NO_MORE_RESULTS;
    4949             : 
    4950             :                                 tdsdump_log(TDS_DBG_FUNC, "dbsqlok: returning %s with %s (%#x)\n", 
    4951             :                                                 prdbretcode(retcode), prdbresults_state(dbproc->dbresults_state), done_flags);
    4952             :                                                 
    4953             :                                 if (retcode == SUCCEED && (done_flags & TDS_DONE_MORE_RESULTS))
    4954             :                                         continue;
    4955             :                                          
    4956             :                                 return retcode;
    4957             : #endif
    4958          47 :                         default:
    4959          47 :                                 tdsdump_log(TDS_DBG_FUNC, "%s %d: logic error: tds_process_tokens result_type %d\n", 
    4960             :                                                 __FILE__, __LINE__, result_type);
    4961             :                                 break;
    4962             :                         }
    4963             :                         break;
    4964             : 
    4965          40 :                 default:
    4966          40 :                         assert(TDS_FAILED(tds_code));
    4967             :                         return FAIL;
    4968             :                         break;
    4969             :                 }
    4970             :         }
    4971             : 
    4972             :         return SUCCEED;
    4973             : }
    4974             : 
    4975             : /**
    4976             :  * \ingroup dblib_core
    4977             :  * \brief Get count of columns in a compute row.
    4978             :  * 
    4979             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    4980             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    4981             :  * \return number of columns, else -1 if no such \a computeid.  
    4982             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumcols(). 
    4983             :  */
    4984             : int
    4985          16 : dbnumalts(DBPROCESS * dbproc, int computeid)
    4986             : {
    4987             :         TDSSOCKET *tds;
    4988             :         TDSCOMPUTEINFO *info;
    4989             :         TDS_SMALLINT compute_id;
    4990             :         TDS_UINT i;
    4991             : 
    4992          16 :         tdsdump_log(TDS_DBG_FUNC, "dbnumalts(%p, %d)\n", dbproc, computeid);
    4993          16 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    4994             : 
    4995          16 :         tds = dbproc->tds_socket;
    4996          16 :         compute_id = computeid;
    4997             : 
    4998          24 :         for (i = 0;; ++i) {
    4999          32 :                 if (i >= tds->num_comp_info)
    5000             :                         return -1;
    5001          24 :                 info = tds->comp_info[i];
    5002          24 :                 if (info->computeid == compute_id)
    5003             :                         break;
    5004             :         }
    5005             : 
    5006          16 :         return info->num_cols;
    5007             : }
    5008             : 
    5009             : /**
    5010             :  * \ingroup dblib_core
    5011             :  * \brief Get count of \c COMPUTE clauses for a result set.
    5012             :  * 
    5013             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5014             :  * \return number of compute clauses for the current query, possibly zero.  
    5015             :  * \sa dbnumalts(), dbresults().
    5016             :  */
    5017             : int
    5018           0 : dbnumcompute(DBPROCESS * dbproc)
    5019             : {
    5020             :         TDSSOCKET *tds;
    5021             : 
    5022           0 :         tdsdump_log(TDS_DBG_FUNC, "dbnumcompute(%p)\n", dbproc);
    5023           0 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    5024             : 
    5025           0 :         tds = dbproc->tds_socket;
    5026             : 
    5027           0 :         return tds->num_comp_info;
    5028             : }
    5029             : 
    5030             : 
    5031             : /**
    5032             :  * \ingroup dblib_core
    5033             :  * \brief Get \c bylist for a compute row.
    5034             :  * 
    5035             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5036             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    5037             :  * \param size \em output: size of \c bylist buffer whose address is returned, possibly zero.  
    5038             :  * \return address of \c bylist for \a computeid.  
    5039             :  * \retval NULL no such \a computeid.
    5040             :  * \remarks Do not free returned pointer.  
    5041             :  * \sa dbadata(), dbadlen(), dbaltlen(), dbalttype(), dbcolname(), dbgetrow(), dbnextrow().
    5042             :  */
    5043             : BYTE *
    5044           0 : dbbylist(DBPROCESS * dbproc, int computeid, int *size)
    5045             : {
    5046             :         TDSSOCKET *tds;
    5047             :         TDSCOMPUTEINFO *info;
    5048             :         TDS_UINT i;
    5049           0 :         const TDS_SMALLINT byte_flag = -0x8000;
    5050             : 
    5051           0 :         tdsdump_log(TDS_DBG_FUNC, "dbbylist(%p, %d, %p)\n", dbproc, computeid, size);
    5052           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    5053             : 
    5054           0 :         tds = dbproc->tds_socket;
    5055             : 
    5056           0 :         for (i = 0;; ++i) {
    5057           0 :                 if (i >= tds->num_comp_info) {
    5058           0 :                         if (size)
    5059           0 :                                 *size = 0;
    5060             :                         return NULL;
    5061             :                 }
    5062           0 :                 info = tds->comp_info[i];
    5063           0 :                 if (info->computeid == computeid)
    5064             :                         break;
    5065             :         }
    5066             : 
    5067           0 :         if (size)
    5068           0 :                 *size = info->by_cols;
    5069             : 
    5070             :         /*
    5071             :          * libtds stores this information using TDS_SMALLINT so we 
    5072             :          * have to convert it. We can do this because libtds just
    5073             :          * stores these data.
    5074             :          */
    5075           0 :         if (info->by_cols > 0 && info->bycolumns[0] != byte_flag) {
    5076             :                 int n;
    5077           0 :                 TDS_TINYINT *p = (TDS_TINYINT*) malloc(sizeof(info->bycolumns[0]) + info->by_cols);
    5078           0 :                 if (!p) {
    5079           0 :                         dbperror(dbproc, SYBEMEM, errno);
    5080           0 :                         return NULL;
    5081             :                 }
    5082           0 :                 for (n = 0; n < info->by_cols; ++n)
    5083           0 :                         p[sizeof(info->bycolumns[0]) + n] = TDS_MIN(info->bycolumns[n], 255);
    5084           0 :                 *((TDS_SMALLINT *)p) = byte_flag;
    5085           0 :                 free(info->bycolumns);
    5086           0 :                 info->bycolumns = (TDS_SMALLINT *) p;
    5087             :         }
    5088           0 :         return (BYTE *) (&info->bycolumns[1]);
    5089             : }
    5090             : 
    5091             : /** \internal
    5092             :  * \ingroup dblib_internal
    5093             :  * \brief Check if \a dbproc is an ex-parrot.  
    5094             :  * 
    5095             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5096             :  * \retval TRUE process has been marked \em dead.
    5097             :  * \retval FALSE process is OK.  
    5098             :  * \remarks dbdead() does not communicate with the server.  
    5099             :  *      Unless a previously db-lib marked \a dbproc \em dead, dbdead() returns \c FALSE.  
    5100             :  * \sa dberrhandle().
    5101             :  */
    5102             : DBBOOL
    5103          10 : dbdead(DBPROCESS * dbproc)
    5104             : {
    5105          10 :         tdsdump_log(TDS_DBG_FUNC, "dbdead(%p) [%s]\n", dbproc, dbproc? IS_TDSDEAD(dbproc->tds_socket)? "dead":"alive" : "quite dead");
    5106             : 
    5107          10 :         if( NULL == dbproc ) 
    5108             :                 return TRUE;
    5109             : 
    5110          10 :         if (IS_TDSDEAD(dbproc->tds_socket))
    5111             :                 return TRUE;
    5112             : 
    5113          10 :         return FALSE;
    5114             : }
    5115             : 
    5116             : /** \internal
    5117             :  * \ingroup dblib_internal
    5118             :  * \brief default error handler for db-lib (handles library-generated errors)
    5119             :  * 
    5120             :  * The default error handler doesn't print anything.  If you want to see your messages printed, 
    5121             :  * install an error handler.  If you think that should be an optional compile- or run-time default, 
    5122             :  * submit a patch.  It could be done.  
    5123             :  * 
    5124             :  * \sa DBDEAD(), dberrhandle().
    5125             :  */
    5126             : /* Thus saith Sybase:
    5127             :  *     "If the user does not supply an error handler (or passes a NULL pointer to 
    5128             :  *      dberrhandle), DB-Library will exhibit its default error-handling 
    5129             :  *      behavior: It will abort the program if the error has made the affected 
    5130             :  *      DBPROCESS unusable (the user can call DBDEAD to determine whether 
    5131             :  *      or not a DBPROCESS has become unusable). If the error has not made the 
    5132             :  *      DBPROCESS unusable, DB-Library will simply return an error code to its caller." 
    5133             :  *
    5134             :  * It is not the error handler, however, that aborts anything.  It is db-lib, cf. dbperror().  
    5135             :  */ 
    5136             : static int
    5137          10 : default_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
    5138             : {
    5139          10 :         tdsdump_log(TDS_DBG_FUNC, "default_err_handler %p, %d, %d, %d, %p, %p", dbproc, severity, dberr, oserr, dberrstr, oserrstr);
    5140             : 
    5141          10 :         if (DBDEAD(dbproc) && (!dbproc || !dbproc->msdblib)) {
    5142             :                 return INT_EXIT;
    5143             :         }
    5144             :         
    5145          10 :         if (!dbproc || !dbproc->msdblib) {   /* i.e. Sybase behavior */
    5146          10 :                 switch(dberr) {
    5147             :                 case SYBETIME:
    5148             :                         return INT_EXIT;
    5149             :                 default: 
    5150             :                         break;
    5151             :                 }
    5152           0 :         }
    5153          10 :         return INT_CANCEL;
    5154             : }
    5155             : 
    5156             : /**
    5157             :  * \ingroup dblib_core
    5158             :  * \brief Set an error handler, for messages from db-lib.
    5159             :  * 
    5160             :  * \param handler pointer to callback function that will handle errors.
    5161             :  *        Pass NULL to restore the default handler.  
    5162             :  * \return address of prior handler, or NULL if none was previously installed. 
    5163             :  * \sa DBDEAD(), dbmsghandle().
    5164             :  */
    5165             : EHANDLEFUNC
    5166        1052 : dberrhandle(EHANDLEFUNC handler)
    5167             : {
    5168        1052 :         EHANDLEFUNC old_handler = _dblib_err_handler;
    5169             : 
    5170        1052 :         tdsdump_log(TDS_DBG_FUNC, "dberrhandle(%p)\n", handler);
    5171             : 
    5172        1052 :         _dblib_err_handler = handler? handler : default_err_handler;
    5173             :         
    5174        1052 :         return (old_handler == default_err_handler)? NULL : old_handler;
    5175             : }
    5176             : 
    5177             : /**
    5178             :  * \ingroup dblib_core
    5179             :  * \brief Set a message handler, for messages from the server.
    5180             :  * 
    5181             :  * \param handler address of the function that will process the messages.
    5182             :  * \sa DBDEAD(), dberrhandle().
    5183             :  */
    5184             : MHANDLEFUNC
    5185        1072 : dbmsghandle(MHANDLEFUNC handler)
    5186             : {
    5187        1072 :         MHANDLEFUNC retFun = _dblib_msg_handler;
    5188             : 
    5189        1072 :         tdsdump_log(TDS_DBG_FUNC, "dbmsghandle(%p)\n", handler);
    5190             : 
    5191        1072 :         _dblib_msg_handler = handler;
    5192        1072 :         return retFun;
    5193             : }
    5194             : 
    5195             : #if defined(DBLIB_UNIMPLEMENTED)
    5196             : /**
    5197             :  * \ingroup dblib_money
    5198             :  * \brief Add two DBMONEY values.
    5199             :  * 
    5200             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5201             :  * \param m1 first operand.
    5202             :  * \param m2 other operand. 
    5203             :  * \param sum \em output: result of computation.  
    5204             :  * \retval SUCCEED Always.  
    5205             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5206             :  * \todo Unimplemented.
    5207             :  */
    5208             : RETCODE
    5209             : dbmnyadd(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * sum)
    5210             : {
    5211             :         tdsdump_log(TDS_DBG_FUNC, "dbmnyadd(%p, %p, %p, %p)\n", dbproc, m1, m2, sum);
    5212             :         CHECK_CONN(FAIL);
    5213             :         CHECK_NULP(m1,  "dbmnyadd", 2, FAIL);
    5214             :         CHECK_NULP(m2,  "dbmnyadd", 3, FAIL);
    5215             :         CHECK_NULP(sum, "dbmnyadd", 4, FAIL);
    5216             : 
    5217             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyadd()\n");
    5218             :         return SUCCEED;
    5219             : }
    5220             : 
    5221             : 
    5222             : /**
    5223             :  * \ingroup dblib_money
    5224             :  * \brief Subtract two DBMONEY values.
    5225             :  * 
    5226             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5227             :  * \param m1 first operand.
    5228             :  * \param m2 other operand, subtracted from \a m1. 
    5229             :  * \param difference \em output: result of computation.  
    5230             :  * \retval SUCCEED Always.  
    5231             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5232             :  * \todo Unimplemented.
    5233             :  */
    5234             : RETCODE
    5235             : dbmnysub(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * difference)
    5236             : {
    5237             :         tdsdump_log(TDS_DBG_FUNC, "dbmnysub(%p, %p, %p, %p)\n", dbproc, m1, m2, difference);
    5238             :         CHECK_CONN(FAIL);
    5239             :         CHECK_NULP(m1, "dbmnysub", 2, FAIL);
    5240             :         CHECK_NULP(m2, "dbmnysub", 3, FAIL);
    5241             :         CHECK_NULP(difference, "dbmnysub", 4, FAIL);
    5242             : 
    5243             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnysyb()\n");
    5244             :         return SUCCEED;
    5245             : }
    5246             : 
    5247             : /**
    5248             :  * \ingroup dblib_money
    5249             :  * \brief Multiply two DBMONEY values.
    5250             :  * 
    5251             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5252             :  * \param m1 first operand.
    5253             :  * \param m2 other operand. 
    5254             :  * \param prod \em output: result of computation.  
    5255             :  * \retval SUCCEED Always.  
    5256             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5257             :  * \todo Unimplemented.
    5258             :  */
    5259             : RETCODE
    5260             : dbmnymul(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * prod)
    5261             : {
    5262             :         tdsdump_log(TDS_DBG_FUNC, "dbmnymul(%p, %p, %p, %p)\n", dbproc, m1, m2, prod);
    5263             :         CHECK_CONN(FAIL);
    5264             :         CHECK_NULP(m1, "dbmnymul", 2, FAIL);
    5265             :         CHECK_NULP(m2, "dbmnymul", 3, FAIL);
    5266             :         CHECK_NULP(prod, "dbmnymul", 4, FAIL);
    5267             : 
    5268             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnymul()\n");
    5269             :         return SUCCEED;
    5270             : }
    5271             : 
    5272             : /**
    5273             :  * \ingroup dblib_money
    5274             :  * \brief Divide two DBMONEY values.
    5275             :  * 
    5276             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5277             :  * \param m1 dividend.
    5278             :  * \param m2 divisor. 
    5279             :  * \param quotient \em output: result of computation.  
    5280             :  * \retval SUCCEED Always.  
    5281             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5282             :  * \todo Unimplemented.
    5283             :  */
    5284             : RETCODE
    5285             : dbmnydivide(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * quotient)
    5286             : {
    5287             :         tdsdump_log(TDS_DBG_FUNC, "dbmnydivide(%p, %p, %p, %p)\n", dbproc, m1, m2, quotient);
    5288             :         CHECK_CONN(FAIL);
    5289             :         CHECK_NULP(m1, "dbmnydivide", 2, FAIL);
    5290             :         CHECK_NULP(m2, "dbmnydivide", 3, FAIL);
    5291             :         CHECK_NULP(quotient, "dbmnydivide", 4, FAIL);
    5292             : 
    5293             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnydivide()\n");
    5294             :         return SUCCEED;
    5295             : }
    5296             : #endif
    5297             : 
    5298             : /**
    5299             :  * \ingroup dblib_money
    5300             :  * \brief Compare two DBMONEY values.
    5301             :  * 
    5302             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5303             :  * \param m1 some money.
    5304             :  * \param m2 some other money. 
    5305             :  * \retval 0 m1 == m2. 
    5306             :  * \retval -1 m1 < m2.
    5307             :  * \retval  1 m1 > m2.
    5308             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5309             :  */
    5310             : int
    5311           0 : dbmnycmp(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2)
    5312             : {
    5313           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnycmp(%p, %p, %p)\n", dbproc, m1, m2);
    5314           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    5315           0 :         CHECK_NULP(m1, "dbmnycmp", 2, 0);
    5316           0 :         CHECK_NULP(m2, "dbmnycmp", 3, 0);
    5317             : 
    5318           0 :         if (m1->mnyhigh < m2->mnyhigh) {
    5319             :                 return -1;
    5320             :         }
    5321           0 :         if (m1->mnyhigh > m2->mnyhigh) {
    5322             :                 return 1;
    5323             :         }
    5324           0 :         if (m1->mnylow < m2->mnylow) {
    5325             :                 return -1;
    5326             :         }
    5327           0 :         if (m1->mnylow > m2->mnylow) {
    5328             :                 return 1;
    5329             :         }
    5330           0 :         return 0;
    5331             : }
    5332             : 
    5333             : #if defined(DBLIB_UNIMPLEMENTED)
    5334             : /**
    5335             :  * \ingroup dblib_money
    5336             :  * \brief Multiply a DBMONEY value by a positive integer, and add an amount. 
    5337             :  * 
    5338             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5339             :  * \param amount starting amount of money, also holds output.
    5340             :  * \param multiplier amount to multiply \a amount by. 
    5341             :  * \param addend amount to add to \a amount, after multiplying by \a multiplier. 
    5342             :  * \retval SUCCEED Always.  
    5343             :  * \remarks This function is goofy.  
    5344             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5345             :  * \todo Unimplemented.
    5346             :  */
    5347             : RETCODE
    5348             : dbmnyscale(DBPROCESS * dbproc, DBMONEY * amount, int multiplier, int addend)
    5349             : {
    5350             :         tdsdump_log(TDS_DBG_FUNC, "dbmnyscale(%p, %p, %d, %d)\n", dbproc, amount, multiplier, addend);
    5351             :         CHECK_CONN(FAIL);
    5352             :         CHECK_NULP(amount, "dbmnyscale", 2, FAIL);
    5353             : 
    5354             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyscale()\n");
    5355             :         return SUCCEED;
    5356             : }
    5357             : #endif
    5358             : 
    5359             : /**
    5360             :  * \ingroup dblib_money
    5361             :  * \brief Set a DBMONEY value to zero.
    5362             :  * 
    5363             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5364             :  * \param dest address of a DBMONEY structure.  
    5365             :  * \retval SUCCEED unless \a amount is NULL.  
    5366             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5367             :  */
    5368             : RETCODE
    5369           0 : dbmnyzero(DBPROCESS * dbproc, DBMONEY * dest)
    5370             : {
    5371           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnyzero(%p, %p)\n", dbproc, dest);
    5372           0 :         CHECK_CONN(FAIL);
    5373           0 :         CHECK_NULP(dest, "dbmnyzero", 2, FAIL);
    5374             : 
    5375           0 :         dest->mnylow = 0;
    5376           0 :         dest->mnyhigh = 0;
    5377           0 :         return SUCCEED;
    5378             : }
    5379             : 
    5380             : /**
    5381             :  * \ingroup dblib_money
    5382             :  * \brief Get maximum positive DBMONEY value supported.
    5383             :  * 
    5384             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5385             :  * \param amount address of a DBMONEY structure.  
    5386             :  * \retval SUCCEED Always.  
    5387             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5388             :  */
    5389             : RETCODE
    5390           0 : dbmnymaxpos(DBPROCESS * dbproc, DBMONEY * amount)
    5391             : {
    5392           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnymaxpos(%p, %p)\n", dbproc, amount);
    5393           0 :         CHECK_CONN(FAIL);
    5394           0 :         CHECK_NULP(amount, "dbmnymaxpos", 2, FAIL);
    5395             : 
    5396           0 :         amount->mnylow = 0xFFFFFFFFlu;
    5397           0 :         amount->mnyhigh = 0x7FFFFFFFl;
    5398           0 :         return SUCCEED;
    5399             : }
    5400             : 
    5401             : /**
    5402             :  * \ingroup dblib_money
    5403             :  * \brief Get maximum negative DBMONEY value supported.
    5404             :  * 
    5405             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5406             :  * \param amount address of a DBMONEY structure.  
    5407             :  * \retval SUCCEED Always.  
    5408             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5409             :  */
    5410             : RETCODE
    5411           0 : dbmnymaxneg(DBPROCESS * dbproc, DBMONEY * amount)
    5412             : {
    5413           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnymaxneg(%p, %p)\n", dbproc, amount);
    5414           0 :         CHECK_CONN(FAIL);
    5415           0 :         CHECK_NULP(amount, "dbmnymaxneg", 2, FAIL);
    5416             : 
    5417           0 :         amount->mnylow = 0;
    5418           0 :         amount->mnyhigh = -0x7FFFFFFFL - 1;
    5419           0 :         return SUCCEED;
    5420             : }
    5421             : 
    5422             : #if defined(DBLIB_UNIMPLEMENTED)
    5423             : /**
    5424             :  * \ingroup dblib_money
    5425             :  * \brief Get the least significant digit of a DBMONEY value, represented as a character.
    5426             :  * 
    5427             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5428             :  * \param mnyptr \em input the money amount, \em and \em output: \a mnyptr divided by 10.  
    5429             :  * \param digit the character value (between '0' and '9') of the rightmost digit in \a mnyptr.  
    5430             :  * \param zero \em output: \c TRUE if \a mnyptr is zero on output, else \c FALSE.  
    5431             :  * \retval SUCCEED Always.  
    5432             :  * \sa dbconvert(), dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5433             :  * \remarks Unimplemented and likely to remain so.  We'd be amused to learn anyone wants this function.  
    5434             :  * \todo Unimplemented.
    5435             :  */
    5436             : RETCODE
    5437             : dbmnyndigit(DBPROCESS * dbproc, DBMONEY * mnyptr, DBCHAR * digit, DBBOOL * zero)
    5438             : {
    5439             :         tdsdump_log(TDS_DBG_FUNC, "dbmnyndigit(%p, %p, %s, %p)\n", dbproc, mnyptr, digit, zero);
    5440             :         CHECK_CONN(FAIL);
    5441             :         CHECK_NULP(mnyptr, "dbmnyndigit", 2, FAIL);
    5442             :         CHECK_NULP(digit, "dbmnyndigit", 3, FAIL);
    5443             :         CHECK_NULP(zero, "dbmnyndigit", 4, FAIL);
    5444             : 
    5445             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyndigit()\n");
    5446             :         return SUCCEED;
    5447             : }
    5448             : 
    5449             : /**
    5450             :  * \ingroup dblib_money
    5451             :  * \brief Prepare a DBMONEY value for use with dbmnyndigit().
    5452             :  * 
    5453             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5454             :  * \param amount address of a DBMONEY structure.  
    5455             :  * \param trim number of digits to trim from \a amount.
    5456             :  * \param negative \em output: \c TRUE if \a amount < 0.  
    5457             :  * \retval SUCCEED Always.  
    5458             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5459             :  * \todo Unimplemented.
    5460             :  */
    5461             : RETCODE
    5462             : dbmnyinit(DBPROCESS * dbproc, DBMONEY * amount, int trim, DBBOOL * negative)
    5463             : {
    5464             :         tdsdump_log(TDS_DBG_FUNC, "dbmnyinit(%p, %p, %d, %p)\n", dbproc, amount, trim, negative);
    5465             :         CHECK_CONN(FAIL);
    5466             :         CHECK_NULP(amount, "dbmnyinit", 2, FAIL);
    5467             :         CHECK_NULP(negative, "dbmnyinit", 4, FAIL);
    5468             : 
    5469             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyinit()\n");
    5470             :         return SUCCEED;
    5471             : }
    5472             : 
    5473             : 
    5474             : /**
    5475             :  * \ingroup dblib_money
    5476             :  * \brief Divide a DBMONEY value by a positive integer.
    5477             :  * 
    5478             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5479             :  * \param amount address of a DBMONEY structure.  
    5480             :  * \param divisor of \a amount.
    5481             :  * \param remainder \em output: modulo of integer division.
    5482             :  * \retval SUCCEED Always.  
    5483             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5484             :  * \todo Unimplemented.
    5485             :  */
    5486             : RETCODE
    5487             : dbmnydown(DBPROCESS * dbproc, DBMONEY * amount, int divisor, int *remainder)
    5488             : {
    5489             :         tdsdump_log(TDS_DBG_FUNC, "dbmnydown(%p, %p, %d, %p)\n", dbproc, amount, divisor, remainder);
    5490             :         CHECK_CONN(FAIL);
    5491             :         CHECK_NULP(amount, "dbmnydown", 2, FAIL);
    5492             :         CHECK_NULP(remainder, "dbmnydown", 4, FAIL);
    5493             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnydown()\n");
    5494             :         return SUCCEED;
    5495             : }
    5496             : #endif
    5497             : 
    5498             : /**
    5499             :  * \ingroup dblib_money
    5500             :  * \brief Add $0.0001 to a DBMONEY value.
    5501             :  * 
    5502             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5503             :  * \param amount address of a DBMONEY structure.  
    5504             :  * \retval SUCCEED or FAIL if overflow or amount NULL.  
    5505             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5506             :  */
    5507             : RETCODE
    5508           0 : dbmnyinc(DBPROCESS * dbproc, DBMONEY * amount)
    5509             : {
    5510           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnyinc(%p, %p)\n", dbproc, amount);
    5511           0 :         CHECK_CONN(FAIL);
    5512           0 :         CHECK_NULP(amount, "dbmnyinc", 2, FAIL);
    5513             : 
    5514           0 :         if (amount->mnylow != 0xFFFFFFFFlu) {
    5515           0 :                 ++amount->mnylow;
    5516           0 :                 return SUCCEED;
    5517             :         }
    5518           0 :         if (amount->mnyhigh == 0x7FFFFFFFl)
    5519             :                 return FAIL;
    5520           0 :         amount->mnylow = 0;
    5521           0 :         ++amount->mnyhigh;
    5522           0 :         return SUCCEED;
    5523             : }
    5524             : 
    5525             : 
    5526             : /**
    5527             :  * \ingroup dblib_money
    5528             :  * \brief Subtract $0.0001 from a DBMONEY value.
    5529             :  *
    5530             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5531             :  * \param amount address of a DBMONEY structure.  
    5532             :  * \retval SUCCEED or FAIL if overflow or amount NULL.  
    5533             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5534             :  */
    5535             : RETCODE
    5536           0 : dbmnydec(DBPROCESS * dbproc, DBMONEY * amount)
    5537             : {
    5538           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnydec(%p, %p)\n", dbproc, amount);
    5539           0 :         CHECK_CONN(FAIL);
    5540           0 :         CHECK_NULP(amount, "dbmnydec", 2, FAIL);
    5541             : 
    5542           0 :         if (amount->mnylow != 0) {
    5543           0 :                 --amount->mnylow;
    5544           0 :                 return SUCCEED;
    5545             :         }
    5546           0 :         if (amount->mnyhigh == -0x7FFFFFFFL - 1)
    5547             :                 return FAIL;
    5548           0 :         amount->mnylow = 0xFFFFFFFFlu;
    5549           0 :         --amount->mnyhigh;
    5550           0 :         return SUCCEED;
    5551             : }
    5552             : 
    5553             : /**
    5554             :  * \ingroup dblib_money
    5555             :  * \brief Negate a DBMONEY value.
    5556             :  * 
    5557             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5558             :  * \param src address of a DBMONEY structure.  
    5559             :  * \param dest \em output: result of negation. 
    5560             :  * \retval SUCCEED or FAIL if overflow or src/dest NULL.
    5561             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5562             :  */
    5563             : RETCODE
    5564           0 : dbmnyminus(DBPROCESS * dbproc, DBMONEY * src, DBMONEY * dest)
    5565             : {
    5566           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnyminus(%p, %p, %p)\n", dbproc, src, dest);
    5567           0 :         CHECK_CONN(FAIL);
    5568           0 :         CHECK_NULP(src, "dbmnyminus", 2, FAIL);
    5569           0 :         CHECK_NULP(dest, "dbmnyminus", 3, FAIL);
    5570             : 
    5571           0 :         if (src->mnyhigh == -0x7FFFFFFFL - 1  &&  src->mnylow == 0)
    5572             :                 return FAIL;
    5573           0 :         dest->mnyhigh = -src->mnyhigh;
    5574           0 :         dest->mnylow = (~src->mnylow) + 1u;
    5575           0 :         return SUCCEED;
    5576             : }
    5577             : 
    5578             : 
    5579             : /**
    5580             :  * \ingroup dblib_money
    5581             :  * \brief Negate a DBMONEY4 value.
    5582             :  * 
    5583             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5584             :  * \param src address of a DBMONEY4 structure.  
    5585             :  * \param dest \em output: result of negation. 
    5586             :  * \retval SUCCEED usually.  
    5587             :  * \retval FAIL  on overflow.  
    5588             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5589             :  */
    5590             : RETCODE
    5591           0 : dbmny4minus(DBPROCESS * dbproc, DBMONEY4 * src, DBMONEY4 * dest)
    5592             : {
    5593             :         DBMONEY4 zero;
    5594             : 
    5595           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4minus(%p, %p, %p)\n", dbproc, src, dest);
    5596           0 :         CHECK_CONN(FAIL);
    5597           0 :         CHECK_NULP(src, "dbmny4minus", 2, FAIL);
    5598           0 :         CHECK_NULP(dest, "dbmny4minus", 3, FAIL);
    5599             : 
    5600           0 :         dbmny4zero(dbproc, &zero);
    5601           0 :         return (dbmny4sub(dbproc, &zero, src, dest));
    5602             : }
    5603             : 
    5604             : /**
    5605             :  * \ingroup dblib_money
    5606             :  * \brief Zero a DBMONEY4 value.
    5607             :  * 
    5608             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5609             :  * \param dest address of a DBMONEY structure.  
    5610             :  * \retval SUCCEED usually.  
    5611             :  * \retval FAIL  \a dest is NULL.  
    5612             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5613             :  */
    5614             : RETCODE
    5615           0 : dbmny4zero(DBPROCESS * dbproc, DBMONEY4 * dest)
    5616             : {
    5617           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4zero(%p, %p)\n", dbproc, dest);
    5618           0 :         CHECK_CONN(FAIL);
    5619           0 :         CHECK_NULP(dest, "dbmny4zero", 2, FAIL);
    5620             : 
    5621           0 :         dest->mny4 = 0;
    5622           0 :         return SUCCEED;
    5623             : }
    5624             : 
    5625             : /**
    5626             :  * \ingroup dblib_money
    5627             :  * \brief Add two DBMONEY4 values.
    5628             :  * 
    5629             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5630             :  * \param m1 first operand.
    5631             :  * \param m2 other operand. 
    5632             :  * \param sum \em output: result of computation.  
    5633             :  * \retval SUCCEED usually.  
    5634             :  * \retval FAIL  on overflow.  
    5635             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5636             :  */
    5637             : RETCODE
    5638           0 : dbmny4add(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * sum)
    5639             : {
    5640           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4add(%p, %p, %p, %p)\n", dbproc, m1, m2, sum);
    5641           0 :         CHECK_CONN(FAIL);
    5642           0 :         CHECK_NULP(m1, "dbmny4add", 2, FAIL);
    5643           0 :         CHECK_NULP(m2, "dbmny4add", 3, FAIL);
    5644           0 :         CHECK_NULP(sum, "dbmny4add", 4, FAIL);
    5645             : 
    5646           0 :         sum->mny4 = m1->mny4 + m2->mny4;
    5647           0 :         if (((m1->mny4 < 0) && (m2->mny4 < 0) && (sum->mny4 >= 0))
    5648           0 :             || ((m1->mny4 > 0) && (m2->mny4 > 0) && (sum->mny4 <= 0))) {
    5649             :                 /* overflow */
    5650           0 :                 sum->mny4 = 0;
    5651           0 :                 return FAIL;
    5652             :         }
    5653             :         return SUCCEED;
    5654             : }
    5655             : 
    5656             : /**
    5657             :  * \ingroup dblib_money
    5658             :  * \brief Subtract two DBMONEY4 values.
    5659             :  * 
    5660             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5661             :  * \param m1 first operand.
    5662             :  * \param m2 other operand, subtracted from \a m1. 
    5663             :  * \param diff \em output: result of computation.  
    5664             :  * \retval SUCCEED usually.  
    5665             :  * \retval FAIL  on overflow.  
    5666             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5667             :  */
    5668             : RETCODE
    5669           0 : dbmny4sub(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * diff)
    5670             : {
    5671             : 
    5672           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4sub(%p, %p, %p, %p)\n", dbproc, m1, m2, diff);
    5673           0 :         CHECK_CONN(FAIL);
    5674           0 :         CHECK_NULP(m1, "dbmny4sub", 2, FAIL);
    5675           0 :         CHECK_NULP(m2, "dbmny4sub", 3, FAIL);
    5676           0 :         CHECK_NULP(diff, "dbmny4sub", 4, FAIL);
    5677             : 
    5678           0 :         diff->mny4 = m1->mny4 - m2->mny4;
    5679           0 :         if (((m1->mny4 <= 0) && (m2->mny4 > 0) && (diff->mny4 > 0))
    5680           0 :             || ((m1->mny4 >= 0) && (m2->mny4 < 0) && (diff->mny4 < 0))) {
    5681             :                 /* overflow */
    5682           0 :                 diff->mny4 = 0;
    5683           0 :                 return FAIL;
    5684             :         }
    5685             :         return SUCCEED;
    5686             : }
    5687             : 
    5688             : #if defined(DBLIB_UNIMPLEMENTED)
    5689             : /**
    5690             :  * \ingroup dblib_money
    5691             :  * \brief Multiply two DBMONEY4 values.
    5692             :  * 
    5693             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5694             :  * \param m1 first operand.
    5695             :  * \param m2 other operand. 
    5696             :  * \param prod \em output: result of computation.  
    5697             :  * \retval SUCCEED usually.  
    5698             :  * \retval FAIL a parameter is NULL.  
    5699             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5700             :  * \todo Unimplemented.
    5701             :  */
    5702             : RETCODE
    5703             : dbmny4mul(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * prod)
    5704             : {
    5705             : 
    5706             :         tdsdump_log(TDS_DBG_FUNC, "dbmny4mul(%p, %p, %p, %p)\n", dbproc, m1, m2, prod);
    5707             :         CHECK_CONN(FAIL);
    5708             :         CHECK_NULP(m1, "dbmny4mul", 2, FAIL);
    5709             :         CHECK_NULP(m2, "dbmny4mul", 3, FAIL);
    5710             :         CHECK_NULP(prod, "dbmny4mul", 4, FAIL);
    5711             : 
    5712             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmny4mul()\n");
    5713             :         return FAIL;
    5714             : }
    5715             : 
    5716             : /**
    5717             :  * \ingroup dblib_money
    5718             :  * \brief Divide two DBMONEY4 values.
    5719             :  * 
    5720             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5721             :  * \param m1 dividend.
    5722             :  * \param m2 divisor. 
    5723             :  * \param quotient \em output: result of computation.  
    5724             :  * \retval SUCCEED usually.  
    5725             :  * \retval FAIL a parameter is NULL.  
    5726             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5727             :  * \todo Unimplemented.
    5728             :  */
    5729             : RETCODE
    5730             : dbmny4divide(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * quotient)
    5731             : {
    5732             : 
    5733             :         tdsdump_log(TDS_DBG_FUNC, "dbmny4divide(%p, %p, %p, %p)\n", dbproc, m1, m2, quotient);
    5734             :         CHECK_CONN(FAIL);
    5735             :         CHECK_NULP(m1, "dbmny4divide", 2, FAIL);
    5736             :         CHECK_NULP(m2, "dbmny4divide", 3, FAIL);
    5737             :         CHECK_NULP(quotient, "dbmny4divide", 4, FAIL);
    5738             : 
    5739             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmny4divide()\n");
    5740             :         return FAIL;
    5741             : }
    5742             : #endif
    5743             : 
    5744             : /**
    5745             :  * \ingroup dblib_money
    5746             :  * \brief Compare two DBMONEY4 values.
    5747             :  * 
    5748             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5749             :  * \param m1 some money.
    5750             :  * \param m2 some other money. 
    5751             :  * \retval 0 m1 == m2. 
    5752             :  * \retval -1 m1 < m2.
    5753             :  * \retval  1 m1 > m2.
    5754             :  * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
    5755             :  */
    5756             : int
    5757           0 : dbmny4cmp(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2)
    5758             : {
    5759             : 
    5760           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4cmp(%p, %p, %p)\n", dbproc, m1, m2);
    5761           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    5762           0 :         CHECK_NULP(m1, "dbmny4cmp", 2, 0);
    5763           0 :         CHECK_NULP(m2, "dbmny4cmp", 3, 0);
    5764             : 
    5765           0 :         if (m1->mny4 < m2->mny4) {
    5766             :                 return -1;
    5767             :         }
    5768           0 :         if (m1->mny4 > m2->mny4) {
    5769             :                 return 1;
    5770             :         }
    5771           0 :         return 0;
    5772             : }
    5773             : 
    5774             : /**
    5775             :  * \ingroup dblib_money
    5776             :  * \brief Copy a DBMONEY4 value.
    5777             :  * 
    5778             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5779             :  * \param src address of a DBMONEY4 structure.  
    5780             :  * \param dest \em output: new money. 
    5781             :  * \retval SUCCEED or FAIL if src/dest NULL.
    5782             :  * \sa dbmnycopy(), dbmnyminus(), dbmny4minus(). 
    5783             :  */
    5784             : RETCODE
    5785           0 : dbmny4copy(DBPROCESS * dbproc, DBMONEY4 * src, DBMONEY4 * dest)
    5786             : {
    5787             : 
    5788           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmny4copy(%p, %p, %p)\n", dbproc, src, dest);
    5789           0 :         CHECK_CONN(FAIL);
    5790           0 :         CHECK_NULP(src, "dbmny4copy", 2, FAIL);
    5791           0 :         CHECK_NULP(dest, "dbmny4copy", 3, FAIL);
    5792             : 
    5793           0 :         dest->mny4 = src->mny4;
    5794           0 :         return SUCCEED;
    5795             : }
    5796             : 
    5797             : /**
    5798             :  * \ingroup dblib_datetime
    5799             :  * \brief Compare DBDATETIME values, similar to strcmp(3).
    5800             :  * 
    5801             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5802             :  * \param d1 a \c DBDATETIME structure address
    5803             :  * \param d2 another \c DBDATETIME structure address
    5804             :  * \retval   0 d1 = d2.
    5805             :  * \retval  -1 d1 < d2.
    5806             :  * \retval   1 d1 > d2.
    5807             :  * \sa dbdate4cmp(), dbmnycmp(), dbmny4cmp().
    5808             :  */
    5809             : int
    5810           0 : dbdatecmp(DBPROCESS * dbproc, DBDATETIME * d1, DBDATETIME * d2)
    5811             : {
    5812           0 :         tdsdump_log(TDS_DBG_FUNC, "dbdatecmp(%p, %p, %p)\n", dbproc, d1, d2);
    5813           0 :         CHECK_CONN(FAIL);
    5814           0 :         CHECK_NULP(d1, "dbdatecmp", 2, 0);
    5815           0 :         CHECK_NULP(d2, "dbdatecmp", 3, 0);
    5816             : 
    5817           0 :         if (d1->dtdays == d2->dtdays) {
    5818           0 :                 if (d1->dttime == d2->dttime)
    5819             :                         return 0;
    5820           0 :                 return d1->dttime > d2->dttime ? 1 : -1;
    5821             :         }
    5822             : 
    5823             :         /* date 1 is before 1900 */
    5824           0 :         if (d1->dtdays > 2958463) {
    5825             : 
    5826           0 :                 if (d2->dtdays > 2958463) /* date 2 is before 1900 */
    5827           0 :                         return d1->dtdays > d2->dtdays ? 1 : -1;
    5828             :                 return -1;
    5829             :         }
    5830             : 
    5831             :         /* date 1 is after 1900 */
    5832           0 :         if (d2->dtdays < 2958463) /* date 2 is after 1900 */
    5833           0 :                 return d1->dtdays > d2->dtdays ? 1 : -1;
    5834             :         return 1;
    5835             : }
    5836             : 
    5837             : static RETCODE
    5838          24 : dblib_datecrack(DBPROCESS * dbproc, BOOL nano_precision, DBDATEREC * output, int type, const void * data)
    5839             : {
    5840             :         TDSDATEREC dr;
    5841          24 :         struct tds_sybase_dbdaterec *di = (struct tds_sybase_dbdaterec*) output;
    5842             : 
    5843          24 :         tdsdump_log(TDS_DBG_FUNC, "dblib_datecrack(%p, %d, %p, %d, %p)\n", dbproc, nano_precision, output, type, data);
    5844          24 :         CHECK_NULP(output, "dbdatecrack", 2, FAIL);
    5845          24 :         CHECK_PARAMETER(data, SYBENDTP, FAIL);
    5846             : 
    5847          24 :         if (TDS_FAILED(tds_datecrack(type, data, &dr)))
    5848             :                 return FAIL;
    5849             : 
    5850          24 :         di->dateyear = dr.year;
    5851          24 :         di->quarter = dr.quarter;
    5852          24 :         di->datemonth = dr.month;
    5853          24 :         di->datedmonth = dr.day;
    5854          24 :         di->datedyear = dr.dayofyear;
    5855          24 :         di->datedweek = dr.weekday;
    5856          24 :         di->datehour = dr.hour;
    5857          24 :         di->dateminute = dr.minute;
    5858          24 :         di->datesecond = dr.second;
    5859          24 :         di->datetzone = dr.timezone;
    5860          24 :         if (nano_precision)
    5861             :                 /* here we are writing to nanosecond field */
    5862           4 :                 di->datemsecond = dr.decimicrosecond * 100u;
    5863             :         else
    5864          20 :                 di->datemsecond = dr.decimicrosecond / 10000u;
    5865             :         /* Revert to compiled-in default if dbproc can't be used to find the runtime override. */
    5866          24 :         if (dbproc ? dbproc->msdblib : dblib_msdblib) {
    5867           0 :                 ++di->quarter;
    5868           0 :                 ++di->datemonth;
    5869           0 :                 ++di->datedweek;
    5870             :         }
    5871             :         return SUCCEED;
    5872             : }
    5873             : 
    5874             : /**
    5875             :  * \ingroup dblib_core
    5876             :  * \brief Break a DBDATETIME value into useful pieces.
    5877             :  *
    5878             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5879             :  * \param di \em output: structure to contain the exploded parts of \a datetime.
    5880             :  * \param datetime \em input: \c DBDATETIME to be converted.
    5881             :  * \retval SUCCEED always.
    5882             :  * \remarks The members of \a di have different names, depending on whether \c --with-msdblib was configured.
    5883             :  *
    5884             :  * If DBPROCESS is NULL, dbdatecrack() uses the compiled in default
    5885             :  * value of MSDBLIB as of when libsybdb was compiled, irrespective of its value when the
    5886             :  * application is compiled.  This can lead to incorrect results because Sybase and Microsoft use different
    5887             :  * ranges -- [0,11] vs. [1,12] -- for the month.
    5888             :  *
    5889             :  * \sa dbconvert(), dbdata(), dbdatechar(), dbdatename(), dbdatepart(), tdsdbopen().
    5890             :  */
    5891             : RETCODE
    5892          20 : dbdatecrack(DBPROCESS * dbproc, DBDATEREC * di, DBDATETIME * datetime)
    5893             : {
    5894          20 :         return dblib_datecrack(dbproc, FALSE, di, SYBDATETIME, datetime);
    5895             : }
    5896             : 
    5897             : /**
    5898             :  * \ingroup dblib_core
    5899             :  * \brief Break any kind of date or time value into useful pieces.
    5900             :  *
    5901             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5902             :  * \param di \em output: structure to contain the exploded parts of \a datetime.
    5903             :  * \param type \em input: \c type of date/time value returned by dbcoltype().
    5904             :  * \param data \em input: \c date/time value to be converted.
    5905             :  * \retval SUCCEED always.
    5906             :  * \remarks The members of \a di have different names, depending on whether \c --with-msdblib was configured.
    5907             :  *
    5908             :  * This is an extension to dbdatecrack(), see it for more information.
    5909             :  *
    5910             :  * \sa dbdatecrack(), dbconvert(), dbdata(), dbdatechar(), dbdatename(), dbdatepart(), tdsdbopen().
    5911             :  */
    5912             : RETCODE
    5913           4 : dbanydatecrack(DBPROCESS * dbproc, DBDATEREC2 * di, int type, const void *data)
    5914             : {
    5915           4 :         return dblib_datecrack(dbproc, TRUE, (DBDATEREC *) di, type, data);
    5916             : }
    5917             : 
    5918             : #if defined(DBLIB_UNIMPLEMENTED)
    5919             : /**
    5920             :  * \ingroup dblib_core
    5921             :  * \brief Clear remote passwords from the LOGINREC structure.
    5922             :  * 
    5923             :  * \param login structure to pass to dbopen().
    5924             :  * \sa dblogin(), dbopen(), dbrpwset(), DBSETLAPP(), DBSETLHOST(), DBSETLPWD(), DBSETLUSER().  
    5925             :  * \remarks Useful for remote stored procedure calls, but not in high demand from FreeTDS.  
    5926             :  * \todo Unimplemented.
    5927             :  */
    5928             : void
    5929             : dbrpwclr(LOGINREC * login)
    5930             : {
    5931             :         tdsdump_log(TDS_DBG_FUNC, "dbrpwclr(%p)\n", login);
    5932             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbrpwclr()\n");
    5933             : }
    5934             : 
    5935             : /**
    5936             :  * \ingroup dblib_core
    5937             :  * \brief Add a remote password to the LOGINREC structure.
    5938             :  * 
    5939             :  * \param login structure to pass to dbopen().
    5940             :  * \param srvname server for which \a password should be used.  
    5941             :  * \param password you guessed it, let's hope no else does.  
    5942             :  * \param pwlen count of \a password, in bytes.
    5943             :  * \remarks Useful for remote stored procedure calls, but not in high demand from FreeTDS.  
    5944             :  * \sa dblogin(), dbopen(), dbrpwclr(), DBSETLAPP(), DBSETLHOST(), DBSETLPWD(), DBSETLUSER().  
    5945             :  * \todo Unimplemented.
    5946             :  */
    5947             : RETCODE
    5948             : dbrpwset(LOGINREC * login, char *srvname, char *password, int pwlen)
    5949             : {
    5950             :         tdsdump_log(TDS_DBG_FUNC, "dbrpwset(%p, %s, %s, %d)\n", login, srvname, password, pwlen);
    5951             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbrpwset()\n");
    5952             :         return SUCCEED;
    5953             : }
    5954             : #endif
    5955             : 
    5956             : /**
    5957             :  * \ingroup dblib_core
    5958             :  * \brief Get server process ID for a \c DBPROCESS.
    5959             :  * 
    5960             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5961             :  * \return \em "spid", the server's process ID.  
    5962             :  * \sa dbopen().  
    5963             :  */
    5964             : int
    5965          10 : dbspid(DBPROCESS * dbproc)
    5966             : {
    5967          10 :         tdsdump_log(TDS_DBG_FUNC, "dbspid(%p)\n", dbproc);
    5968          10 :         CHECK_CONN(-1);
    5969             : 
    5970          10 :         return dbproc->tds_socket->conn->spid;
    5971             : }
    5972             : 
    5973             : /**
    5974             :  * \ingroup dblib_core
    5975             :  * \brief Associate client-allocated (and defined) data with a \c DBPROCESS.   
    5976             :  * 
    5977             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5978             :  * \param ptr address of client-defined data.
    5979             :  * \remarks \a ptr is the location of user data that \c db-lib will associate with \a dbproc. 
    5980             :  * The client allocates the buffer addressed by \a ptr.  \c db-lib never examines or uses the information; 
    5981             :  * it just stashes the pointer for later retrieval by the application with \c dbgetuserdata().  
    5982             :  * \sa dbgetuserdata().  
    5983             :  */
    5984             : void
    5985         262 : dbsetuserdata(DBPROCESS * dbproc, BYTE * ptr)
    5986             : {
    5987         262 :         tdsdump_log(TDS_DBG_FUNC, "dbsetuserdata(%p, %p)\n", dbproc, ptr);
    5988         262 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    5989             : 
    5990         262 :         dbproc->user_data = ptr;
    5991             : }
    5992             : 
    5993             : /**
    5994             :  * \ingroup dblib_core
    5995             :  * \brief Get address of user-allocated data from a \c DBPROCESS.   
    5996             :  * 
    5997             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    5998             :  * \return address of user-defined data that \c db-lib associated with \a dbproc when the client called  dbsetuserdata(). 
    5999             :  * \retval undefined (probably \c NULL) dbsetuserdata() was not previously called.  
    6000             :  * \sa dbsetuserdata().  
    6001             :  */
    6002             : BYTE *
    6003         196 : dbgetuserdata(DBPROCESS * dbproc)
    6004             : {
    6005         196 :         tdsdump_log(TDS_DBG_FUNC, "dbgetuserdata(%p)\n", dbproc);
    6006         196 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    6007             : 
    6008         196 :         return dbproc->user_data;
    6009             : }
    6010             : 
    6011             : /**
    6012             :  * \ingroup dblib_core
    6013             :  * \brief Specify a db-lib version level.
    6014             :  * 
    6015             :  * \param version anything, really. 
    6016             :  * \retval SUCCEED Always.  
    6017             :  * \remarks No effect on behavior of \c db-lib in \c FreeTDS.  
    6018             :  * \sa 
    6019             :  */
    6020             : RETCODE
    6021          40 : dbsetversion(DBINT version)
    6022             : {
    6023          40 :         tdsdump_log(TDS_DBG_FUNC, "dbsetversion(%d)\n", version);
    6024             : 
    6025          40 :         switch (version) {
    6026          40 :         case DBVERSION_42:
    6027             :         case DBVERSION_46:
    6028             :         case DBVERSION_100:
    6029             :         case DBVERSION_70:
    6030             :         case DBVERSION_71:
    6031             :         case DBVERSION_72:
    6032             :         case DBVERSION_73:
    6033             :         case DBVERSION_74:
    6034          40 :                 g_dblib_version = version;
    6035          40 :                 return SUCCEED;
    6036             :         default:
    6037             :                 break;
    6038             :         }
    6039             :         
    6040           0 :         dbperror(NULL, SYBEIVERS, 0);
    6041           0 :         return FAIL;
    6042             : }
    6043             : 
    6044             : /**
    6045             :  * \ingroup dblib_money
    6046             :  * \brief Copy a DBMONEY value.
    6047             :  * 
    6048             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6049             :  * \param src address of a DBMONEY structure.  
    6050             :  * \param dest \em output: new money. 
    6051             :  * \retval SUCCEED always, unless \a src or \a dest is \c NULL.  
    6052             :  * \sa 
    6053             :  */
    6054             : RETCODE
    6055           0 : dbmnycopy(DBPROCESS * dbproc, DBMONEY * src, DBMONEY * dest)
    6056             : {
    6057           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmnycopy(%p, %p, %p)\n", dbproc, src, dest);
    6058           0 :         CHECK_CONN(FAIL);
    6059           0 :         CHECK_NULP(src, "dbmnycopy", 2, FAIL);
    6060           0 :         CHECK_NULP(dest, "dbmnycopy", 3, FAIL);
    6061             : 
    6062           0 :         dest->mnylow = src->mnylow;
    6063           0 :         dest->mnyhigh = src->mnyhigh;
    6064           0 :         return SUCCEED;
    6065             : }
    6066             : 
    6067             : 
    6068             : /**
    6069             :  * \ingroup dblib_core
    6070             :  * \brief Cancel the query currently being retrieved, discarding all pending rows.  
    6071             :  * 
    6072             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6073             :  * \sa 
    6074             :  */
    6075             : RETCODE
    6076         814 : dbcanquery(DBPROCESS * dbproc)
    6077             : {
    6078             :         TDSRET rc;
    6079             :         TDS_INT result_type;
    6080             : 
    6081         814 :         tdsdump_log(TDS_DBG_FUNC, "dbcanquery(%p)\n", dbproc);
    6082         814 :         CHECK_CONN(FAIL);
    6083             : 
    6084             :         /* Just throw away all pending rows from the last query */
    6085             : 
    6086         814 :         rc = tds_process_tokens(dbproc->tds_socket, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE);
    6087             : 
    6088         814 :         if (TDS_FAILED(rc))
    6089             :                 return FAIL;
    6090             : 
    6091         814 :         dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
    6092             : 
    6093         814 :         return SUCCEED;
    6094             : }
    6095             : 
    6096             : /**
    6097             :  * \ingroup dblib_core
    6098             :  * \brief Erase the command buffer, in case \c DBNOAUTOFREE was set with dbsetopt(). 
    6099             :  * 
    6100             :  * 
    6101             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6102             :  * \sa dbcmd(), dbfcmd(), dbgetchar(), dbsqlexec(), dbsqlsend(), dbsetopt(), dbstrcpy(), dbstrlen().  
    6103             :  */
    6104             : void
    6105       20584 : dbfreebuf(DBPROCESS * dbproc)
    6106             : {
    6107       20584 :         tdsdump_log(TDS_DBG_FUNC, "dbfreebuf(%p)\n", dbproc);
    6108       20584 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    6109             : 
    6110       20584 :         if (dbproc->dbbuf)
    6111       20564 :                 TDS_ZERO_FREE(dbproc->dbbuf);
    6112       20584 :         dbproc->dbbufsz = 0;
    6113             : }
    6114             : 
    6115             : /**
    6116             :  * \ingroup dblib_core
    6117             :  * \brief Reset an option.
    6118             :  * 
    6119             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6120             :  * \param option to be turned off.
    6121             :  * \param param clearing some options requires a parameter, believe it or not.  
    6122             :  * \retval SUCCEED \a option and \a parameter seem sane.
    6123             :  * \retval FAIL no such \a option.
    6124             :  * \remarks Only the following options are recognized:
    6125             :         - DBARITHABORT
    6126             :         - DBARITHIGNORE
    6127             :         - DBCHAINXACTS
    6128             :         - DBFIPSFLAG
    6129             :         - DBISOLATION
    6130             :         - DBNOCOUNT
    6131             :         - DBNOEXEC
    6132             :         - DBPARSEONLY
    6133             :         - DBSHOWPLAN
    6134             :         - DBSTORPROCID
    6135             :         - DBQUOTEDIDENT
    6136             :         - DBSETTIME
    6137             :  * \sa dbisopt(), dbsetopt().
    6138             :  */
    6139             : RETCODE
    6140          20 : dbclropt(DBPROCESS * dbproc, int option, const char param[])
    6141             : {
    6142             :         char *cmd;
    6143             : 
    6144          20 :         tdsdump_log(TDS_DBG_FUNC, "dbclropt(%p, %d, %s)\n", dbproc, option, param);
    6145          20 :         CHECK_CONN(FAIL);
    6146          20 :         if (option != DBSETTIME) {
    6147           0 :                 CHECK_NULP(param, "dbclropt", 3, FAIL);
    6148             :         }
    6149             : 
    6150          20 :         if ((option < 0) || (option >= DBNUMOPTIONS)) {
    6151             :                 return FAIL;
    6152             :         }
    6153          20 :         dbproc->dbopts[option].factive = 0;
    6154             :         switch (option) {
    6155           0 :         case DBARITHABORT:
    6156             :         case DBARITHIGNORE:
    6157             :         case DBCHAINXACTS:
    6158             :         case DBFIPSFLAG:
    6159             :         case DBISOLATION:
    6160             :         case DBNOCOUNT:
    6161             :         case DBNOEXEC:
    6162             :         case DBPARSEONLY:
    6163             :         case DBSHOWPLAN:
    6164             :         case DBSTORPROCID:
    6165             :         case DBQUOTEDIDENT:
    6166             :                 /* server options (on/off) */
    6167           0 :                 if (asprintf(&cmd, "set %s off\n", dbproc->dbopts[option].text) < 0) {
    6168             :                         return FAIL;
    6169             :                 }
    6170           0 :                 dbstring_concat(&(dbproc->dboptcmd), cmd);
    6171           0 :                 free(cmd);
    6172           0 :                 break;
    6173           0 :         case DBBUFFER:
    6174           0 :                 buffer_set_capacity(dbproc, 1); /* frees row_buf->rows */
    6175           0 :                 return SUCCEED;
    6176             :                 break;
    6177          20 :         case DBSETTIME:
    6178          20 :                 tds_mutex_lock(&dblib_mutex);
    6179             :                 /*
    6180             :                  * Use global value of query timeout set by dbsettime() if any,
    6181             :                  * otherwise set it to zero just like tds_init_socket() does.
    6182             :                  */
    6183          20 :                 if (g_dblib_ctx.query_timeout > 0) {
    6184          10 :                         dbproc->tds_socket->query_timeout = g_dblib_ctx.query_timeout;
    6185             :                 } else {
    6186          10 :                         dbproc->tds_socket->query_timeout = 0;
    6187             :                 }
    6188          20 :                 tds_mutex_unlock(&dblib_mutex);
    6189          20 :                 return SUCCEED;
    6190             :                 break;
    6191             :         default:
    6192             :                 break;
    6193             :         }
    6194           0 :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbclropt(option = %d)\n", option);
    6195             :         return FAIL;
    6196             : }
    6197             : 
    6198             : /**
    6199             :  * \ingroup dblib_core
    6200             :  * \brief Get value of an option
    6201             :  * 
    6202             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6203             :  * \param option the option
    6204             :  * \param param a parameter to \a option. 
    6205             :  * \sa dbclropt(), dbsetopt().
    6206             :  */
    6207             : DBBOOL
    6208         180 : dbisopt(DBPROCESS * dbproc, int option, const char param[])
    6209             : {
    6210         180 :         tdsdump_log(TDS_DBG_FUNC, "dbisopt(%p, %d, %s)\n", dbproc, option, param);
    6211         180 :         CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
    6212             :         /* sometimes param can be NULL */
    6213             :         
    6214         180 :         if ((option < 0) || (option >= DBNUMOPTIONS)) {
    6215             :                 return FALSE;
    6216             :         }
    6217         180 :         return dbproc->dbopts[option].factive;
    6218             : }
    6219             : 
    6220             : /** \internal
    6221             :  * \ingroup dblib_internal
    6222             :  * \brief Get number of the row currently being read.
    6223             :  * 
    6224             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6225             :  * \return ostensibly the row number, or 0 if no rows have been read yet.  
    6226             :  * \retval 0 Always.  
    6227             :  * \sa DBCURROW(), dbclrbuf(), DBFIRSTROW(), dbgetrow(), DBLASTROW(), dbnextrow(), dbsetopt(),.  
    6228             :  * \todo Unimplemented.
    6229             :  */
    6230             : DBINT
    6231           0 : dbcurrow(DBPROCESS * dbproc)
    6232             : {
    6233           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcurrow(%p)\n", dbproc);
    6234           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    6235           0 :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbcurrow()\n");
    6236             :         return 0;
    6237             : }
    6238             : 
    6239             : 
    6240             : /** \internal
    6241             :  * \ingroup dblib_internal
    6242             :  * \brief Get returned row's type.
    6243             :  * 
    6244             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6245             :  * \sa DBROWTYPE().  
    6246             :  */
    6247             : STATUS
    6248           0 : dbrowtype(DBPROCESS * dbproc)
    6249             : {
    6250           0 :         tdsdump_log(TDS_DBG_FUNC, "dbrowtype(%p)\n", dbproc);
    6251           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NO_MORE_ROWS);
    6252           0 :         return dbproc->row_type;
    6253             : }
    6254             : 
    6255             : 
    6256             : /** \internal
    6257             :  * \ingroup dblib_internal
    6258             :  * \brief Get number of the row just returned.
    6259             :  * 
    6260             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6261             :  * \sa DBCURROW().
    6262             :  * \todo Unimplemented.
    6263             :  */
    6264             : int
    6265           0 : dbcurcmd(DBPROCESS * dbproc)
    6266             : {
    6267           0 :         tdsdump_log(TDS_DBG_FUNC, "dbcurcmd(%p)\n", dbproc);
    6268           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    6269           0 :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbcurcmd()\n");
    6270             :         return 0;
    6271             : }
    6272             : 
    6273             : 
    6274             : /** 
    6275             :  * \ingroup dblib_core
    6276             :  * \brief See if more commands are to be processed.
    6277             :  * 
    6278             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6279             :  * \sa DBMORECMDS(). DBCMDROW(), dbresults(), DBROWS(), DBROWTYPE().  
    6280             :  */
    6281             : RETCODE
    6282          30 : dbmorecmds(DBPROCESS * dbproc)
    6283             : {
    6284          30 :         tdsdump_log(TDS_DBG_FUNC, "dbmorecmds(%p)\n", dbproc);
    6285          30 :         CHECK_CONN(FAIL);
    6286             : 
    6287          30 :         if (dbproc->tds_socket->res_info == NULL) {
    6288             :                 return FAIL;
    6289             :         }
    6290             : 
    6291          30 :         if (!dbproc->tds_socket->res_info->more_results) {
    6292          20 :                 tdsdump_log(TDS_DBG_FUNC, "more_results is false; returns FAIL\n");
    6293             :                 return FAIL;
    6294             :         }
    6295             :         
    6296          10 :         tdsdump_log(TDS_DBG_FUNC, "more_results is true; returns SUCCEED\n");
    6297             :         
    6298             :         return SUCCEED;
    6299             : }
    6300             : 
    6301             : /**
    6302             :  * \ingroup dblib_rpc
    6303             :  * \brief Get datatype of a stored procedure's return parameter.
    6304             :  * 
    6305             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6306             :  * \param retnum Nth return parameter, between 1 and \c dbnumrets().  
    6307             :  * \return SYB* datatype token, or -1 if \a retnum is out of range. 
    6308             :  * \sa dbnextrow(), dbnumrets(), dbprtype(), dbresults(), dbretdata(), dbretlen(), dbretname(), dbrpcinit(), dbrpcparam(). 
    6309             :  */
    6310             : int
    6311          66 : dbrettype(DBPROCESS * dbproc, int retnum)
    6312             : {
    6313             :         TDSCOLUMN *colinfo;
    6314             : 
    6315          66 :         tdsdump_log(TDS_DBG_FUNC, "dbrettype(%p, %d)\n", dbproc, retnum);
    6316          66 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    6317          66 :         assert(dbproc->tds_socket);
    6318          66 :         assert(dbproc->tds_socket->param_info);
    6319             : 
    6320          66 :         if (retnum < 1 || retnum > dbproc->tds_socket->param_info->num_cols)
    6321             :                 return -1;
    6322             : 
    6323          66 :         colinfo = dbproc->tds_socket->param_info->columns[retnum - 1];
    6324             : 
    6325          66 :         return tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    6326             : }
    6327             : 
    6328             : /**
    6329             :  * \ingroup dblib_core
    6330             :  * \brief Get size of the command buffer, in bytes. 
    6331             :  * 
    6332             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6333             :  * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbgetchar(), dbstrcpy(). 
    6334             :  */
    6335             : int
    6336           0 : dbstrlen(DBPROCESS * dbproc)
    6337             : {
    6338           0 :         tdsdump_log(TDS_DBG_FUNC, "dbstrlen(%p)\n", dbproc);
    6339           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    6340             :         
    6341           0 :         return dbproc->dbbufsz;
    6342             : }
    6343             : 
    6344             : 
    6345             : /**
    6346             :  * \ingroup dblib_core
    6347             :  * \brief Get address of a position in the command buffer.
    6348             :  * 
    6349             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6350             :  * \param pos offset within the command buffer, starting at \em 0.   
    6351             :  * \remarks A bit overspecialized, this one.  
    6352             :  * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbstrcpy(), dbstrlen(),
    6353             :  */
    6354             : char *
    6355         490 : dbgetchar(DBPROCESS * dbproc, int pos)
    6356             : {
    6357         490 :         tdsdump_log(TDS_DBG_FUNC, "dbgetchar(%p, %d)\n", dbproc, pos);
    6358         490 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    6359         490 :         tdsdump_log(TDS_DBG_FUNC, "dbgetchar() bufsz = %d, pos = %d\n", dbproc->dbbufsz, pos);
    6360             : 
    6361         490 :         if (dbproc->dbbufsz > 0) {
    6362         490 :                 if (pos >= 0 && pos < (dbproc->dbbufsz - 1))
    6363         480 :                         return (char *) &dbproc->dbbuf[pos];
    6364             :                 return NULL;
    6365             :         }
    6366             :         return NULL;
    6367             : }
    6368             : 
    6369             : /**
    6370             :  * \ingroup dblib_core
    6371             :  * \brief Get a copy of a chunk of the command buffer.
    6372             :  * 
    6373             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6374             :  * \param start position in the command buffer to start copying from, starting from \em 0.  
    6375             :  *      If start is past the end of the command buffer, dbstrcpy() inserts a null terminator at dest[0].
    6376             :  * \param numbytes number of bytes to copy. 
    6377             :         - If -1, dbstrcpy() copies the whole command buffer.  
    6378             :         - If  0 dbstrcpy() writes a \c NULL to dest[0]. 
    6379             :         - If the command buffer contains fewer than \a numbytes (taking \a start into account) dbstrcpy() 
    6380             :         copies the rest of it.  
    6381             :  * \param dest \em output: the buffer to write to.  Make sure it's big enough.  
    6382             :  * \retval SUCCEED the inputs were valid and \a dest was affected.  
    6383             :  * \retval FAIL \a start < 0 or \a numbytes < -1.  
    6384             :  * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbgetchar(), dbstrlen().  
    6385             :  */
    6386             : RETCODE
    6387           0 : dbstrcpy(DBPROCESS * dbproc, int start, int numbytes, char *dest)
    6388             : {
    6389           0 :         tdsdump_log(TDS_DBG_FUNC, "dbstrcpy(%p, %d, %d, %s)\n", dbproc, start, numbytes, dest);
    6390           0 :         CHECK_CONN(FAIL);
    6391           0 :         CHECK_NULP(dest, "dbstrcpy", 4, FAIL);
    6392             : 
    6393           0 :         if (start < 0) {
    6394           0 :                 dbperror(dbproc, SYBENSIP, 0);
    6395           0 :                 return FAIL;
    6396             :         }
    6397           0 :         if (numbytes < -1) {
    6398           0 :                 dbperror(dbproc, SYBEBNUM, 0);
    6399           0 :                 return FAIL;
    6400             :         }
    6401           0 :         dest[0] = 0;            /* start with empty string being returned */
    6402           0 :         if (dbproc->dbbufsz > 0 && start < dbproc->dbbufsz) {
    6403           0 :                 if (numbytes == -1)
    6404           0 :                         numbytes = dbproc->dbbufsz - start;
    6405           0 :                 if (start + numbytes > dbproc->dbbufsz)
    6406           0 :                         numbytes = dbproc->dbbufsz - start;
    6407           0 :                 memcpy(dest, (char *) &dbproc->dbbuf[start], numbytes);
    6408           0 :                 dest[numbytes] = '\0';
    6409             :         }
    6410             :         return SUCCEED;
    6411             : }
    6412             : 
    6413             : /**
    6414             :  * \ingroup dblib_core
    6415             :  * \brief safely quotes character values in SQL text.  
    6416             :  * 
    6417             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6418             :  * \param src input string.
    6419             :  * \param srclen length of \a src in bytes, or -1 to indicate it's null-terminated.  
    6420             :  * \param dest \em output: client-provided output buffer. 
    6421             :  * \param destlen size of \a dest in bytes, or -1 to indicate it's "big enough" and the data should be null-terminated.  
    6422             :  * \param quotetype
    6423             :         - \c DBSINGLE Doubles all single quotes (').
    6424             :         - \c DBDOUBLE Doubles all double quotes (").
    6425             :         - \c DBBOTH   Doubles all single and double quotes.
    6426             :  * \retval SUCCEED everything worked.
    6427             :  * \retval FAIL no such \a quotetype, or insufficient room in \a dest.
    6428             :  * \sa dbcmd(), dbfcmd(). 
    6429             :  */
    6430             : RETCODE
    6431          50 : dbsafestr(DBPROCESS * dbproc, const char *src, DBINT srclen, char *dest, DBINT destlen, int quotetype)
    6432             : {
    6433          50 :         int i, j = 0;
    6434          50 :         bool squote = false, dquote = false;
    6435             : 
    6436          50 :         tdsdump_log(TDS_DBG_FUNC, "dbsafestr(%p, %s, %d, %s, %d, %d)\n", dbproc, src, srclen, dest, destlen, quotetype);
    6437          50 :         CHECK_NULP(src, "dbsafestr", 2, FAIL);
    6438          50 :         CHECK_NULP(dest, "dbsafestr", 4, FAIL);
    6439             : 
    6440             :         /* check parameters */
    6441          50 :         if (srclen < -1 || destlen < -1)
    6442             :                 return FAIL;
    6443             : 
    6444          50 :         if (srclen == -1)
    6445          50 :                 srclen = (int)strlen(src);
    6446             : 
    6447          50 :         if (quotetype == DBSINGLE || quotetype == DBBOTH)
    6448          40 :                 squote = true;
    6449          50 :         if (quotetype == DBDOUBLE || quotetype == DBBOTH)
    6450          30 :                 dquote = true;
    6451             : 
    6452             :         /* return FAIL if invalid quotetype */
    6453          50 :         if (!dquote && !squote)
    6454             :                 return FAIL;
    6455             : 
    6456             : 
    6457        1790 :         for (i = 0; i < srclen; i++) {
    6458             : 
    6459             :                 /* dbsafestr returns fail if the deststr is not big enough */
    6460             :                 /* need one char + one for terminator */
    6461        1800 :                 if (destlen >= 0 && j >= destlen)
    6462             :                         return FAIL;
    6463             : 
    6464        1790 :                 if (squote && src[i] == '\'')
    6465          40 :                         dest[j++] = '\'';
    6466        1750 :                 else if (dquote && src[i] == '\"')
    6467          30 :                         dest[j++] = '\"';
    6468             : 
    6469        1790 :                 if (destlen >= 0 && j >= destlen)
    6470             :                         return FAIL;
    6471             : 
    6472        1790 :                 dest[j++] = src[i];
    6473             :         }
    6474             : 
    6475          40 :         if (destlen >= 0 && j >= destlen)
    6476             :                 return FAIL;
    6477             : 
    6478          30 :         dest[j] = '\0';
    6479          30 :         return SUCCEED;
    6480             : }
    6481             : 
    6482             : /**
    6483             :  * \ingroup dblib_core
    6484             :  * \brief Print a token value's name to a buffer
    6485             :  * 
    6486             :  * \param token server SYB* value, e.g. SYBINT.  
    6487             : 
    6488             :  * \return ASCII null-terminated string.  
    6489             :  * \sa dbaltop(), dbalttype(), dbcoltype(), dbrettype().
    6490             :  */
    6491             : const char *
    6492          24 : dbprtype(int token)
    6493             : {
    6494          24 :         tdsdump_log(TDS_DBG_FUNC, "dbprtype(%d)\n", token);
    6495          24 :         return tds_prtype(token);
    6496             : }
    6497             : 
    6498             : /**
    6499             :  * \ingroup dblib_core
    6500             :  * \brief describe table column attributes with a single call (Freetds-only API function modelled on dbcolinfo)
    6501             :  * 
    6502             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6503             :  * \param column Nth in the result set, starting from 1.
    6504             :  * \param pdbcol address of structure to be populated by this function.  
    6505             :  * \return SUCCEED or FAIL. 
    6506             :  * \sa dbcolinfo().
    6507             :  */
    6508             : RETCODE
    6509          30 : dbtablecolinfo(DBPROCESS *dbproc, DBINT column, DBCOL *pdbcol)
    6510             : {
    6511             :         TDSCOLUMN *colinfo;
    6512             : 
    6513          30 :         tdsdump_log(TDS_DBG_FUNC, "dbtablecolinfo(%p, %d, %p)\n", dbproc, column, pdbcol);
    6514          30 :         CHECK_CONN(FAIL);
    6515          30 :         CHECK_NULP(pdbcol, "dbtablecolinfo", 3, FAIL);
    6516          30 :         DBPERROR_RETURN(pdbcol->SizeOfStruct != sizeof(DBCOL)
    6517             :                         && pdbcol->SizeOfStruct != sizeof(DBCOL2), SYBECOLSIZE);
    6518             : 
    6519          30 :         colinfo = dbcolptr(dbproc, column);
    6520          30 :         if (!colinfo)
    6521             :                 return FAIL;
    6522             : 
    6523          60 :         strlcpy(pdbcol->Name, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->Name));
    6524          60 :         strlcpy(pdbcol->ActualName, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->ActualName));
    6525          60 :         strlcpy(pdbcol->TableName, tds_dstr_cstr(&colinfo->table_name), sizeof(pdbcol->TableName));
    6526             : 
    6527          30 :         pdbcol->Type = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
    6528          30 :         pdbcol->UserType = colinfo->column_usertype;
    6529          30 :         pdbcol->MaxLength = colinfo->column_size;
    6530          30 :         if (colinfo->column_nullable)
    6531           0 :                 pdbcol->Null = TRUE;
    6532             :         else
    6533          30 :                 pdbcol->Null = FALSE;
    6534             : 
    6535          30 :         pdbcol->VarLength = FALSE;
    6536             : 
    6537          30 :         if (colinfo->column_nullable
    6538          30 :             || is_nullable_type(colinfo->column_type))
    6539           0 :                 pdbcol->VarLength = TRUE;
    6540             :         
    6541          30 :         pdbcol->Precision = colinfo->column_prec;
    6542          30 :         pdbcol->Scale = colinfo->column_scale;
    6543             : 
    6544          30 :         pdbcol->Updatable = colinfo->column_writeable ? TRUE : FALSE;
    6545          30 :         pdbcol->Identity = colinfo->column_identity ? TRUE : FALSE;
    6546             : 
    6547          30 :         if (pdbcol->SizeOfStruct >= sizeof(DBCOL2)) {
    6548          30 :                 DBCOL2 *col = (DBCOL2 *) pdbcol;
    6549             :                 TDSRET rc;
    6550             : 
    6551          30 :                 col->ServerType = colinfo->on_server.column_type;
    6552          30 :                 col->ServerMaxLength = colinfo->on_server.column_size;
    6553             : 
    6554          30 :                 rc = tds_get_column_declaration(dbproc->tds_socket, colinfo, col->ServerTypeDeclaration);
    6555          30 :                 if (TDS_FAILED(rc))
    6556             :                         return FAIL;
    6557             :         }
    6558             : 
    6559             :         return SUCCEED;
    6560             : }
    6561             : 
    6562             : /**
    6563             :  * \ingroup dblib_core
    6564             :  * \brief Get text timestamp for a column in the current row.
    6565             :  * 
    6566             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6567             :  * \param column number of the column in the \c SELECT statement, starting at 1.
    6568             :  * \return timestamp for \a column, may be NULL.
    6569             :  * \sa dbtxptr(), dbwritetext().  
    6570             :  */
    6571             : DBBINARY *
    6572          56 : dbtxtimestamp(DBPROCESS * dbproc, int column)
    6573             : {
    6574             :         TDSCOLUMN *colinfo;
    6575             :         TDSBLOB *blob;
    6576             : 
    6577          56 :         tdsdump_log(TDS_DBG_FUNC, "dbtxtimestamp(%p, %d)\n", dbproc, column);
    6578             : 
    6579          56 :         colinfo = dbcolptr(dbproc, column);
    6580          56 :         if (!colinfo || !is_blob_col(colinfo))
    6581             :                 return NULL;
    6582             : 
    6583          56 :         blob = (TDSBLOB *) colinfo->column_data;
    6584             : 
    6585             :         /* test if valid */
    6586          56 :         if (!blob->valid_ptr)
    6587             :                 return NULL;
    6588             : 
    6589          48 :         return (DBBINARY *) blob->timestamp;
    6590             : }
    6591             : 
    6592             : /**
    6593             :  * \ingroup dblib_core
    6594             :  * \brief Get text pointer for a column in the current row.
    6595             :  * 
    6596             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6597             :  * \param column number of the column in the \c SELECT statement, starting at 1.
    6598             :  * \return text pointer for \a column, may be NULL.
    6599             :  * \sa dbtxtimestamp(), dbwritetext(). 
    6600             :  */
    6601             : DBBINARY *
    6602          56 : dbtxptr(DBPROCESS * dbproc, int column)
    6603             : {
    6604             :         TDSCOLUMN *colinfo;
    6605             :         TDSBLOB *blob;
    6606             : 
    6607          56 :         tdsdump_log(TDS_DBG_FUNC, "dbtxptr(%p, %d)\n", dbproc, column);
    6608             : 
    6609          56 :         colinfo = dbcolptr(dbproc, column);
    6610          56 :         if (!colinfo || !is_blob_col(colinfo))
    6611             :                 return NULL;
    6612             : 
    6613          56 :         blob = (TDSBLOB *) colinfo->column_data;
    6614             : 
    6615             :         /* test if valid */
    6616          56 :         if (!blob->valid_ptr)
    6617             :                 return NULL;
    6618             : 
    6619          48 :         return (DBBINARY *) blob->textptr;
    6620             : }
    6621             : 
    6622             : /**
    6623             :  * \ingroup dblib_core
    6624             :  * \brief Send text or image data to the server.
    6625             :  * 
    6626             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6627             :  * \param objname table name
    6628             :  * \param textptr text pointer to be modified, obtained from dbtxptr(). 
    6629             :  * \param textptrlen \em Ignored.  Supposed to be \c DBTXPLEN.
    6630             :  * \param timestamp text timestamp to be modified, obtained from dbtxtimestamp() or dbtxtsnewval(), may be \c NULL.
    6631             :  * \param log \c TRUE if the operation is to be recorded in the transaction log.
    6632             :  * \param size overall size of the data (in total, not just for this call), in bytes.  A guideline, must not overstate the case.  
    6633             :  * \param text the chunk of data to write.  
    6634             :  * \retval SUCCEED everything worked.
    6635             :  * \retval FAIL not sent, possibly because \a timestamp is invalid or was changed in the database since it was fetched.  
    6636             :  * \sa dbmoretext(), dbtxptr(), dbtxtimestamp(), dbwritetext(), dbtxtsput().  
    6637             :  */
    6638             : RETCODE
    6639          48 : dbwritetext(DBPROCESS * dbproc, char *objname, DBBINARY * textptr, DBTINYINT textptrlen, DBBINARY * timestamp, DBBOOL log,
    6640             :             DBINT size, BYTE * text)
    6641             : {
    6642             :         char textptr_string[35];        /* 16 * 2 + 2 (0x) + 1 */
    6643             :         char timestamp_string[19];      /* 8 * 2 + 2 (0x) + 1 */
    6644             :         TDS_INT result_type;
    6645             : 
    6646          48 :         tdsdump_log(TDS_DBG_FUNC, "dbwritetext(%p, %s, %p, %d, %p, %d)\n", 
    6647             :                                   dbproc, objname, textptr, textptrlen, timestamp, log);
    6648          48 :         CHECK_CONN(FAIL);
    6649          48 :         CHECK_NULP(objname, "dbwritetext", 2, FAIL);
    6650          48 :         CHECK_NULP(textptr, "dbwritetext", 3, FAIL);
    6651          48 :         CHECK_NULP(timestamp, "dbwritetext", 5, FAIL);
    6652          48 :         CHECK_PARAMETER(size, SYBEZTXT, FAIL);
    6653             : 
    6654             :         if (IS_TDSDEAD(dbproc->tds_socket))
    6655             :                 return FAIL;
    6656             : 
    6657          48 :         if (textptrlen > DBTXPLEN)
    6658             :                 return FAIL;
    6659             : 
    6660          48 :         dbconvert(dbproc, SYBBINARY, (BYTE *) textptr, textptrlen, SYBCHAR, (BYTE *) textptr_string, -1);
    6661          48 :         dbconvert(dbproc, SYBBINARY, (BYTE *) timestamp, 8, SYBCHAR, (BYTE *) timestamp_string, -1);
    6662             : 
    6663          48 :         dbproc->dbresults_state = _DB_RES_INIT;
    6664             : 
    6665          48 :         if (dbproc->tds_socket->state == TDS_PENDING) {
    6666           0 :                 const TDSRET ret = tds_process_tokens(dbproc->tds_socket, &result_type, NULL, TDS_TOKEN_TRAILING);
    6667           0 :                 if (ret != TDS_NO_MORE_RESULTS) {
    6668           0 :                         dbperror(dbproc, SYBERPND, 0);
    6669           0 :                         dbproc->command_state = DBCMDSENT;
    6670           0 :                         return FAIL;
    6671             :                 }
    6672             :         }
    6673             :         
    6674          48 :         if (TDS_FAILED(tds_writetext_start(dbproc->tds_socket, objname, 
    6675             :                 textptr_string, timestamp_string, (log == TRUE), size)))
    6676             :                 return FAIL;
    6677             : 
    6678          48 :         if (!text) {
    6679          24 :                 dbproc->text_size = size;
    6680          24 :                 dbproc->text_sent = 0;
    6681          24 :                 return SUCCEED;
    6682             :         }
    6683             : 
    6684          24 :         tds_writetext_continue(dbproc->tds_socket, text, size);
    6685          24 :         tds_writetext_end(dbproc->tds_socket);
    6686          24 :         dbproc->text_sent = 0;
    6687             : 
    6688          24 :         if (dbsqlok(dbproc) == SUCCEED && dbresults(dbproc) == SUCCEED)
    6689             :                 return SUCCEED;
    6690             :         return FAIL;
    6691             : }
    6692             : 
    6693             : /**
    6694             :  * \ingroup dblib_core
    6695             :  * \brief Fetch part of a text or image value from the server.
    6696             :  * 
    6697             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6698             :  * \param buf \em output: buffer into which text will be placed.
    6699             :  * \param bufsize size of \a buf, in bytes. 
    6700             :  * \return 
    6701             :         - \c >0 count of bytes placed in \a buf.
    6702             :         - \c  0 end of row.  
    6703             :         - \c -1 \em error, no result set ready for \a dbproc.
    6704             :         - \c NO_MORE_ROWS all rows read, no further data. 
    6705             :  * \sa dbmoretext(), dbnextrow(), dbwritetext(). 
    6706             :  */
    6707             : STATUS
    6708         816 : dbreadtext(DBPROCESS * dbproc, void *buf, DBINT bufsize)
    6709             : {
    6710             :         TDSSOCKET *tds;
    6711             :         TDSCOLUMN *curcol;
    6712             :         int cpbytes, bytes_avail;
    6713             :         TDS_INT result_type;
    6714             :         TDSRESULTINFO *resinfo;
    6715             : 
    6716         816 :         tdsdump_log(TDS_DBG_FUNC, "dbreadtext(%p, %p, %d)\n", dbproc, buf, bufsize);
    6717         816 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    6718         816 :         CHECK_NULP(buf, "dbreadtext", 2, -1);
    6719             : 
    6720         816 :         tds = dbproc->tds_socket;
    6721             : 
    6722         816 :         if (!tds || !tds->res_info || !tds->res_info->columns[0])
    6723             :                 return -1;
    6724             : 
    6725         816 :         resinfo = tds->res_info;
    6726         816 :         curcol = resinfo->columns[0];
    6727             : 
    6728             :         /*
    6729             :          * if the current position is beyond the end of the text
    6730             :          * set pos to 0 and return 0 to denote the end of the 
    6731             :          * text 
    6732             :          */
    6733         816 :         if (curcol->column_textpos && curcol->column_textpos >= curcol->column_cur_size) {
    6734          48 :                 curcol->column_textpos = 0;
    6735          48 :                 return 0;
    6736             :         }
    6737             : 
    6738             :         /*
    6739             :          * if pos is 0 (first time through or last call exhausted the text)
    6740             :          * then read another row
    6741             :          */
    6742             : 
    6743         768 :         if (curcol->column_textpos == 0) {
    6744          96 :                 const int mask = TDS_STOPAT_ROWFMT|TDS_STOPAT_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE;
    6745          96 :                 buffer_save_row(dbproc);
    6746          96 :                 switch (tds_process_tokens(dbproc->tds_socket, &result_type, NULL, mask)) {
    6747          96 :                 case TDS_SUCCESS:
    6748          96 :                         if (result_type == TDS_ROW_RESULT || result_type == TDS_COMPUTE_RESULT)
    6749             :                                 break;
    6750             :                 case TDS_NO_MORE_RESULTS:
    6751             :                         return NO_MORE_ROWS;
    6752           0 :                 default:
    6753           0 :                         return -1;
    6754             :                 }
    6755         720 :         }
    6756             : 
    6757             :         /* find the number of bytes to return */
    6758         720 :         bytes_avail = curcol->column_cur_size - curcol->column_textpos;
    6759         720 :         cpbytes = bytes_avail > bufsize ? bufsize : bytes_avail;
    6760         720 :         memcpy(buf, &((TDSBLOB *) curcol->column_data)->textvalue[curcol->column_textpos], cpbytes);
    6761         720 :         curcol->column_textpos += cpbytes;
    6762         720 :         return cpbytes;
    6763             : }
    6764             : 
    6765             : /**
    6766             :  * \ingroup dblib_core
    6767             :  * \brief Send chunk of a text/image value to the server.
    6768             :  * 
    6769             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6770             :  * \param size count of bytes to send.  
    6771             :  * \param text textpointer, obtained from dbtxptr.
    6772             :  * \retval SUCCEED always.
    6773             :  * \sa dbtxptr(), dbtxtimestamp(), dbwritetext(). 
    6774             :  * \todo Check return value of called functions and return \c FAIL if appropriate. 
    6775             :  */
    6776             : RETCODE
    6777         360 : dbmoretext(DBPROCESS * dbproc, DBINT size, const BYTE text[])
    6778             : {
    6779         360 :         tdsdump_log(TDS_DBG_FUNC, "dbmoretext(%p, %d, %p)\n", dbproc, size, text);
    6780         360 :         CHECK_CONN(FAIL);
    6781         360 :         CHECK_NULP(text, "dbmoretext", 3, FAIL);
    6782             : 
    6783         360 :         assert(dbproc->text_size >= dbproc->text_sent);
    6784             : 
    6785             :         /* TODO this test should be inside tds_writetext_continue, currently not */
    6786         360 :         if (size < 0 || size > dbproc->text_size - dbproc->text_sent)
    6787             :                 return FAIL;
    6788             : 
    6789         360 :         if (size) {
    6790         360 :                 if (TDS_FAILED(tds_writetext_continue(dbproc->tds_socket, text, size)))
    6791             :                         return FAIL;
    6792         360 :                 dbproc->text_sent += size;
    6793             : 
    6794         360 :                 if (dbproc->text_sent == dbproc->text_size) {
    6795          24 :                         tds_writetext_end(dbproc->tds_socket);
    6796          24 :                         dbproc->text_sent = 0;
    6797             :                 }
    6798             :         }
    6799             : 
    6800             :         return SUCCEED;
    6801             : }
    6802             : 
    6803             : /**
    6804             :  * \ingroup dblib_core
    6805             :  * \brief Record to a file all SQL commands sent to the server
    6806             :  * 
    6807             :  * \param filename name of file to write to. 
    6808             :  * \remarks Files are named \em filename.n, where n is an integer, starting with 0, and incremented with each callto dbopen().  
    6809             :  * \sa dbopen(), TDSDUMP environment variable(). 
    6810             :  */
    6811             : void
    6812           0 : dbrecftos(const char filename[])
    6813             : {
    6814             :         char *f;
    6815             : 
    6816           0 :         tdsdump_log(TDS_DBG_FUNC, "dbrecftos(%s)\n", filename);
    6817           0 :         if (filename == NULL) { 
    6818           0 :                 dbperror(NULL, SYBENULP, 0); 
    6819           0 :                 return;
    6820             :         }
    6821             :         
    6822           0 :         f = strdup(filename);
    6823           0 :         if (!f) {
    6824           0 :                 dbperror(NULL, SYBEMEM, 0); 
    6825           0 :                 return;
    6826             :         }
    6827             :         
    6828           0 :         tds_mutex_lock(&dblib_mutex);
    6829           0 :         free(g_dblib_ctx.recftos_filename);
    6830           0 :         g_dblib_ctx.recftos_filename = f;
    6831           0 :         g_dblib_ctx.recftos_filenum = 0;
    6832           0 :         tds_mutex_unlock(&dblib_mutex);
    6833             : }
    6834             : 
    6835             : /** \internal
    6836             :  * \ingroup dblib_internal
    6837             :  * \brief Get the TDS version in use for \a dbproc.  
    6838             :  * 
    6839             :  * 
    6840             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6841             :  * \return a \c DBTDS* token.  
    6842             :  * \remarks The integer values of the constants are counterintuitive.  
    6843             :  * \sa DBTDS().
    6844             :  */
    6845             : int
    6846         452 : dbtds(DBPROCESS * dbproc)
    6847             : {
    6848         452 :         tdsdump_log(TDS_DBG_FUNC, "dbtds(%p)\n", dbproc);
    6849         452 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    6850             : 
    6851         452 :         if (dbproc->tds_socket) {
    6852         452 :                 switch (dbproc->tds_socket->conn->tds_version) {
    6853             :                 case 0x402:
    6854             :                         return DBTDS_4_2;
    6855           0 :                 case 0x406:
    6856           0 :                         return DBTDS_4_6;
    6857          74 :                 case 0x500:
    6858          74 :                         return DBTDS_5_0;
    6859           0 :                 case 0x700:
    6860           0 :                         return DBTDS_7_0;
    6861         176 :                 case 0x701:
    6862         176 :                         return DBTDS_7_1;
    6863           0 :                 case 0x702:
    6864           0 :                         return DBTDS_7_2;
    6865         100 :                 case 0x703:
    6866         100 :                         return DBTDS_7_3;
    6867         102 :                 case 0x704:
    6868         102 :                         return DBTDS_7_4;
    6869           0 :                 case 0x800:
    6870           0 :                         return DBTDS_8_0_;
    6871           0 :                 default:
    6872           0 :                         return DBTDS_UNKNOWN;
    6873             :                 }
    6874             :         }
    6875             :         return -1;
    6876             : }
    6877             : 
    6878             : /**
    6879             :  * \ingroup dblib_core
    6880             :  * \brief See which version of db-lib is in use.
    6881             :  * 
    6882             :  * \return null-terminated ASCII string representing the version of db-lib.  
    6883             :  * \remarks FreeTDS returns the CVS version string of dblib.c.  
    6884             :  * \sa 
    6885             :  */
    6886             : const char *
    6887           0 : dbversion(void)
    6888             : {
    6889           0 :         tdsdump_log(TDS_DBG_FUNC, "dbversion(void)\n");
    6890           0 :         return TDS_VERSION_NO;
    6891             : }
    6892             : 
    6893             : #if defined(DBLIB_UNIMPLEMENTED)
    6894             : /**
    6895             :  * \ingroup dblib_core
    6896             :  * \brief Set the default character set.  
    6897             :  * 
    6898             :  * \param charset null-terminated ASCII string, matching a row in master..syscharsets.  
    6899             :  * \sa dbsetdeflang(), dbsetdefcharset(), dblogin(), dbopen(). 
    6900             :  * \todo Unimplemented.
    6901             :  */
    6902             : RETCODE
    6903             : dbsetdefcharset(char *charset)
    6904             : {
    6905             :         tdsdump_log(TDS_DBG_FUNC, "dbsetdefcharset(%s)\n", charset);
    6906             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetdefcharset()\n");
    6907             :         return SUCCEED;
    6908             : }
    6909             : 
    6910             : /**
    6911             :  * \ingroup dblib_core
    6912             :  * \brief Ready execution of a registered procedure.
    6913             :  * 
    6914             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6915             :  * \param procedure_name to call.
    6916             :  * \param namelen size of \a procedure_name, in bytes.
    6917             :  * \sa dbregparam(), dbregexec(), dbregwatch(), dbreglist(), dbregwatchlist
    6918             :  * \todo Unimplemented.
    6919             :  */
    6920             : RETCODE
    6921             : dbreginit(DBPROCESS * dbproc, DBCHAR * procedure_name, DBSMALLINT namelen)
    6922             : {
    6923             :         tdsdump_log(TDS_DBG_FUNC, "dbreginit(%p, %s, %d)\n", dbproc, procedure_name, namelen);
    6924             :         CHECK_CONN(FAIL);
    6925             :         CHECK_NULP(procedure_name, "dbreginit", 2, FAIL);
    6926             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbreginit()\n");
    6927             :         return SUCCEED;
    6928             : }
    6929             : 
    6930             : 
    6931             : /**
    6932             :  * \ingroup dblib_core
    6933             :  * \brief Get names of Open Server registered procedures.
    6934             :  * 
    6935             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6936             :  * \sa dbregparam(), dbregexec(), dbregwatch(), dbreglist(), dbregwatchlist(). 
    6937             :  * \todo Unimplemented.
    6938             :  */
    6939             : RETCODE
    6940             : dbreglist(DBPROCESS * dbproc)
    6941             : {
    6942             :         tdsdump_log(TDS_DBG_FUNC, "dbreglist(%p)\n", dbproc);
    6943             :         CHECK_CONN(FAIL);
    6944             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbreglist()\n");
    6945             :         return SUCCEED;
    6946             : }
    6947             : 
    6948             : 
    6949             : /**
    6950             :  * \ingroup dblib_core
    6951             :  * \brief  Describe parameter of registered procedure .
    6952             :  * 
    6953             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6954             :  * \param param_name
    6955             :  * \param type \c SYB* datatype. 
    6956             :  * \param datalen size of \a data.
    6957             :  * \param data address of buffer holding value for the parameter.  
    6958             :  * \sa dbreginit(), dbregexec(), dbnpdefine(), dbnpcreate(), dbregwatch(). 
    6959             :  * \todo Unimplemented.
    6960             :  */
    6961             : RETCODE
    6962             : dbregparam(DBPROCESS * dbproc, char *param_name, int type, DBINT datalen, BYTE * data)
    6963             : {
    6964             :         tdsdump_log(TDS_DBG_FUNC, "dbregparam(%p, %s, %d, %d, %p)\n", dbproc, param_name, type, datalen, data);
    6965             :         CHECK_CONN(FAIL);
    6966             :         CHECK_NULP(param_name, "dbregparam", 2, FAIL);
    6967             :         CHECK_NULP(data, "dbregparam", 5, FAIL);
    6968             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbregparam()\n");
    6969             :         return SUCCEED;
    6970             : }
    6971             : 
    6972             : 
    6973             : /**
    6974             :  * \ingroup dblib_core
    6975             :  * \brief Execute a registered procedure.
    6976             :  * 
    6977             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6978             :  * \param options
    6979             :  * \sa dbreginit(), dbregparam(), dbregwatch(), dbregnowatch
    6980             :  * \todo Unimplemented.
    6981             :  */
    6982             : RETCODE
    6983             : dbregexec(DBPROCESS * dbproc, DBUSMALLINT options)
    6984             : {
    6985             :         tdsdump_log(TDS_DBG_FUNC, "dbregexec(%p, %d)\n", dbproc, options);
    6986             :         CHECK_CONN(FAIL);
    6987             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbregexec()\n");
    6988             :         return SUCCEED;
    6989             : }
    6990             : #endif
    6991             : 
    6992             : 
    6993             : /**
    6994             :  * \ingroup dblib_datetime
    6995             :  * \brief Get name of a month, in some human language.
    6996             :  * 
    6997             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    6998             :  * \param language \em ignored.
    6999             :  * \param monthnum number of the month, starting with 1.
    7000             :  * \param shortform set to \c TRUE for a three letter output ("Jan" - "Dec"), else zero.  
    7001             :  * \return address of null-terminated ASCII string, or \c NULL on error. 
    7002             :  * \sa db12hour(), dbdateorder(), dbdayname(), DBSETLNATLANG(), dbsetopt().  
    7003             :  */
    7004             : const char *
    7005           0 : dbmonthname(DBPROCESS * dbproc, char *language, int monthnum, DBBOOL shortform)
    7006             : {
    7007             :         static const char shortmon[][4] = {
    7008             :                 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    7009             :         };
    7010             :         static const char longmon[][12] = {
    7011             :                 "January", "February", "March", "April", "May", "June",
    7012             :                 "July", "August", "September", "October", "November", "December"
    7013             :         };
    7014             : 
    7015           0 :         tdsdump_log(TDS_DBG_FUNC, "dbmonthname(%p, %s, %d, %d)\n", dbproc, language, monthnum, shortform);
    7016           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    7017           0 :         CHECK_NULP(language, "dbmonthname", 2, NULL);
    7018             : 
    7019           0 :         if (monthnum < 1 || monthnum > 12)
    7020             :                 return NULL;
    7021           0 :         return (shortform) ? shortmon[monthnum - 1] : longmon[monthnum - 1];
    7022             : }
    7023             : 
    7024             : /**
    7025             :  * \ingroup dblib_core
    7026             :  * \brief See if a command caused the current database to change.
    7027             :  * 
    7028             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7029             :  * \return name of new database, if changed, as a null-terminated ASCII string, else \c NULL.
    7030             : 
    7031             :  * \sa dbname(), dbresults(), dbsqlexec(), dbsqlsend(), dbuse().  
    7032             :  */
    7033             : char *
    7034           0 : dbchange(DBPROCESS * dbproc)
    7035             : {
    7036           0 :         tdsdump_log(TDS_DBG_FUNC, "dbchange(%p)\n", dbproc);
    7037           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    7038             : 
    7039           0 :         if (dbproc->envchange_rcv & (1 << (TDS_ENV_DATABASE - 1))) {
    7040           0 :                 return dbproc->dbcurdb;
    7041             :         }
    7042             :         return NULL;
    7043             : }
    7044             : 
    7045             : /**
    7046             :  * \ingroup dblib_core
    7047             :  * \brief Get name of current database.
    7048             :  * 
    7049             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7050             :  * \return current database name, as null-terminated ASCII string.
    7051             :  * \sa dbchange(), dbuse().  
    7052             :  */
    7053             : char *
    7054           0 : dbname(DBPROCESS * dbproc)
    7055             : {
    7056           0 :         tdsdump_log(TDS_DBG_FUNC, "dbname(%p)\n", dbproc);
    7057           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    7058           0 :         return dbproc->dbcurdb;
    7059             : }
    7060             : 
    7061             : /**
    7062             :  * \ingroup dblib_core
    7063             :  * \brief Get \c syscharset name of the server character set.
    7064             :  * 
    7065             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7066             :  * \return name of server's charset, as null-terminated ASCII string.
    7067             :  * \sa dbcharsetconv(), dbgetcharset(), DBSETLCHARSET().  
    7068             :  */
    7069             : char *
    7070           0 : dbservcharset(DBPROCESS * dbproc)
    7071             : {
    7072             : 
    7073           0 :         tdsdump_log(TDS_DBG_FUNC, "dbservcharset(%p)\n", dbproc);
    7074           0 :         CHECK_PARAMETER(dbproc, SYBENULL, NULL);
    7075             : 
    7076           0 :         return dbproc->servcharset;
    7077             : }
    7078             : 
    7079             : /**
    7080             :  * \ingroup dblib_core
    7081             :  * \brief Transmit the command buffer to the server.  \em Non-blocking, does not wait for a response.
    7082             :  * 
    7083             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7084             :  * \retval SUCCEED SQL sent.
    7085             :  * \retval FAIL protocol problem, unless dbsqlsend() when it's not supposed to be (in which case a db-lib error
    7086             :  message will be emitted).  
    7087             :  * \sa dbcmd(), dbfcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), dbresults(), dbsettime(), dbsqlexec(), dbsqlok().  
    7088             :  */
    7089             : RETCODE
    7090       20344 : dbsqlsend(DBPROCESS * dbproc)
    7091             : {
    7092             :         TDSSOCKET *tds;
    7093             :         char *cmdstr;
    7094             :         TDSRET rc;
    7095             :         TDS_INT result_type;
    7096             :         char timestr[256];
    7097             : 
    7098       20344 :         tdsdump_log(TDS_DBG_FUNC, "dbsqlsend(%p)\n", dbproc);
    7099       20344 :         CHECK_CONN(FAIL);
    7100             : 
    7101       20344 :         tds = dbproc->tds_socket;
    7102             : 
    7103       20344 :         if (tds->state == TDS_PENDING) {
    7104             : 
    7105         130 :                 if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_TRAILING) != TDS_NO_MORE_RESULTS) {
    7106          70 :                         dbperror(dbproc, SYBERPND, 0);
    7107          70 :                         dbproc->command_state = DBCMDSENT;
    7108          70 :                         return FAIL;
    7109             :                 }
    7110             :         }
    7111             : 
    7112       20274 :         if (dbproc->dboptcmd) {
    7113           0 :                 if ((cmdstr = dbstring_get(dbproc->dboptcmd)) == NULL) {
    7114           0 :                         dbperror(dbproc, SYBEASEC, 0); /* Attempt to send an empty command buffer to the server */
    7115           0 :                         return FAIL;
    7116             :                 }
    7117           0 :                 rc = tds_submit_query(dbproc->tds_socket, cmdstr);
    7118           0 :                 free(cmdstr);
    7119           0 :                 dbstring_free(&(dbproc->dboptcmd));
    7120           0 :                 if (TDS_FAILED(rc)) {
    7121             :                         return FAIL;
    7122             :                 }
    7123           0 :                 dbproc->avail_flag = FALSE;
    7124           0 :                 dbproc->envchange_rcv = 0;
    7125           0 :                 dbproc->dbresults_state = _DB_RES_INIT;
    7126           0 :                 while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))
    7127             :                        == TDS_SUCCESS);
    7128           0 :                 if (rc != TDS_NO_MORE_RESULTS) {
    7129             :                         return FAIL;
    7130             :                 }
    7131             :         }
    7132       20274 :         dbproc->more_results = TRUE;
    7133             : 
    7134       20274 :         if (dbproc->ftos != NULL) {
    7135           0 :                 fprintf(dbproc->ftos, "%s\n", dbproc->dbbuf);
    7136           0 :                 fprintf(dbproc->ftos, "go /* %s */\n", _dbprdate(timestr));
    7137           0 :                 fflush(dbproc->ftos);
    7138             :         }
    7139             : 
    7140       20274 :         if (TDS_FAILED(tds_submit_query(dbproc->tds_socket, (char *) dbproc->dbbuf))) {
    7141             :                 return FAIL;
    7142             :         }
    7143       20264 :         dbproc->avail_flag = FALSE;
    7144       20264 :         dbproc->envchange_rcv = 0;
    7145       20264 :         dbproc->dbresults_state = _DB_RES_INIT;
    7146       20264 :         dbproc->command_state = DBCMDSENT;
    7147       20264 :         return SUCCEED;
    7148             : }
    7149             : 
    7150             : /**
    7151             :  * \ingroup dblib_core
    7152             :  * \brief Get user-defined datatype of a compute column.
    7153             :  * 
    7154             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7155             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    7156             :  * \param column Nth column in \a computeid, starting from 1.
    7157             :  * \returns user-defined datatype of compute column, else -1.
    7158             :  * \sa dbalttype(), dbcolutype().
    7159             :  */
    7160             : DBINT
    7161           0 : dbaltutype(DBPROCESS * dbproc, int computeid, int column)
    7162             : {
    7163             :         TDSCOLUMN *colinfo;
    7164             : 
    7165           0 :         tdsdump_log(TDS_DBG_FUNC, "dbaltutype(%p, %d, %d)\n", dbproc, computeid, column);
    7166             : 
    7167           0 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    7168           0 :         if (!colinfo)
    7169             :                 return -1;
    7170             : 
    7171           0 :         return colinfo->column_usertype;
    7172             : }
    7173             : 
    7174             : /**
    7175             :  * \ingroup dblib_core
    7176             :  * \brief Get size of data in compute column.
    7177             :  * 
    7178             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7179             :  * \param computeid of \c COMPUTE clause to which we're referring. 
    7180             :  * \param column Nth column in \a computeid, starting from 1.
    7181             :  * \sa dbadata(), dbadlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
    7182             :  */
    7183             : DBINT
    7184           0 : dbaltlen(DBPROCESS * dbproc, int computeid, int column)
    7185             : {
    7186             :         TDSCOLUMN *colinfo;
    7187             : 
    7188           0 :         tdsdump_log(TDS_DBG_FUNC, "dbaltlen(%p, %d, %d)\n", dbproc, computeid, column);
    7189             : 
    7190           0 :         colinfo = dbacolptr(dbproc, computeid, column, false);
    7191           0 :         if (!colinfo)
    7192             :                 return -1;
    7193             : 
    7194           0 :         return colinfo->column_size;
    7195             : 
    7196             : }
    7197             : 
    7198             : /**
    7199             :  * \ingroup dblib_core
    7200             :  * \brief See if a server response has arrived.
    7201             :  * 
    7202             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7203             :  * \param milliseconds how long to wait for the server before returning:
    7204             :         - \c  0 return immediately.
    7205             :         - \c -1 do not return until the server responds or a system interrupt occurs.
    7206             :  * \param ready_dbproc \em output: DBPROCESS for which a response arrived, of \c NULL. 
    7207             :  * \param return_reason \em output: 
    7208             :         - \c DBRESULT server responded.
    7209             :         - \c DBNOTIFICATION registered procedure notification has arrived. dbpoll() the registered handler, if
    7210             :                 any, before it returns.
    7211             :         - \c DBTIMEOUT \a milliseconds elapsed before the server responded.
    7212             :         - \c DBINTERRUPT operating-system interrupt occurred before the server responded.
    7213             :  * \retval SUCCEED everything worked.
    7214             :  * \retval FAIL a server connection died.
    7215             :  * \sa  DBIORDESC(), DBRBUF(), dbresults(), dbreghandle(), dbsqlok(). 
    7216             :  * \todo Unimplemented.
    7217             :  */
    7218             : #if defined(DBLIB_UNIMPLEMENTED)
    7219             : RETCODE
    7220             : dbpoll(DBPROCESS * dbproc, long milliseconds, DBPROCESS ** ready_dbproc, int *return_reason)
    7221             : {
    7222             :         tdsdump_log(TDS_DBG_FUNC, "dbpoll(%p, %ld, %p, %p)\n", dbproc, milliseconds, ready_dbproc, return_reason);
    7223             :         CHECK_CONN(FAIL);
    7224             :         CHECK_NULP(ready_dbproc, "dbpoll", 3, FAIL);
    7225             :         CHECK_NULP(return_reason, "dbpoll", 4, FAIL);
    7226             :         tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbpoll()\n");
    7227             :         return SUCCEED;
    7228             : }
    7229             : #endif
    7230             : 
    7231             : /** \internal
    7232             :  * \ingroup dblib_internal
    7233             :  * \brief Get number of the first row in the row buffer.
    7234             :  * 
    7235             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7236             :  * \sa DBFIRSTROW(), dbclrbuf(), DBCURROW(), dbgetrow(), DBLASTROW(), dbnextrow(), dbsetopt(). 
    7237             :  */
    7238             : DBINT
    7239           0 : dbfirstrow(DBPROCESS * dbproc)
    7240             : {
    7241           0 :         tdsdump_log(TDS_DBG_FUNC, "dbfirstrow(%p)\n", dbproc);
    7242           0 :         CHECK_CONN(0);
    7243           0 :         return buffer_idx2row(&dbproc->row_buf, dbproc->row_buf.tail);
    7244             : }
    7245             : 
    7246             : /** \internal
    7247             :  * \ingroup dblib_internal
    7248             :  * \brief Get number of the last row in the row buffer.
    7249             :  * 
    7250             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7251             :  * \sa DBLASTROW(), dbclrbuf(), DBCURROW(), DBFIRSTROW(), dbgetrow(), dbnextrow(), dbsetopt(). 
    7252             :  */
    7253             : DBINT
    7254           0 : dblastrow(DBPROCESS * dbproc)
    7255             : {
    7256             :         int idx;
    7257             : 
    7258           0 :         tdsdump_log(TDS_DBG_FUNC, "dblastrow(%p)\n", dbproc);
    7259           0 :         CHECK_PARAMETER(dbproc, SYBENULL, 0);
    7260           0 :         idx = dbproc->row_buf.head;
    7261           0 :         if (dbproc->row_buf.head != dbproc->row_buf.tail) {
    7262           0 :                 if (--idx < 0) 
    7263           0 :                         idx = dbproc->row_buf.capacity - 1;
    7264             :         }
    7265           0 :         assert(idx >= 0);
    7266           0 :         return buffer_idx2row(&dbproc->row_buf, idx);
    7267             : }
    7268             : 
    7269             : 
    7270             : /** \internal
    7271             :  * \ingroup dblib_internal
    7272             :  * \brief Get file descriptor of the socket used by a \c DBPROCESS to read data coming from the server. (!)
    7273             :  * 
    7274             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7275             :  * \sa dbcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), DBRBUF(), dbresults(), dbsqlok(), dbsqlsend().  
    7276             :  */
    7277             : int
    7278           0 : dbiordesc(DBPROCESS * dbproc)
    7279             : {
    7280           0 :         tdsdump_log(TDS_DBG_FUNC, "dbiordesc(%p)\n", dbproc);
    7281           0 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    7282           0 :         return (int) tds_get_s(dbproc->tds_socket);
    7283             : }
    7284             : 
    7285             : 
    7286             : /** \internal
    7287             :  * \ingroup dblib_internal
    7288             :  * \brief Get file descriptor of the socket used by a \c DBPROCESS to write data coming to the server. (!)
    7289             :  * 
    7290             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7291             :  * \sa dbcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), DBRBUF(), dbresults(), dbsqlok(), dbsqlsend().  
    7292             :  */
    7293             : int
    7294          20 : dbiowdesc(DBPROCESS * dbproc)
    7295             : {
    7296          20 :         tdsdump_log(TDS_DBG_FUNC, "dbiowdesc(%p)\n", dbproc);
    7297          20 :         CHECK_PARAMETER(dbproc, SYBENULL, -1);
    7298             : 
    7299          20 :         return (int) tds_get_s(dbproc->tds_socket);
    7300             : }
    7301             : 
    7302             : DBBOOL
    7303           0 : dbisavail(DBPROCESS * dbproc)
    7304             : {
    7305           0 :         tdsdump_log(TDS_DBG_FUNC, "dbisavail(%p)\n", dbproc);
    7306           0 :         CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
    7307           0 :         return dbproc->avail_flag;
    7308             : }
    7309             : 
    7310             : 
    7311             : /** \internal
    7312             :  * \ingroup dblib_internal
    7313             :  * \brief Mark a \c DBPROCESS as "available".
    7314             :  * 
    7315             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7316             :  * \remarks Basically bogus.  \c FreeTDS behaves the way Sybase's implementation does, but so what?  
    7317             :         Many \c db-lib functions set the \c DBPROCESS to "not available", but only 
    7318             :         dbsetavail() resets it to "available".  
    7319             :  * \sa DBISAVAIL(). DBSETAVAIL().
    7320             :  */
    7321             : void
    7322           0 : dbsetavail(DBPROCESS * dbproc)
    7323             : {
    7324           0 :         tdsdump_log(TDS_DBG_FUNC, "dbsetavail(%p)\n", dbproc);
    7325           0 :         CHECK_PARAMETER(dbproc, SYBENULL, );
    7326           0 :         dbproc->avail_flag = TRUE;
    7327             : }
    7328             : 
    7329             : 
    7330             : /**
    7331             :  * \ingroup dblib_core
    7332             :  * \brief Build a printable string from text containing placeholders for variables.
    7333             :  * 
    7334             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    7335             :  * \param charbuf \em output: buffer that will contain the ASCII null-terminated string built by \c dbstrbuild().
    7336             :  * \param bufsize size of \a charbuf, in bytes. 
    7337             :  * \param text null-terminated ASCII string, with \em placeholders for variables. \em A Placeholder is a
    7338             :  *      three-byte string, made up of: 
    7339             :         - '%' a percent sign
    7340             :         - 0-9 an integer (designates the argument number to use, starting with 1.)
    7341             :         - '!' an exclamation point
    7342             :  * \param formats null-terminated ASCII sprintf-style string.  Has one format specifier for each placeholder in \a text.
    7343             :  * \remarks Following \a formats are the arguments, the values to substitute for the placeholders.  
    7344             :  * \sa dbconvert(), dbdatename(), dbdatepart().  
    7345             :  */
    7346             : RETCODE
    7347           0 : dbstrbuild(DBPROCESS * dbproc, char *charbuf, int bufsize, char *text, char *formats, ...)
    7348             : {
    7349             :         va_list ap;
    7350             :         TDSRET rc;
    7351             :         int resultlen;
    7352             : 
    7353           0 :         tdsdump_log(TDS_DBG_FUNC, "dbstrbuild(%p, %s, %d, %s, %s, ...)\n", dbproc, charbuf, bufsize, text, formats);
    7354           0 :         CHECK_NULP(charbuf, "dbstrbuild", 2, FAIL);
    7355           0 :         CHECK_NULP(text, "dbstrbuild", 4, FAIL);
    7356           0 :         CHECK_NULP(formats, "dbstrbuild", 5, FAIL);
    7357             : 
    7358           0 :         va_start(ap, formats);
    7359           0 :         rc = tds_vstrbuild(charbuf, bufsize, &resultlen, text, TDS_NULLTERM, formats, TDS_NULLTERM, ap);
    7360           0 :         charbuf[resultlen] = '\0';
    7361           0 :         va_end(ap);
    7362           0 :         return TDS_SUCCEED(rc) ? SUCCEED : FAIL;
    7363             : }
    7364             : 
    7365             : static char *
    7366           0 : _dbprdate(char *timestr)
    7367             : {
    7368           0 :         time_t currtime = time(NULL);
    7369             :         
    7370           0 :         assert(timestr);
    7371             : 
    7372           0 :         strcpy(timestr, asctime(gmtime(&currtime)));
    7373           0 :         timestr[strlen(timestr) - 1] = '\0';    /* remove newline */
    7374           0 :         return timestr;
    7375             : 
    7376             : }
    7377             : 
    7378             : static DBINT
    7379           0 : _dbnullable(DBPROCESS * dbproc, int column)
    7380             : {
    7381             :         TDSCOLUMN *colinfo;
    7382             :         TDSRESULTINFO *resinfo;
    7383             : 
    7384           0 :         assert(dbproc && dbproc->tds_socket);
    7385           0 :         resinfo = dbproc->tds_socket->res_info;
    7386           0 :         if (!resinfo || column < 1 || column > resinfo->num_cols)
    7387             :                 return FALSE;
    7388           0 :         colinfo = resinfo->columns[column - 1];
    7389             : 
    7390           0 :         if (colinfo->column_nullable) 
    7391             :                 return TRUE;
    7392           0 :         return FALSE;
    7393             : }
    7394             : 
    7395             : /** Returns type in string. Used for debugging purpose */
    7396             : static const char *
    7397           0 : tds_prdatatype(int datatype_token)
    7398             : {
    7399           0 :         switch ((TDS_SERVER_TYPE) datatype_token) {
    7400             :         case SYBCHAR:           return "SYBCHAR";
    7401           0 :         case SYBVARCHAR:        return "SYBVARCHAR";
    7402           0 :         case SYBINTN:           return "SYBINTN";
    7403           0 :         case SYBINT1:           return "SYBINT1";
    7404           0 :         case SYBINT2:           return "SYBINT2";
    7405           0 :         case SYBINT4:           return "SYBINT4";
    7406           0 :         case SYBINT8:           return "SYBINT8";
    7407           0 :         case SYBFLT8:           return "SYBFLT8";
    7408           0 :         case SYBDATETIME:       return "SYBDATETIME";
    7409           0 :         case SYBBIT:            return "SYBBIT";
    7410           0 :         case SYBTEXT:           return "SYBTEXT";
    7411           0 :         case SYBNTEXT:          return "SYBNTEXT";
    7412           0 :         case SYBIMAGE:          return "SYBIMAGE";
    7413           0 :         case SYBMONEY4:         return "SYBMONEY4";
    7414           0 :         case SYBMONEY:          return "SYBMONEY";
    7415           0 :         case SYBDATETIME4:      return "SYBDATETIME4";
    7416           0 :         case SYBREAL:           return "SYBREAL";
    7417           0 :         case SYBBINARY:         return "SYBBINARY";
    7418           0 :         case SYBVOID:           return "SYBVOID";
    7419           0 :         case SYBVARBINARY:      return "SYBVARBINARY";
    7420           0 :         case SYBNVARCHAR:       return "SYBNVARCHAR";
    7421           0 :         case SYBBITN:           return "SYBBITN";
    7422           0 :         case SYBNUMERIC:        return "SYBNUMERIC";
    7423           0 :         case SYBDECIMAL:        return "SYBDECIMAL";
    7424           0 :         case SYBFLTN:           return "SYBFLTN";
    7425           0 :         case SYBMONEYN:         return "SYBMONEYN";
    7426           0 :         case SYBDATETIMN:       return "SYBDATETIMN";
    7427           0 :         case XSYBCHAR:          return "XSYBCHAR";
    7428           0 :         case XSYBVARCHAR:       return "XSYBVARCHAR";
    7429           0 :         case XSYBNVARCHAR:      return "XSYBNVARCHAR";
    7430           0 :         case XSYBNCHAR:         return "XSYBNCHAR";
    7431           0 :         case XSYBVARBINARY:     return "XSYBVARBINARY";
    7432           0 :         case XSYBBINARY:        return "XSYBBINARY";
    7433           0 :         case SYBLONGBINARY:     return "SYBLONGBINARY";
    7434           0 :         case SYBSINT1:          return "SYBSINT1";
    7435           0 :         case SYBUINT2:          return "SYBUINT2";
    7436           0 :         case SYBUINT4:          return "SYBUINT4";
    7437           0 :         case SYBUINT8:          return "SYBUINT8";
    7438           0 :         case SYBUNIQUE:         return "SYBUNIQUE";
    7439           0 :         case SYBVARIANT:        return "SYBVARIANT";
    7440           0 :         case SYBMSXML:          return "SYBMSXML";
    7441           0 :         case SYBMSDATE:         return "SYBMSDATE";
    7442           0 :         case SYBMSTIME:         return "SYBMSTIME";
    7443           0 :         case SYBMSDATETIME2:    return "SYBMSDATETIME2";
    7444           0 :         case SYBMSDATETIMEOFFSET: return "SYBMSDATETIMEOFFSET";
    7445           0 :         case SYBDATE:           return "SYBDATE";
    7446           0 :         case SYBTIME:           return "SYBTIME";
    7447           0 :         case SYB5BIGDATETIME:   return "SYBBIGDATETIME";
    7448           0 :         case SYB5BIGTIME:       return "SYBBIGTIME";
    7449           0 :         case SYBMSUDT:          return "SYBMSUDT";
    7450           0 :         case SYBUINT1:          return "SYBUINT1";
    7451           0 :         case SYBDATEN:          return "SYBDATEN";
    7452           0 :         case SYB5INT8:          return "SYB5INT8";
    7453           0 :         case SYBINTERVAL:       return "SYBINTERVAL";
    7454           0 :         case SYBTIMEN:          return "SYBTIMEN";
    7455           0 :         case SYBUINTN:          return "SYBUINTN";
    7456           0 :         case SYBUNITEXT:        return "SYBUNITEXT";
    7457           0 :         case SYBXML:            return "SYBXML";
    7458           0 :         case SYBMSTABLE:        return "SYBMSTABLE";
    7459             :         }
    7460           0 :         return "(unknown)";
    7461             : }
    7462             : #if 1
    7463             : void
    7464      410744 : copy_data_to_host_var(DBPROCESS * dbproc, TDS_SERVER_TYPE srctype, const BYTE * src, DBINT srclen, 
    7465             :                       BYTE * dest, DBINT destlen,
    7466             :                       int bindtype, DBINT *indicator)
    7467             : {
    7468             :         CONV_RESULT dres;
    7469             :         DBINT ret;
    7470             :         int len;
    7471      410744 :         DBINT indicator_value = 0;
    7472             : 
    7473      410744 :         bool limited_dest_space = false;
    7474      410744 :         TDS_SERVER_TYPE desttype = dblib_bound_type(bindtype);
    7475             : 
    7476      410744 :         tdsdump_log(TDS_DBG_FUNC, "copy_data_to_host_var(%d [%s] len %d => %d [%s] len %d)\n", 
    7477             :                      srctype, tds_prdatatype(srctype), srclen, desttype, tds_prdatatype(desttype), destlen);
    7478      820990 :         CHECK_NULP(src, "copy_data_to_host_var", 3, );
    7479      410744 :         CHECK_NULP(dest, "copy_data_to_host_var", 6, );
    7480      410744 :         if (desttype == TDS_INVALID_TYPE)
    7481             :                 return;
    7482             :         /* indicator can be NULL */
    7483             : 
    7484      410744 :         assert(srclen >= 0);
    7485             : 
    7486      410744 :         if (destlen > 0) {
    7487         290 :                 limited_dest_space = true;
    7488             :         }
    7489             : 
    7490             :         /* oft times we are asked to convert a data type to itself */
    7491             : 
    7492      410744 :         if (desttype == SYBNUMERIC) {
    7493         240 :                 DBNUMERIC *num = NULL;          /* num->scale is unsigned */
    7494             : 
    7495             :                 /* only MS, use always source */
    7496         240 :                 if (bindtype == SRCNUMERICBIND || bindtype == SRCDECIMALBIND) {
    7497         120 :                         if (is_numeric_type(srctype))
    7498             :                                 num = (DBNUMERIC*) src;
    7499             :                         else
    7500          40 :                                 num = (DBNUMERIC*) dest;
    7501         120 :                 } else if (dbproc->msdblib) {
    7502             :                         /* MS by default use only destination information */
    7503             :                         num = (DBNUMERIC*) dest;
    7504             :                 } else {
    7505             :                         /* Sybase, dbbind means source or default */
    7506             :                         /* TODO if dbbind_ps is used is more complicated */
    7507          60 :                         if (is_numeric_type(srctype))
    7508             :                                 num = (DBNUMERIC*) src;
    7509             :                 }
    7510             :                 if (!num) {
    7511          20 :                         dres.n.precision = 18;
    7512          20 :                         dres.n.scale = 0;
    7513             :                 } else {
    7514         220 :                         dres.n.precision = num->precision;
    7515         220 :                         dres.n.scale = num->scale;
    7516             :                 }
    7517      411850 :         } else if ((srctype == desttype) ||
    7518        2434 :                 (is_similar_type(srctype, desttype))) {
    7519             : 
    7520      410246 :                 tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var() srctype == desttype\n");
    7521      410246 :                 switch (desttype) {
    7522             : 
    7523          10 :                 case SYBVARBINARY:
    7524             :                 case SYBBINARY:
    7525             :                 case SYBIMAGE:
    7526          10 :                         if (bindtype == VARYBINBIND) {
    7527          10 :                                 DBVARYBIN *bin = (DBVARYBIN*) dest;
    7528          10 :                                 if (limited_dest_space) {
    7529          10 :                                         if (srclen > sizeof(bin->array)) {
    7530           0 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7531           0 :                                                 indicator_value = srclen;
    7532           0 :                                                 srclen = sizeof(bin->array);
    7533             :                                         }
    7534             :                                 }
    7535          10 :                                 memcpy(bin->array, src, srclen);
    7536          10 :                                 bin->len = srclen;
    7537          10 :                                 break;
    7538             :                         }
    7539             : 
    7540           0 :                         if (srclen > destlen && destlen >= 0) {
    7541           0 :                                 dbperror(dbproc, SYBECOFL, 0);
    7542             :                         } else {
    7543           0 :                                 memcpy(dest, src, srclen);
    7544           0 :                                 if (srclen < destlen)
    7545           0 :                                         memset(dest + srclen, 0, destlen - srclen);
    7546             :                         }
    7547             :                         break;
    7548             : 
    7549      205622 :                 case SYBCHAR:
    7550             :                 case SYBVARCHAR:
    7551             :                 case SYBTEXT:
    7552             : 
    7553      205622 :                         switch (bindtype) {
    7554             :                                 case NTBSTRINGBIND: /* strip trailing blanks, null term */
    7555         574 :                                         while (srclen && src[srclen - 1] == ' ') {
    7556         252 :                                                 --srclen;
    7557             :                                         }
    7558         322 :                                         if (limited_dest_space) {
    7559          40 :                                                 if (srclen + 1 > destlen) {
    7560          10 :                                                         dbperror(dbproc, SYBECOFL, 0);
    7561          10 :                                                         indicator_value = srclen + 1;
    7562          10 :                                                         srclen = destlen - 1;
    7563             :                                                 }
    7564             :                                         }
    7565         322 :                                         memcpy(dest, src, srclen);
    7566         322 :                                         dest[srclen] = '\0';
    7567         322 :                                         break;
    7568      205240 :                                 case STRINGBIND:   /* pad with blanks, NUL term */
    7569      205240 :                                         if (limited_dest_space) {
    7570          30 :                                                 if (srclen + 1 > destlen) {
    7571          20 :                                                         dbperror(dbproc, SYBECOFL, 0);
    7572          20 :                                                         indicator_value = srclen + 1;
    7573          20 :                                                         srclen = destlen - 1;
    7574             :                                                 }
    7575             :                                         } else {
    7576      205210 :                                                 destlen = srclen + 1;
    7577             :                                         }
    7578      205240 :                                         memcpy(dest, src, srclen);
    7579      205240 :                                         if (srclen < destlen)
    7580      205240 :                                                 memset(dest + srclen, ' ', destlen - srclen - 1);
    7581      205240 :                                         dest[destlen - 1] = '\0';
    7582      205240 :                                         break;
    7583          40 :                                 case CHARBIND:   /* pad with blanks, NO NUL term */
    7584          40 :                                         if (limited_dest_space) {
    7585          30 :                                                 if (srclen > destlen) {
    7586          10 :                                                         dbperror(dbproc, SYBECOFL, 0);
    7587          10 :                                                         indicator_value = srclen;
    7588          10 :                                                         srclen = destlen;
    7589             :                                                 }
    7590             :                                         } else {
    7591             :                                                 destlen = srclen; 
    7592             :                                         }
    7593          40 :                                         memcpy(dest, src, srclen);
    7594          40 :                                         if (srclen < destlen)
    7595          10 :                                                 memset(dest + srclen, ' ', destlen - srclen);
    7596             :                                         break;
    7597          20 :                                 case VARYCHARBIND: /* strip trailing blanks, NO NUL term */
    7598          20 :                                         if (limited_dest_space) {
    7599          20 :                                                 if (srclen > destlen) {
    7600           0 :                                                         dbperror(dbproc, SYBECOFL, 0);
    7601           0 :                                                         indicator_value = srclen;
    7602           0 :                                                         srclen = destlen;
    7603             :                                                 }
    7604             :                                         }
    7605          20 :                                         memcpy(((DBVARYCHAR *)dest)->str, src, srclen);
    7606          20 :                                         ((DBVARYCHAR *)dest)->len = srclen;
    7607          20 :                                         break;
    7608             :                         } 
    7609             :                         break;
    7610      204614 :                 case SYBINT1:
    7611             :                 case SYBINT2:
    7612             :                 case SYBINT4:
    7613             :                 case SYBINT8:
    7614             :                 case SYBFLT8:
    7615             :                 case SYBREAL:
    7616             :                 case SYBBIT:
    7617             :                 case SYBBITN:
    7618             :                 case SYBMONEY:
    7619             :                 case SYBMONEY4:
    7620             :                 case SYBDATETIME:
    7621             :                 case SYBDATETIME4:
    7622             :                 case SYBDATE:
    7623             :                 case SYBTIME:
    7624             :                 case SYB5BIGDATETIME:
    7625             :                 case SYB5BIGTIME:
    7626             :                 case SYBUNIQUE:
    7627      204614 :                         ret = tds_get_size_by_type(desttype);
    7628      204614 :                         memcpy(dest, src, ret);
    7629      204614 :                         break;
    7630             : 
    7631           0 :                 case SYBMSDATE:
    7632             :                 case SYBMSTIME:
    7633             :                 case SYBMSDATETIME2:
    7634             :                 case SYBMSDATETIMEOFFSET:
    7635           0 :                         ret = sizeof(TDS_DATETIMEALL);
    7636           0 :                         memcpy(dest, src, ret);
    7637           0 :                         break;
    7638             : 
    7639             :                 default:
    7640             :                         break;
    7641             :                 }
    7642      410246 :                 if (indicator)
    7643         136 :                         *indicator = indicator_value;
    7644             : 
    7645             :                 return;
    7646             : 
    7647             :         } /* end srctype == desttype */
    7648             : 
    7649         498 :         len = tds_convert(g_dblib_ctx.tds_ctx, srctype, src, srclen, desttype, &dres);
    7650             : 
    7651         498 :         tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var(): tds_convert returned %d\n", len);
    7652             : 
    7653         498 :         if (len < 0) {
    7654           0 :                 _dblib_convert_err(dbproc, len);
    7655           0 :                 return;
    7656             :         }
    7657             : 
    7658         498 :         switch (desttype) {
    7659          60 :         case SYBVARBINARY:
    7660             :         case SYBBINARY:
    7661             :         case SYBIMAGE:
    7662          60 :                 if (bindtype == VARYBINBIND) {
    7663          30 :                         if (limited_dest_space) {
    7664          30 :                                 if (len > sizeof(((DBVARYBIN *)dest)->array)) {
    7665           0 :                                         dbperror(dbproc, SYBECOFL, 0);
    7666           0 :                                         indicator_value = len;
    7667           0 :                                         len = sizeof(((DBVARYBIN *)dest)->array);
    7668             :                                 }
    7669             :                         }
    7670          30 :                         memcpy(((DBVARYBIN *)dest)->array, dres.c, len);
    7671          30 :                         ((DBVARYBIN *)dest)->len = len;
    7672             :                 } else {
    7673          30 :                         if (len > destlen && destlen >= 0) {
    7674           0 :                                 dbperror(dbproc, SYBECOFL, 0);
    7675             :                         } else {
    7676          30 :                                 memcpy(dest, dres.ib, len);
    7677          30 :                                 if (len < destlen)
    7678          10 :                                         memset(dest + len, 0, destlen - len);
    7679             :                         }
    7680             :                 }
    7681          60 :                 TDS_ZERO_FREE(dres.ib);
    7682          60 :                 break;
    7683         250 :         case SYBINT1:
    7684             :         case SYBINT2:
    7685             :         case SYBINT4:
    7686             :         case SYBINT8:
    7687             :         case SYBFLT8:
    7688             :         case SYBREAL:
    7689             :         case SYBBIT:
    7690             :         case SYBBITN:
    7691             :         case SYBMONEY:
    7692             :         case SYBMONEY4:
    7693             :         case SYBDATETIME:
    7694             :         case SYBDATETIME4:
    7695             :         case SYBDATE:
    7696             :         case SYBTIME:
    7697             :         case SYBNUMERIC:
    7698             :         case SYBDECIMAL:
    7699             :         case SYBUNIQUE:
    7700             :         case SYBMSDATE:
    7701             :         case SYBMSTIME:
    7702             :         case SYB5BIGDATETIME:
    7703             :         case SYB5BIGTIME:
    7704             :         case SYBMSDATETIME2:
    7705             :         case SYBMSDATETIMEOFFSET:
    7706         250 :                 memcpy(dest, &(dres.ti), len);
    7707         250 :                 break;
    7708         188 :         case SYBCHAR:
    7709             :         case SYBVARCHAR:
    7710             :         case SYBTEXT:
    7711         188 :                 tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var() outputs %d bytes char data destlen = %d \n", len, destlen);
    7712         188 :                 switch (bindtype) {
    7713             :                         case NTBSTRINGBIND: /* strip trailing blanks, null term */
    7714          40 :                                 while (len && dres.c[len - 1] == ' ') {
    7715           0 :                                         --len;
    7716             :                                 }
    7717          40 :                                 if (limited_dest_space) {
    7718          30 :                                         if (len + 1 > destlen) {
    7719          10 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7720          10 :                                                 len = destlen - 1;
    7721             :                                         }
    7722             :                                 }
    7723          40 :                                 memcpy(dest, dres.c, len);
    7724          40 :                                 dest[len] = '\0';
    7725          40 :                                 break;
    7726          98 :                         case STRINGBIND:   /* pad with blanks, null term */
    7727          98 :                                 if (limited_dest_space) {
    7728          30 :                                         if (len + 1 > destlen) {
    7729          10 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7730          10 :                                                 len = destlen - 1;
    7731             :                                         }
    7732             :                                 } else {
    7733          68 :                                         destlen = len + 1;
    7734             :                                 }
    7735          98 :                                 memcpy(dest, dres.c, len);
    7736          98 :                                 if (len < destlen)
    7737          98 :                                         memset(dest + len, ' ', destlen - len - 1);
    7738          98 :                                 dest[destlen - 1] = '\0';
    7739          98 :                                 break;
    7740          50 :                         case CHARBIND:   /* pad with blanks, NO null term */
    7741          50 :                                 if (limited_dest_space) {
    7742          40 :                                         if (len > destlen) {
    7743          10 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7744          10 :                                                 indicator_value = len;
    7745          10 :                                                 len = destlen;
    7746             :                                         }
    7747             :                                 } else {
    7748             :                                         destlen = len; 
    7749             :                                 }
    7750          50 :                                 memcpy(dest, dres.c, len);
    7751          50 :                                 if (len < destlen)
    7752          20 :                                         memset(dest + len, ' ', destlen - len);
    7753             :                                 break;
    7754           0 :                         case VARYCHARBIND: /* strip trailing blanks, NO null term */
    7755           0 :                                 if (limited_dest_space) {
    7756           0 :                                         if (len > sizeof(((DBVARYCHAR *)dest)->str)) {
    7757           0 :                                                 dbperror(dbproc, SYBECOFL, 0);
    7758           0 :                                                 indicator_value = len;
    7759           0 :                                                 len = sizeof(((DBVARYCHAR *)dest)->str);
    7760             :                                         }
    7761             :                                 } 
    7762           0 :                                 memcpy(((DBVARYCHAR *)dest)->str, dres.c, len);
    7763           0 :                                 ((DBVARYCHAR *)dest)->len = len;
    7764           0 :                                 break;
    7765             :                 } 
    7766             : 
    7767         188 :                 free(dres.c);
    7768         188 :                 break;
    7769           0 :         default:
    7770           0 :                 tdsdump_log(TDS_DBG_INFO1, "error: copy_data_to_host_var(): unrecognized desttype %d \n", desttype);
    7771             :                 break;
    7772             : 
    7773             :         }
    7774         498 :         if (indicator)
    7775           0 :                 *indicator = indicator_value;
    7776             : }
    7777             : #endif
    7778             : 
    7779             : /** \internal
    7780             :  * \ingroup dblib_internal
    7781             :  * \remarks member msgno Vendor-defined message number
    7782             :  * \remarks member severity Is passed to the error handler 
    7783             :  * \remarks member msgtext Text of message
    7784             :  */
    7785             : typedef struct _dblib_error_message 
    7786             : {
    7787             :         DBINT msgno;
    7788             :         int severity;
    7789             :         const char *msgtext;
    7790             : } DBLIB_ERROR_MESSAGE;
    7791             : 
    7792             : /*
    7793             :  * The msgtext member holds up to two strings.  The first one is the message text, which may contain placeholders.  
    7794             :  * The second one, if it exists, is the format string for dbstrbuild().  Messages containing no placeholders still need
    7795             :  * an extra NULL to indicate a zero-length format string. 
    7796             :  */
    7797             : static const DBLIB_ERROR_MESSAGE dblib_error_messages[] = 
    7798             :         { { SYBEVERDOWN,           EXINFO,      "TDS version downgraded to 7.1!\0" }
    7799             :         , { SYBEICONVIU,     EXCONVERSION,      "Some character(s) could not be converted into client's character set\0" }
    7800             :         , { SYBEICONVAVAIL,  EXCONVERSION,      "Character set conversion is not available between client character set '%1!' and "
    7801             :                                                 "server character set '%2!'\0%s %s" }
    7802             :         , { SYBEICONVO,      EXCONVERSION,      "Error converting characters into server's character set. Some character(s) could "
    7803             :                                                 "not be converted\0" }
    7804             :         , { SYBEICONVI,      EXCONVERSION,      "Some character(s) could not be converted into client's character set.  Unconverted "
    7805             :                                                 "bytes were changed to question marks ('?')\0" }
    7806             :         , { SYBEICONV2BIG,   EXCONVERSION,      "Buffer overflow converting characters from client into server's character set\0" }
    7807             :         
    7808             :         
    7809             :         , { SYBEPORT,              EXUSER,      "Both port and instance specified\0" }
    7810             :         , { SYBETDSVER,            EXUSER,      "Cannot bcp with TDSVER < 5.0\0" }
    7811             :         , { SYBEAAMT,           EXPROGRAM,      "User attempted a dbaltbind with mismatched column and variable types\0" }
    7812             :         , { SYBEABMT,           EXPROGRAM,      "User attempted a dbbind with mismatched column and variable types\0" }
    7813             :         , { SYBEABNC,           EXPROGRAM,      "Attempt to bind to a non-existent column\0" }
    7814             :         , { SYBEABNP,           EXPROGRAM,      "Attempt to bind using NULL pointers\0" }
    7815             :         , { SYBEABNV,           EXPROGRAM,      "Attempt to bind to a NULL program variable\0" }
    7816             :         , { SYBEACNV,        EXCONVERSION,      "Attempt to do data-conversion with NULL destination variable.\0" }
    7817             :         , { SYBEADST,       EXCONSISTENCY,      "International Release: Error in attempting to determine the size of a pair of "
    7818             :                                                 "translation tables\0" }
    7819             :         , { SYBEAICF,       EXCONSISTENCY,      "International Release: Error in attempting to install custom format\0" }
    7820             :         , { SYBEALTT,       EXCONSISTENCY,      "International Release: Error in attempting to load a pair of translation tables\0" }
    7821             :         , { SYBEAOLF,          EXRESOURCE,      "International Release: Error in attempting to open a localization file\0" }
    7822             :         , { SYBEAPCT,       EXCONSISTENCY,      "International Release: Error in attempting to perform a character set translation\0" }
    7823             :         , { SYBEAPUT,           EXPROGRAM,      "Attempt to print unknown token\0" }
    7824             :         , { SYBEARDI,          EXRESOURCE,      "International Release: Error in attempting to read datetime information from a "
    7825             :                                                 "localization file\0" }
    7826             :         , { SYBEARDL,          EXRESOURCE,      "International Release: Error in attempting to read the dblib.loc localization file\0" }
    7827             :         , { SYBEASEC,           EXPROGRAM,      "Attempt to send an empty command buffer to the server\0" }
    7828             :         , { SYBEASNL,           EXPROGRAM,      "Attempt to set fields in a null LOGINREC\0" }
    7829             :         , { SYBEASTL,           EXPROGRAM,      "Synchronous I/O attempted at AST level\0" }
    7830             :         , { SYBEASUL,           EXPROGRAM,      "Attempt to set unknown LOGINREC field\0" }
    7831             :         , { SYBEAUTN,           EXPROGRAM,      "Attempt to update the timestamp of a table that has no timestamp column\0" }
    7832             :         , { SYBEBADPK,             EXINFO,      "Packet size of %1! not supported -- size of %2! used instead!\0%d %d" }
    7833             :         , { SYBEBBCI,              EXINFO,      "Batch successfully bulk copied to the server\0" }
    7834             :         , { SYBEBBL,            EXPROGRAM,      "Bad bindlen parameter passed to dbsetnull\0" }
    7835             :         , { SYBEBCBC,           EXPROGRAM,      "bcp_columns must be called before bcp_colfmt and bcp_colfmt_ps\0" }
    7836             :         , { SYBEBCBNPR,         EXPROGRAM,      "bcp_bind: if varaddr is NULL, prefixlen must be 0 "
    7837             :                                                 "and no terminator should be specified\0" }
    7838             :         , { SYBEBCBNTYP,        EXPROGRAM,      "bcp_bind: if varaddr is NULL and varlen greater than 0, the table column type "
    7839             :                                                 "must be SYBTEXT or SYBIMAGE and the program variable type must be SYBTEXT, SYBCHAR, "
    7840             :                                                 "SYBIMAGE or SYBBINARY\0" }
    7841             :         , { SYBEBCBPREF,        EXPROGRAM,      "Illegal prefix length. Legal values are 0, 1, 2 or 4\0" }
    7842             :         , { SYBEBCFO,              EXUSER,      "bcp host files must contain at least one column\0" }
    7843             :         , { SYBEBCHLEN,         EXPROGRAM,      "host_collen should be greater than or equal to -1\0" }
    7844             :         , { SYBEBCIS,       EXCONSISTENCY,      "Attempt to bulk copy an illegally-sized column value to the server\0" }
    7845             :         , { SYBEBCIT,           EXPROGRAM,      "It is illegal to use BCP terminators with program variables other than SYBCHAR, "
    7846             :                                                 "SYBBINARY, SYBTEXT, or SYBIMAGE\0" }
    7847             :         , { SYBEBCITBLEN,       EXPROGRAM,      "bcp_init: tblname parameter is too long\0" }
    7848             :         , { SYBEBCITBNM,        EXPROGRAM,      "bcp_init: tblname parameter cannot be NULL\0" }
    7849             :         , { SYBEBCMTXT,         EXPROGRAM,      "bcp_moretext may be used only when there is at least one text or image column in "
    7850             :                                                 "the server table\0" }
    7851             :         , { SYBEBCNL,          EXNONFATAL,      "Negative length-prefix found in BCP data-file\0" }
    7852             :         , { SYBEBCNN,              EXUSER,      "Attempt to bulk copy a NULL value into a Server column "
    7853             :                                                 "which does not accept null values\0" }
    7854             :         , { SYBEBCNT,              EXUSER,      "Attempt to use Bulk Copy with a non-existent Server table\0" }
    7855             :         , { SYBEBCOR,       EXCONSISTENCY,      "Attempt to bulk copy an oversized row to the server\0" }
    7856             :         , { SYBEBCPB,           EXPROGRAM,      "bcp_bind, bcp_moretext and bcp_sendrow may not be used after bcp_init has been "
    7857             :                                                 "passed a non-NULL input file name\0" }
    7858             :         , { SYBEBCPCTYP,        EXPROGRAM,      "bcp_colfmt: If table_colnum is 0, host_type cannot be 0\0" }
    7859             :         , { SYBEBCPI,           EXPROGRAM,      "bcp_init must be called before any other bcp routines\0" }
    7860             :         , { SYBEBCPN,           EXPROGRAM,      "bcp_bind, bcp_collen, bcp_colptr, bcp_moretext and bcp_sendrow may be used only "
    7861             :                                                 "after bcp_init has been called with the copy direction set to DB_IN\0" }
    7862             :         , { SYBEBCPREC,        EXNONFATAL,      "Column %1!: Illegal precision value encountered\0%d" }
    7863             :         , { SYBEBCPREF,         EXPROGRAM,      "Illegal prefix length. Legal values are -1, 0, 1, 2 or 4\0" }
    7864             :         , { SYBEBCRE,          EXNONFATAL,      "I/O error while reading bcp datafile\0" }
    7865             :         , { SYBEBCRO,              EXINFO,      "The BCP hostfile '%1!' contains only %2! rows. It was impossible to read the "
    7866             :                                                 "requested %3! rows\0%s %d %d" }
    7867             :         , { SYBEBCSA,              EXUSER,      "The BCP hostfile '%1!' contains only %2! rows. "
    7868             :                                                 "Skipping all of these rows is not allowed\0%s %d" }
    7869             :         , { SYBEBCSET,      EXCONSISTENCY,      "Unknown character-set encountered\0" }
    7870             :         , { SYBEBCSI,           EXPROGRAM,      "Host-file columns may be skipped only when copying into the Server\0" }
    7871             :         , { SYBEBCSNDROW,       EXPROGRAM,      "bcp_sendrow may not be called unless all text data for the previous row has been "
    7872             :                                                 "sent using bcp_moretext\0" }
    7873             :         , { SYBEBCSNTYP,        EXPROGRAM,      "column number %1!: if varaddr is NULL and varlen greater than 0, the table column "
    7874             :                                                 "type must be SYBTEXT or SYBIMAGE and the program variable type must be SYBTEXT, "
    7875             :                                                 "SYBCHAR, SYBIMAGE or SYBBINARY\0%d" }
    7876             :         , { SYBEBCUC,          EXRESOURCE,      "bcp: Unable to close host datafile\0" }
    7877             :         , { SYBEBCUO,          EXRESOURCE,      "bcp: Unable to open host datafile\0" }
    7878             :         , { SYBEBCVH,           EXPROGRAM,      "bcp_exec may be called only after bcp_init has been passed a valid host file\0" }
    7879             :         , { SYBEBCVLEN,         EXPROGRAM,      "varlen should be greater than or equal to -1\0" }
    7880             :         , { SYBEBCWE,          EXNONFATAL,      "I/O error while writing bcp datafile\0" }
    7881             :         , { SYBEBDIO,           EXPROGRAM,      "Bad bulk copy direction. Must be either IN or OUT\0" }
    7882             :         , { SYBEBEOF,          EXNONFATAL,      "Unexpected EOF encountered in bcp datafile\0" }
    7883             :         , { SYBEBIHC,           EXPROGRAM,      "Incorrect host-column number found in bcp format file\0" }
    7884             :         , { SYBEBIVI,           EXPROGRAM,      "bcp_columns, bcp_colfmt and bcp_colfmt_ps may be used only after bcp_init has been "
    7885             :                                                 "passed a valid input file\0" }
    7886             :         , { SYBEBNCR,           EXPROGRAM,      "Attempt to bind user variable to a non-existent compute row\0" }
    7887             :         , { SYBEBNUM,           EXPROGRAM,      "Bad numbytes parameter passed to dbstrcpy\0" }
    7888             :         , { SYBEBPKS,           EXPROGRAM,      "In DBSETLPACKET, the packet size parameter must be between 0 and 999999\0" }
    7889             :         , { SYBEBPREC,          EXPROGRAM,      "Illegal precision specified\0" }
    7890             :         , { SYBEBPROBADDEF, EXCONSISTENCY,      "bcp protocol error: illegal default column id received\0" }
    7891             :         , { SYBEBPROCOL,    EXCONSISTENCY,      "bcp protocol error: returned column count differs from the actual number of "
    7892             :                                                 "columns received\0" }
    7893             :         , { SYBEBPRODEF,    EXCONSISTENCY,      "bcp protocol error: expected default information and got none\0" }
    7894             :         , { SYBEBPRODEFID,  EXCONSISTENCY,      "bcp protocol error: default column id and actual column id are not same\0" }
    7895             :         , { SYBEBPRODEFTYP, EXCONSISTENCY,      "bcp protocol error: default value datatype differs from column datatype\0" }
    7896             :         , { SYBEBPROEXTDEF, EXCONSISTENCY,      "bcp protocol error: more than one row of default information received\0" }
    7897             :         , { SYBEBPROEXTRES, EXCONSISTENCY,      "bcp protocol error: unexpected set of results received\0" }
    7898             :         , { SYBEBPRONODEF,  EXCONSISTENCY,      "bcp protocol error: default value received for column that does not have default\0" }
    7899             :         , { SYBEBPRONUMDEF, EXCONSISTENCY,      "bcp protocol error: expected number of defaults differs from the actual number of "
    7900             :                                                 "defaults received\0" }
    7901             :         , { SYBEBRFF,          EXRESOURCE,      "I/O error while reading bcp format file\0" }
    7902             :         , { SYBEBSCALE,         EXPROGRAM,      "Illegal scale specified\0" }
    7903             :         , { SYBEBTMT,           EXPROGRAM,      "Attempt to send too much text data via the bcp_moretext call\0" }
    7904             :         , { SYBEBTOK,              EXCOMM,      "Bad token from the server: Datastream processing out of sync\0" }
    7905             :         , { SYBEBTYP,           EXPROGRAM,      "Unknown bind type passed to DB-Library function\0" }
    7906             :         , { SYBEBTYPSRV,        EXPROGRAM,      "Datatype is not supported by the server\0" }
    7907             :         , { SYBEBUCE,          EXRESOURCE,      "bcp: Unable to close error file\0" }
    7908             :         , { SYBEBUCF,           EXPROGRAM,      "bcp: Unable to close format file\0" }
    7909             :         , { SYBEBUDF,           EXPROGRAM,      "bcp: Unrecognized datatype found in format file\0" }
    7910             :         , { SYBEBUFF,           EXPROGRAM,      "bcp: Unable to create format file\0" }
    7911             :         , { SYBEBUFL,       EXCONSISTENCY,      "DB-Library internal error-send buffer length corrupted\0" }
    7912             :         , { SYBEBUOE,          EXRESOURCE,      "bcp: Unable to open error file\0" }
    7913             :         , { SYBEBUOF,           EXPROGRAM,      "bcp: Unable to open format file\0" }
    7914             :         , { SYBEBWEF,          EXNONFATAL,      "I/O error while writing bcp error file\0" }
    7915             :         , { SYBEBWFF,          EXRESOURCE,      "I/O error while writing bcp format file\0" }
    7916             :         , { SYBECAP,               EXCOMM,      "DB-Library capabilities not accepted by the Server\0" }
    7917             :         , { SYBECAPTYP,            EXCOMM,      "Unexpected capability type in CAPABILITY datastream\0" }
    7918             :         , { SYBECDNS,       EXCONSISTENCY,      "Datastream indicates that a compute column is derived from a non-existent select "
    7919             :                                                 "list member\0" }
    7920             :         , { SYBECDOMAIN,     EXCONVERSION,      "Source field value is not within the domain of legal values\0" }
    7921             :         , { SYBECINTERNAL,   EXCONVERSION,      "Internal Conversion error\0" }
    7922             :         , { SYBECLOS,              EXCOMM,      "Error in closing network connection\0" }
    7923             :         , { SYBECLPR,        EXCONVERSION,      "Data conversion resulted in loss of precision\0" }
    7924             :         , { SYBECNOR,           EXPROGRAM,      "Column number out of range\0" }
    7925             :         , { SYBECNOV,        EXCONVERSION,      "Attempt to set variable to NULL resulted in overflow\0" }
    7926             :         , { SYBECOFL,        EXCONVERSION,      "Data conversion resulted in overflow\0" }
    7927             :         , { SYBECONN,              EXCOMM,      "Unable to connect: TDS server is unavailable or does not exist\0" }
    7928             :         , { SYBECRNC,           EXPROGRAM,      "The current row is not a result of compute clause %1!, so it is illegal to attempt "
    7929             :                                                 "to extract that data from this row\0%d" }
    7930             :         , { SYBECRSAGR,         EXPROGRAM,      "Aggregate functions are not allowed in a cursor statement\0" }
    7931             :         , { SYBECRSBROL,        EXPROGRAM,      "Backward scrolling cannot be used in a forward scrolling cursor\0" }
    7932             :         , { SYBECRSBSKEY,       EXPROGRAM,      "Keyset cannot be scrolled backward in mixed cursors with a previous fetch type\0" }
    7933             :         , { SYBECRSBUFR,        EXPROGRAM,      "Row buffering should not be turned on when using cursor APIs\0" }
    7934             :         , { SYBECRSDIS,         EXPROGRAM,      "Cursor statement contains one of the disallowed phrases compute, union, for "
    7935             :                                                 "browse, or select into\0" }
    7936             :         , { SYBECRSFLAST,       EXPROGRAM,      "Fetch type LAST requires fully keyset driven cursors\0" }
    7937             :         , { SYBECRSFRAND,       EXPROGRAM,      "Fetch types RANDOM and RELATIVE can only be used within the keyset of keyset "
    7938             :                                                 "driven cursors\0" }
    7939             :         , { SYBECRSFROWN,       EXPROGRAM,      "Row number to be fetched is outside valid range\0" }
    7940             :         , { SYBECRSFTYPE,      EXRESOURCE,      "Unknown fetch type\0" }
    7941             :         , { SYBECRSINV,         EXPROGRAM,      "Invalid cursor statement\0" }
    7942             :         , { SYBECRSINVALID,    EXRESOURCE,      "The cursor handle is invalid\0" }
    7943             :         , { SYBECRSMROWS,      EXRESOURCE,      "Multiple rows are returned, only one is expected while retrieving dbname\0" }
    7944             :         , { SYBECRSNOBIND,      EXPROGRAM,      "Cursor bind must be called prior to dbcursor invocation\0" }
    7945             :         , { SYBECRSNOCOUNT,     EXPROGRAM,      "The DBNOCOUNT option should not be turned on "
    7946             :                                                 "when doing updates or deletes with dbcursor\0" }
    7947             :         , { SYBECRSNOFREE,      EXPROGRAM,      "The DBNOAUTOFREE option should not be turned on when using cursor APIs\0" }
    7948             :         , { SYBECRSNOIND,       EXPROGRAM,      "One of the tables involved in the cursor statement does not have a unique index\0" }
    7949             :         , { SYBECRSNOKEYS,     EXRESOURCE,      "The entire keyset must be defined for KEYSET type cursors\0" }
    7950             :         , { SYBECRSNOLEN,      EXRESOURCE,      "No unique index found\0" }
    7951             :         , { SYBECRSNOPTCC,     EXRESOURCE,      "No OPTCC was found\0" }
    7952             :         , { SYBECRSNORDER,     EXRESOURCE,      "The order of clauses must be from, where, and order by\0" }
    7953             :         , { SYBECRSNORES,       EXPROGRAM,      "Cursor statement generated no results\0" }
    7954             :         , { SYBECRSNROWS,      EXRESOURCE,      "No rows returned, at least one is expected\0" }
    7955             :         , { SYBECRSNOTABLE,    EXRESOURCE,      "Table name is NULL\0" }
    7956             :         , { SYBECRSNOUPD,       EXPROGRAM,      "Update or delete operation did not affect any rows\0" }
    7957             :         , { SYBECRSNOWHERE,     EXPROGRAM,      "A WHERE clause is not allowed in a cursor update or insert\0" }
    7958             :         , { SYBECRSNUNIQUE,    EXRESOURCE,      "No unique keys associated with this view\0" }
    7959             :         , { SYBECRSORD,         EXPROGRAM,      "Only fully keyset driven cursors can have order by, group by, or having phrases\0" }
    7960             :         , { SYBECRSRO,          EXPROGRAM,      "Data locking or modifications cannot be made in a READONLY cursor\0" }
    7961             :         , { SYBECRSSET,         EXPROGRAM,      "A SET clause is required for a cursor update or insert\0" }
    7962             :         , { SYBECRSTAB,         EXPROGRAM,      "Table name must be determined in operations involving data locking or modifications\0" }
    7963             :         , { SYBECRSVAR,        EXRESOURCE,      "There is no valid address associated with this bind\0" }
    7964             :         , { SYBECRSVIEW,        EXPROGRAM,      "A view cannot be joined with another table or a view in a cursor statement\0" }
    7965             :         , { SYBECRSVIIND,       EXPROGRAM,      "The view used in the cursor statement does not include all the unique index "
    7966             :                                                 "columns of the underlying tables\0" }
    7967             :         , { SYBECRSUPDNB,       EXPROGRAM,      "Update or insert operations cannot use bind variables when binding type is NOBIND\0" }
    7968             :         , { SYBECRSUPDTAB,      EXPROGRAM,      "Update or insert operations using bind variables require single table cursors\0" }
    7969             :         , { SYBECSYN,        EXCONVERSION,      "Attempt to convert data stopped by syntax error in source field\0" }
    7970             :         , { SYBECUFL,        EXCONVERSION,      "Data conversion resulted in underflow\0" }
    7971             :         , { SYBECWLL,           EXPROGRAM,      "Attempt to set column width less than 1\0" }
    7972             :         , { SYBEDBPS,          EXRESOURCE,      "Maximum number of DBPROCESSes already allocated\0" }
    7973             :         , { SYBEDDNE,              EXCOMM,      "DBPROCESS is dead or not enabled\0" }
    7974             :         , { SYBEDIVZ,              EXUSER,      "Attempt to divide by $0.00 in function %1!\0%s" }
    7975             :         , { SYBEDNTI,           EXPROGRAM,      "Attempt to use dbtxtsput to put a new text timestamp into a column whose datatype "
    7976             :                                                 "is neither SYBTEXT nor SYBIMAGE\0" }
    7977             :         , { SYBEDPOR,           EXPROGRAM,      "Out-of-range datepart constant\0" }
    7978             :         , { SYBEDVOR,           EXPROGRAM,      "Day values must be between 1 and 7\0" }
    7979             :         , { SYBEECAN,              EXINFO,      "Attempted to cancel unrequested event notification\0" }
    7980             :         , { SYBEEINI,              EXINFO,      "Must call dbreginit before dbregexec\0" }
    7981             :         , { SYBEETD,            EXPROGRAM,      "Failure to send the expected amount of TEXT or IMAGE data via dbmoretext\0" }
    7982             :         , { SYBEEUNR,              EXCOMM,      "Unsolicited event notification received\0" }
    7983             :         , { SYBEEVOP,              EXINFO,      "Called dbregwatch with a bad options parameter\0" }
    7984             :         , { SYBEEVST,              EXINFO,      "Must initiate a transaction before calling dbregparam\0" }
    7985             :         , { SYBEFCON,              EXCOMM,      "TDS server connection failed\0" }
    7986             :         , { SYBEFRES,             EXFATAL,      "Challenge-Response function failed\0" }
    7987             :         , { SYBEFSHD,          EXRESOURCE,      "Error in attempting to find the Sybase home directory\0" }
    7988             :         , { SYBEFUNC,           EXPROGRAM,      "Functionality not supported at the specified version level\0" }
    7989             :         , { SYBEICN,            EXPROGRAM,      "Invalid computeid or compute column number\0" }
    7990             :         , { SYBEIDCL,       EXCONSISTENCY,      "Illegal datetime column length returned by TDS server. Legal datetime lengths "
    7991             :                                                 "are 4 and 8 bytes\0" }
    7992             :         , { SYBEIDECCL,     EXCONSISTENCY,      "Invalid decimal column length returned by the server\0" }
    7993             :         , { SYBEIFCL,       EXCONSISTENCY,      "Illegal floating-point column length returned by TDS server. Legal "
    7994             :                                                 "floating-point lengths are 4 and 8 bytes\0" }
    7995             :         , { SYBEIFNB,           EXPROGRAM,      "Illegal field number passed to bcp_control\0" }
    7996             :         , { SYBEIICL,       EXCONSISTENCY,      "Illegal integer column length returned by TDS server. Legal integer lengths "
    7997             :                                                 "are 1, 2, and 4 bytes\0" }
    7998             :         , { SYBEIMCL,       EXCONSISTENCY,      "Illegal money column length returned by TDS server. Legal money lengths are 4 "
    7999             :                                                 "and 8 bytes\0" }
    8000             :         , { SYBEINLN,              EXUSER,      "Interface file: unexpected end-of-line\0" }
    8001             :         , { SYBEINTF,              EXUSER,      "Server name not found in configuration files\0" }
    8002             :         , { SYBEINUMCL,     EXCONSISTENCY,      "Invalid numeric column length returned by the server\0" }
    8003             :         , { SYBEIPV,               EXINFO,      "%1! is an illegal value for the %2! parameter of %3!\0%d %s %s" }
    8004             :         , { SYBEISOI,       EXCONSISTENCY,      "International Release: Invalid sort-order information found\0" }
    8005             :         , { SYBEISRVPREC,   EXCONSISTENCY,      "Illegal precision value returned by the server\0" }
    8006             :         , { SYBEISRVSCL,    EXCONSISTENCY,      "Illegal scale value returned by the server\0" }
    8007             :         , { SYBEITIM,           EXPROGRAM,      "Illegal timeout value specified\0" }
    8008             :         , { SYBEIVERS,          EXPROGRAM,      "Illegal version level specified\0" }
    8009             :         , { SYBEKBCI,              EXINFO,      "1000 rows sent to the server\0" }
    8010             :         , { SYBEKBCO,              EXINFO,      "1000 rows successfully bulk copied to host file\0" }
    8011             :         , { SYBEMEM,           EXRESOURCE,      "Unable to allocate sufficient memory\0" }
    8012             :         , { SYBEMOV,               EXUSER,      "Money arithmetic resulted in overflow in function %1!\0%s" }
    8013             :         , { SYBEMPLL,              EXUSER,      "Attempt to set maximum number of DBPROCESSes lower than 1\0" }
    8014             :         , { SYBEMVOR,           EXPROGRAM,      "Month values must be between 1 and 12\0" }
    8015             :         , { SYBENBUF,              EXINFO,      "Called dbsendpassthru with a NULL buf parameter\0" }
    8016             :         , { SYBENBVP,           EXPROGRAM,      "Cannot pass dbsetnull a NULL bindval pointer\0" }
    8017             :         , { SYBENDC,            EXPROGRAM,      "Cannot have negative component in date in numeric form\0" }
    8018             :         , { SYBENDTP,           EXPROGRAM,      "Called dbdatecrack with NULL datetime parameter\0" }
    8019             :         , { SYBENEG,               EXCOMM,      "Negotiated login attempt failed\0" }
    8020             :         , { SYBENHAN,              EXINFO,      "Called dbrecvpassthru with a NULL handle parameter\0" }
    8021             :         , { SYBENMOB,           EXPROGRAM,      "No such member of order by clause\0" }
    8022             :         , { SYBENOEV,              EXINFO,      "DBPOLL can not be called when registered procedure notifications have been disabled\0" }
    8023             :         , { SYBENPRM,           EXPROGRAM,      "NULL parameter not allowed for this dboption\0" }
    8024             :         , { SYBENSIP,           EXPROGRAM,      "Negative starting index passed to dbstrcpy\0" }
    8025             :         , { SYBENTLL,              EXUSER,      "Name too long for LOGINREC field\0" }
    8026             :         , { SYBENTTN,           EXPROGRAM,      "Attempt to use dbtxtsput to put a new text timestamp into a non-existent data row\0" }
    8027             :         , { SYBENULL,              EXINFO,      "NULL DBPROCESS pointer passed to DB-Library\0" }
    8028             :         , { SYBENULP,           EXPROGRAM,      "Called %1! with parameter %2! NULL\0%s %d" }
    8029             :         , { SYBENXID,          EXNONFATAL,      "The Server did not grant us a distributed-transaction ID\0" }
    8030             :         , { SYBEONCE,           EXPROGRAM,      "Function can be called only once\0" }
    8031             :         , { SYBEOOB,               EXCOMM,      "Error in sending out-of-band data to the server\0" }
    8032             :         , { SYBEOPIN,          EXNONFATAL,      "Could not open interface file\0" }
    8033             :         , { SYBEOPNA,          EXNONFATAL,      "Option is not available with current server\0" }
    8034             :         , { SYBEOREN,              EXINFO,      "International Release: Warning: an out-of-range error-number was encountered in "
    8035             :                                                 "dblib.loc. The maximum permissible error-number is defined as DBERRCOUNT in sybdb.h\0" }
    8036             :         , { SYBEORPF,              EXUSER,      "Attempt to set remote password would overflow "
    8037             :                                                 "the login record's remote password field\0" }
    8038             :         , { SYBEPOLL,              EXINFO,      "There is already an active dbpoll\0" }
    8039             :         , { SYBEPRTF,              EXINFO,      "dbtracestring may only be called from a printfunc\0" }
    8040             :         , { SYBEPWD,               EXUSER,      "Login incorrect\0" }
    8041             :         , { SYBERDCN,        EXCONVERSION,      "Requested data conversion does not exist\0" }
    8042             :         , { SYBERDNR,           EXPROGRAM,      "Attempt to retrieve data from a non-existent row\0" }
    8043             :         , { SYBEREAD,              EXCOMM,      "Read from the server failed\0" }
    8044             :         , { SYBERESP,           EXPROGRAM,      "Response function address passed to dbresponse must be non-NULL\0" }
    8045             :         , { SYBERPCS,              EXINFO,      "Must call dbrpcinit before dbrpcparam or dbrpcsend\0" }
    8046             :         , { SYBERPIL,           EXPROGRAM,      "It is illegal to pass -1 to dbrpcparam for the datalen of parameters which are of "
    8047             :                                                 "type SYBCHAR, SYBVARCHAR, SYBBINARY, or SYBVARBINARY\0" }
    8048             :         , { SYBERPNA,          EXNONFATAL,      "The RPC facility is available only when using a server whose version number is 4.0 "
    8049             :                                                 "or later\0" }
    8050             :         , { SYBERPND,           EXPROGRAM,      "Attempt to initiate a new TDS server operation with results pending\0" }
    8051             :         , { SYBERPNULL,         EXPROGRAM,      "value parameter for dbrpcparam can be NULL, only if the datalen parameter is 0\0" }
    8052             :         , { SYBERPTXTIM,        EXPROGRAM,      "RPC parameters cannot be of type text or image\0" }
    8053             :         , { SYBERPUL,           EXPROGRAM,      "When passing a SYBINTN, SYBDATETIMN, SYBMONEYN, or SYBFLTN parameter via "
    8054             :                                                 "dbrpcparam, it is necessary to specify the parameter's maximum or actual length so "
    8055             :                                                 "that DB-Library can recognize it as a SYINT1, SYBINT2, SYBINT4, SYBMONEY, SYBMONEY4, "
    8056             :                                                 "and so on\0" }
    8057             :         , { SYBERTCC,           EXPROGRAM,      "dbreadtext may not be used to receive the results of a query that contains a "
    8058             :                                                 "COMPUTE clause\0" }
    8059             :         , { SYBERTSC,           EXPROGRAM,      "dbreadtext may be used only to receive the results of a query that contains a "
    8060             :                                                 "single result column\0" }
    8061             :         , { SYBERXID,          EXNONFATAL,      "The Server did not recognize our distributed-transaction ID\0" }
    8062             :         , { SYBESECURE,         EXPROGRAM,      "Secure SQL Server function not supported in this version\0" }
    8063             :         , { SYBESEFA,           EXPROGRAM,      "DBSETNOTIFS cannot be called if connections are present\0" }
    8064             :         , { SYBESEOF,              EXCOMM,      "Unexpected EOF from the server\0" }
    8065             :         , { SYBESFOV,           EXPROGRAM,      "International Release: dbsafestr overflowed its destination buffer\0" }
    8066             :         , { SYBESMSG,            EXSERVER,      "General TDS server error: Check messages from the server\0" }
    8067             :         , { SYBESOCK,              EXCOMM,      "Unable to open socket\0" }
    8068             :         , { SYBESPID,           EXPROGRAM,      "Called dbspid with a NULL dbproc\0" }
    8069             :         , { SYBESYNC,              EXCOMM,      "Read attempted while out of synchronization with TDS server\0" }
    8070             :         , { SYBETEXS,              EXINFO,      "Called dbmoretext with a bad size parameter\0" }
    8071             :         , { SYBETIME,              EXTIME,      "TDS server connection timed out\0" }
    8072             :         , { SYBETMCF,           EXPROGRAM,      "Attempt to install too many custom formats via dbfmtinstall\0" }
    8073             :         , { SYBETMTD,           EXPROGRAM,      "Attempt to send too much TEXT data via the dbmoretext call\0" }
    8074             :         , { SYBETPAR,           EXPROGRAM,      "No SYBTEXT or SYBIMAGE parameters were defined\0" }
    8075             :         , { SYBETPTN,              EXUSER,      "Syntax error: only two periods are permitted in table names\0" }
    8076             :         , { SYBETRAC,              EXINFO,      "Attempted to turn off a trace flag that was not on\0" }
    8077             :         , { SYBETRAN,              EXINFO,      "DBPROCESS is being used for another transaction\0" }
    8078             :         , { SYBETRAS,              EXINFO,      "DB-Library internal error - trace structure not found\0" }
    8079             :         , { SYBETRSN,              EXINFO,      "Bad numbytes parameter passed to dbtracestring\0" }
    8080             :         , { SYBETSIT,              EXINFO,      "Attempt to call dbtsput with an invalid timestamp\0" }
    8081             :         , { SYBETTS,               EXUSER,      "The table which bulk copy is attempting to copy to a host file is shorter than the "
    8082             :                                                 "number of rows which bulk copy was instructed to skip\0" }
    8083             :         , { SYBETYPE,              EXINFO,      "Invalid argument type given to Hyper/DB-Library\0" }
    8084             :         , { SYBEUCPT,              EXUSER,      "Unrecognized custom-format parameter-type encountered in dbstrbuild\0" }
    8085             :         , { SYBEUCRR,       EXCONSISTENCY,      "Internal software error: Unknown connection result reported by dbpasswd\0" }
    8086             :         , { SYBEUDTY,       EXCONSISTENCY,      "Unknown datatype encountered\0" }
    8087             :         , { SYBEUFDS,              EXUSER,      "Unrecognized format encountered in dbstrbuild\0" }
    8088             :         , { SYBEUFDT,       EXCONSISTENCY,      "Unknown fixed-length datatype encountered\0" }
    8089             :         , { SYBEUHST,              EXUSER,      "Unknown host machine name\0" }
    8090             :         , { SYBEUMSG,              EXCOMM,      "Unknown message-id in MSG datastream\0" }
    8091             :         , { SYBEUNAM,             EXFATAL,      "Unable to get current user name from operating system\0" }
    8092             :         , { SYBEUNOP,          EXNONFATAL,      "Unknown option passed to dbsetopt\0" }
    8093             :         , { SYBEUNT,               EXUSER,      "Unknown network type found in interface file\0" }
    8094             :         , { SYBEURCI,          EXRESOURCE,      "International Release: Unable to read copyright information from the DB-Library "
    8095             :                                                 "localization file\0" }
    8096             :         , { SYBEUREI,          EXRESOURCE,      "International Release: Unable to read error information from the DB-Library "
    8097             :                                                 "localization file\0" }
    8098             :         , { SYBEUREM,          EXRESOURCE,      "International Release: Unable to read error mnemonic from the DB-Library "
    8099             :                                                 "localization file\0" }
    8100             :         , { SYBEURES,          EXRESOURCE,      "International Release: Unable to read error string from the DB-Library "
    8101             :                                                 "localization file. 401 Error severities\0" }
    8102             :         , { SYBEURMI,          EXRESOURCE,      "International Release: Unable to read money-format information from the DB-Library "
    8103             :                                                 "localization file\0" }
    8104             :         , { SYBEUSCT,              EXCOMM,      "Unable to set communications timer\0" }
    8105             :         , { SYBEUTDS,              EXCOMM,      "Unrecognized TDS version received from the server\0" }
    8106             :         , { SYBEUVBF,           EXPROGRAM,      "Attempt to read an unknown version of bcp format file\0" }
    8107             :         , { SYBEUVDT,       EXCONSISTENCY,      "Unknown variable-length datatype encountered\0" }
    8108             :         , { SYBEVDPT,              EXUSER,      "For bulk copy, all variable-length data must have either a length-prefix or a "
    8109             :                                                 "terminator specified\0" }
    8110             :         , { SYBEWAID,       EXCONSISTENCY,      "DB-Library internal error: ALTFMT following ALTNAME has wrong id\0" }
    8111             :         , { SYBEWRIT,              EXCOMM,      "Write to the server failed\0" }
    8112             :         , { SYBEXOCI,          EXNONFATAL,      "International Release: A character-set translation overflowed its destination "
    8113             :                                                 "buffer while using bcp to copy data from a host-file to the server\0" }
    8114             :         , { SYBEXTDN,           EXPROGRAM,      "Warning: the xlt_todisp parameter to dbfree_xlate was NULL. The space associated "
    8115             :                                                 "with the xlt_tosrv parameter has been freed\0" }
    8116             :         , { SYBEXTN,            EXPROGRAM,      "The xlt_tosrv and xlt_todisp parameters to dbfree_xlate were NULL\0" }
    8117             :         , { SYBEXTSN,           EXPROGRAM,      "Warning: the xlt_tosrv parameter to dbfree_xlate was NULL. The space associated "
    8118             :                                                 "with the xlt_todisp parameter has been freed\0" }
    8119             :         , { SYBEZTXT,              EXINFO,      "Attempt to send zero length TEXT or IMAGE to dataserver via dbwritetext\0" }
    8120             :         , { SYBECOLSIZE,           EXINFO,      "Invalid column information structure size\0" }
    8121             :         };
    8122             : 
    8123             : /**  \internal
    8124             :  * \ingroup dblib_internal
    8125             :  * \brief Call client-installed error handler
    8126             :  * 
    8127             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
    8128             :  * \param msgno identifies the error message to be passed to the client's handler.
    8129             :  * \param errnum identifies the OS error (errno), if any.  Use 0 if not applicable.  
    8130             :  * \returns the handler's return code, subject to correction and adjustment for vendor style:
    8131             :  *      - INT_CANCEL    The db-lib function that encountered the error will return FAIL.  
    8132             :  *      - INT_TIMEOUT   The db-lib function will cancel the operation and return FAIL.  \a dbproc remains useable.  
    8133             :  *      - INT_CONTINUE  The db-lib function will retry the operation.  
    8134             :  * \remarks 
    8135             :  *      The client-installed handler may also return INT_EXIT.  If Sybase semantics are used, this function notifies
    8136             :  *      the user and calls exit(3).  If Microsoft semantics are used, this function returns INT_CANCEL.  
    8137             :  *
    8138             :  *      If the client-installed handler returns something other than these four INT_* values, or returns timeout-related
    8139             :  *      value for anything but SYBETIME, it's treated here as INT_EXIT (see above).  
    8140             :  *
    8141             :  * Instead of sprinkling error text all over db-lib, we consolidate it here, 
    8142             :  * where it can be translated (one day), and where it can be mapped to the TDS error number.  
    8143             :  * The libraries don't use consistent error numbers or messages, so when libtds has to emit 
    8144             :  * an error message, it can't include the text.  It can pass its error number to a client-library
    8145             :  * function, which will interpret it, add the text, call the application's installed handler
    8146             :  * (if any) and return the handler's return code back to the caller.  
    8147             :  * 
    8148             :  * The call stack may look something like this:
    8149             :  *
    8150             :  * -#   application
    8151             :  * -#           db-lib function (encounters error)
    8152             :  * -#           dbperror
    8153             :  * -#   error handler (installed by application)
    8154             :  *
    8155             :  * The error handling in this case is unambiguous: the caller invokes this function, the client's handler returns its 
    8156             :  * instruction, which the caller receives.  Quite often the caller will get INT_CANCEL, in which case it should put its 
    8157             :  * house in order and return FAIL.  
    8158             :  *
    8159             :  * The call stack may otherwise look something like this:
    8160             :  *                      
    8161             :  * -#   application
    8162             :  * -#           db-lib function
    8163             :  * -#                   libtds function (encounters error)
    8164             :  * -#           _dblib_handle_err_message
    8165             :  * -#           dbperror
    8166             :  * -#   error handler (installed by application)
    8167             :  *
    8168             :  * Because different client libraries specify their handler semantics differently, 
    8169             :  * and because libtds doesn't know which client library is in charge of any given connection, it cannot interpret the 
    8170             :  * raw return code from a db-lib error handler.  For these reasons, 
    8171             :  * libtds calls _dblib_handle_err_message, which translates between libtds and db-lib semantics.  
    8172             :  * \sa dberrhandle(), _dblib_handle_err_message().
    8173             :  */
    8174             : int
    8175         262 : dbperror(DBPROCESS *dbproc, DBINT msgno, long errnum, ...)
    8176             : {
    8177             :         static const char int_exit_text[] = "FreeTDS: db-lib: exiting because client error handler returned %s for msgno %d\n";
    8178             :         static const char int_invalid_text[] = "%s (%d) received from client-installed error handler for nontimeout for error %d."
    8179             :                                                "  Treating as INT_EXIT\n";
    8180             :         static const DBLIB_ERROR_MESSAGE default_message = { 0, EXCONSISTENCY, "unrecognized msgno" };
    8181         262 :         DBLIB_ERROR_MESSAGE constructed_message = { 0, EXCONSISTENCY, NULL };
    8182         262 :         const DBLIB_ERROR_MESSAGE *msg = &default_message;
    8183             :         
    8184         262 :         int i, rc = INT_CANCEL;
    8185         262 :         const char *os_msgtext = strerror((int) errnum);
    8186         262 :         const char *rc_name = "logic error";
    8187             :         char rc_buf[16];
    8188             : 
    8189         262 :         tdsdump_log(TDS_DBG_FUNC, "dbperror(%p, %d, %ld)\n", dbproc, msgno, errnum);  /* dbproc can be NULL */
    8190             : 
    8191             : #ifdef _WIN32
    8192             :         /*
    8193             :          * Unfortunately MinGW uses the "old" msvcrt.dll (Visual C++ 2005 uses
    8194             :          * a newer version) which does not set errno when allocation functions
    8195             :          * cannot allocate memory, so we do it for them.
    8196             :          */
    8197             :         if (msgno == SYBEMEM)
    8198             :                 errnum = ENOMEM;
    8199             : #endif
    8200             : 
    8201         262 :         if (os_msgtext == NULL)
    8202           0 :                 os_msgtext = "no OS error";
    8203             :         
    8204         262 :         assert(_dblib_err_handler != NULL);     /* always installed by dbinit() or dberrhandle() */
    8205             : 
    8206             :         /* look up the error message */
    8207       44874 :         for (i=0; i < TDS_VECTOR_SIZE(dblib_error_messages); i++ ) {
    8208       45136 :                 if (dblib_error_messages[i].msgno == msgno) {
    8209             : 
    8210             :                         /* 
    8211             :                          * See if the message has placeholders.  If so, build a message string on the heap.  
    8212             :                          * The presence of placeholders is indicated by the existence of a "string after the string", 
    8213             :                          * i.e., a format string (for dbstrbuild) after a null "terminator" in the message. 
    8214             :                          * On error -- can't allocate, can't build the string -- give up and call the client handler anyway. 
    8215             :                          */
    8216         262 :                         const char * ptext = dblib_error_messages[i].msgtext;
    8217         262 :                         const char * pformats = ptext + strlen(ptext) + 1;
    8218         262 :                         msg = &dblib_error_messages[i];
    8219         262 :                         assert(*(pformats - 1) == '\0'); 
    8220         262 :                         if(*pformats != '\0') {
    8221             :                                 va_list ap;
    8222          10 :                                 int result_len, len = 2 * (int)strlen(ptext);
    8223          10 :                                 char * buffer = tds_new0(char, len);
    8224             : 
    8225          10 :                                 if (buffer == NULL)
    8226             :                                         break;
    8227          10 :                                 va_start(ap, errnum);
    8228          10 :                                 rc = tds_vstrbuild(buffer, len, &result_len, ptext, TDS_NULLTERM, pformats, TDS_NULLTERM, ap);
    8229          10 :                                 buffer[result_len] = '\0';
    8230          10 :                                 va_end(ap);
    8231          10 :                                 if (TDS_FAILED(rc)) {
    8232           0 :                                         free(buffer);
    8233           0 :                                         break;
    8234             :                                 }
    8235          10 :                                 constructed_message.msgtext = buffer;
    8236          10 :                                 constructed_message.severity = msg->severity;
    8237          10 :                                 msg = &constructed_message;
    8238             :                         }
    8239             :                         break;
    8240             :                 }
    8241             :         }
    8242             : 
    8243         262 :         if (dbproc && dbproc->tds_socket && dbproc->tds_socket->login) {
    8244           0 :                 DSTR server_name_dstr = dbproc->tds_socket->login->server_name;
    8245           0 :                 if (!tds_dstr_isempty(&server_name_dstr)) {
    8246           0 :                         char * buffer = NULL;
    8247           0 :                         if (asprintf(&buffer, "%s (%s)", msg->msgtext,
    8248             :                                      tds_dstr_cstr(&server_name_dstr)) >= 0) {
    8249           0 :                                 free((char*) constructed_message.msgtext);
    8250           0 :                                 constructed_message.msgtext = buffer;
    8251           0 :                                 constructed_message.severity = msg->severity;
    8252           0 :                                 msg = &constructed_message;
    8253             :                         }
    8254             :                 }
    8255             :         }
    8256             : 
    8257         262 :         tdsdump_log(TDS_DBG_FUNC, "dbperror: Calling dblib_err_handler with msgno = %d; msg->msgtext = \"%s\"\n",
    8258             :                 msgno, msg->msgtext);
    8259             : 
    8260             :         /* call the error handler */
    8261         262 :         rc = (*_dblib_err_handler)(dbproc, msg->severity, msgno, (int) errnum, (char*) msg->msgtext, (char*) os_msgtext);
    8262         262 :         switch (rc) {
    8263             :         case INT_EXIT:
    8264             :                 rc_name = "INT_EXIT"; 
    8265             :                 break;
    8266          40 :         case INT_CONTINUE:      
    8267          40 :                 rc_name = "INT_CONTINUE";
    8268          40 :                 break;
    8269         202 :         case INT_CANCEL:
    8270         202 :                 rc_name = "INT_CANCEL";
    8271         202 :                 break;
    8272          20 :         case INT_TIMEOUT:
    8273          20 :                 rc_name = "INT_TIMEOUT";
    8274          20 :                 break;
    8275           0 :         default:
    8276           0 :                 rc_name = "invalid";
    8277           0 :                 break;
    8278             :         }
    8279         262 :         tdsdump_log(TDS_DBG_FUNC, "dbperror: dblib_err_handler for msgno = %d; msg->msgtext = \"%s\" -- returns %d (%s)\n",
    8280             :                 msgno, msg->msgtext, rc, rc_name);
    8281             : 
    8282             :         /* we're done with the dynamic string now. */
    8283         262 :         free((char*) constructed_message.msgtext);
    8284             :         
    8285             :         /* Timeout return codes are errors for non-timeout conditions. */
    8286         262 :         if (msgno != SYBETIME) {
    8287         192 :                 switch (rc) {
    8288           0 :                 case INT_CONTINUE:
    8289           0 :                         tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "INT_CONTINUE", rc, msgno);
    8290             :                         rc = INT_EXIT;
    8291             :                         break;
    8292           0 :                 case INT_TIMEOUT:
    8293           0 :                         tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "INT_TIMEOUT", rc, msgno);
    8294             :                         rc = INT_EXIT;
    8295             :                         break;
    8296             :                 default:
    8297             :                         break;
    8298             :                 }
    8299          70 :         }
    8300             : 
    8301             :         /* 
    8302             :          * Sybase exits on INT_EXIT; Microsoft converts to INT_CANCEL.
    8303             :          * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dblibc/dbc_pdc04c_6v39.asp
    8304             :          */
    8305         262 :         switch (rc) {
    8306          40 :         case INT_CONTINUE:
    8307             :                 /* Microsoft does not define INT_TIMEOUT.  Instead, two consecutive INT_CONTINUEs yield INT_CANCEL. */
    8308          40 :                 if (dbproc && dbproc->msdblib && ++dbproc->ntimeouts >=2) {
    8309           0 :                         dbproc->ntimeouts = 0;
    8310           0 :                         rc = INT_CANCEL;
    8311             :                 }       /* fall through */
    8312             :         case INT_CANCEL:
    8313             :         case INT_TIMEOUT:
    8314             :                 return rc;      /* normal case */
    8315             :                 break;
    8316           0 :         default:
    8317           0 :                 sprintf(rc_buf, "%d", rc);
    8318           0 :                 rc_name = rc_buf;
    8319           0 :                 tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "Invalid return code", rc, msgno);
    8320             :                 /* fall through */
    8321             :         case INT_EXIT:
    8322           0 :                 if (dbproc && dbproc->msdblib) {
    8323             :                         /* Microsoft behavior */
    8324             :                         return INT_CANCEL;
    8325             :                 }
    8326             :                 /* fprintf(stderr, int_exit_text, rc_name, msgno); */
    8327           0 :                 tdsdump_log(TDS_DBG_SEVERE, int_exit_text, rc_name, msgno);
    8328             :                 break;
    8329             :         }
    8330           0 :         exit(EXIT_FAILURE);
    8331             :         return rc; /* not reached */
    8332             : }
    8333             : 

Generated by: LCOV version 1.13