LCOV - code coverage report
Current view: top level - src/tds - data.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 638 740 86.2 %
Date: 2025-02-21 09:36:06 Functions: 36 51 70.6 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 2003-2011 Frediano Ziglio
       3             :  *
       4             :  * This library is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU Library General Public
       6             :  * License as published by the Free Software Foundation; either
       7             :  * version 2 of the License, or (at your option) any later version.
       8             :  *
       9             :  * This library is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             :  * Library General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU Library General Public
      15             :  * License along with this library; if not, write to the
      16             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      17             :  * Boston, MA 02111-1307, USA.
      18             :  */
      19             : 
      20             : /**
      21             :  * \page new_type How to add a new type
      22             :  * \section intro Introduction
      23             :  * Adding a new type in FreeTDS is a quite complicated task involving
      24             :  * different tasks.
      25             :  *
      26             :  * To see an example you can look at
      27             :  * \commit{adb893f1381fd3ea40564c775e30dc8cdc81dcf2}
      28             :  * ("Implement big(date)time types") and parent changes in the source
      29             :  * repository.
      30             :  *
      31             :  * \section tds libTDS changes
      32             :  * <ul>
      33             :  * <li>protocol. First thing to do is add the type to the protocol.
      34             :  *    A type usually have some mnemonic constant and a structure.
      35             :  *    Declare them in \c include/freetds/proto.h file. Note that
      36             :  *    here you should declare the structure the server use not
      37             :  *    the structure to hold the data in libTDS.
      38             :  *    <br>Cfr \commit{a74a06e1f97f3137f6cf1bc7319dd7a2cfb52b1f}.
      39             :  *
      40             :  * <li>base information. Add the type to \c misc/types.csv file
      41             :  *    (I use LibreOffice Calc to do it). This table maintain the
      42             :  *    base information for a type.
      43             :  *    <br>Cfr \commit{680cb3371e042bb372cbc5e6feb4054e50d40c1a}.
      44             :  *
      45             :  * <li>data. There should be some code to handle this type to/from
      46             :  *    the server. This code is implemented in \c include/freetds/data.h
      47             :  *    and \c src/tds/data.c. You can either add a new set of functions
      48             :  *    to handle this new type or add the type handling do another set
      49             :  *    of types depending on how complicated is that type.
      50             :  *    One thing you have to to at this step is determine how you store
      51             :  *    that type in libTDS. This is quite important at upper level
      52             :  *    libraries will have to use these structures or even present
      53             :  *    these data to client code (like DB-Library usually do).
      54             :  *    Due to the way FreeTDS works now you would get a linker error
      55             :  *    in the ODBC part. You can either ignore the error and proceed
      56             :  *    with libTDS, add the code to ODBC or disable temporarily ODBC.
      57             :  *    <br>Cfr \commit{680cb3371e042bb372cbc5e6feb4054e50d40c1a}.
      58             :  *
      59             :  * <li>enable the type from server. In order to receive the new type
      60             :  *    from the server you have to tell the server that we support
      61             :  *    that type. This can be either done changing the protocol (usually
      62             :  *    Microsoft) or enabling some flags (capabilities for Sybase).
      63             :  *    <br>Cfr \commit{a498703ff9e309c656b19dd990f4cad0283a47c7}.
      64             :  *
      65             :  * <li>conversions. Conversions are not hard to write but usually
      66             :  *    require quite a bit of coding. After extending CONV_RESULT
      67             :  *    type in \c include/freetds/convert.h and adding the type to
      68             :  *    the script that generate the conversion tables in
      69             :  *    \c src/tds/tds_willconvert.pl you have to write the big part
      70             :  *    in \c src/tds/covnert.c. You have to implement all kind of
      71             :  *    conversions you declared in the previous file. Reuse the
      72             :  *    functions that are there (for instance there are some
      73             :  *    parser functions). Also if there are similar types it could
      74             :  *    be helpful to convert first your type to a super type then
      75             :  *    use the conversion for that type. For instance for SMALLINT
      76             :  *    type (\c tds_convert_int2) the type is just readed and then
      77             :  *    \c tds_convert_int is called which handle any int (actually
      78             :  *    32 bit integer). Same for data where the \c TDS_DATETIMEALL
      79             :  *    structure is used. Note that conversions to binary (which
      80             :  *    usually are implemented) are done in another function
      81             :  *    (\c tds_convert_to_binary).
      82             :  *    <br>Cfr \commit{9ed52cb78f725607ac109c8c284ca7c4658d87a9}.
      83             :  *
      84             :  * <li>string definition. Add string for your type to
      85             :  *    \c src/tds/token.c in \c tds_prtype.
      86             :  *    <br>Cfr \commit{ac0d3b46db7d98436cd76f906b7d455f7651faae}.
      87             :  *
      88             :  * <li>conversion tests. You probably will have done some mistake
      89             :  *    with conversions but don't mind, there are some tests which
      90             :  *    will help sorting this out.
      91             :  *    \c src/tds/unittests/convert.c
      92             :  *    try any possible combination of conversion to check if
      93             :  *    all conversion are implemented (it does not check the
      94             :  *    conversions themself).
      95             :  *    \c src/tds/unittests/t0007.c test that your conversion
      96             :  *    are working. Just add manually the conversions you want
      97             :  *    to try.
      98             :  *    <br>Cfr \commit{abcc09c9a88acd0e9a45b46dab3ca44309917a02}.
      99             :  *
     100             :  * <li>parameter. Add type/parameter declaration in
     101             :  *    \c tds_get_column_declaration in \c src/tds/query.c.
     102             :  *    Also do any necessary step to initialize the parameter
     103             :  *    to send to server.
     104             :  *    <br>Cfr \commit{54fdd3233e430c045cf5524ac385770738d9e92c},
     105             :  *    \commit{88cfea19d91245372779b8893a2d62b42696cd49}.
     106             :  *
     107             :  * <li>emulated prepared/rpc. If needed handle your type
     108             :  *    in \c tds_put_param_as_string in \c src/tds/query.c.
     109             :  *    <br>Cfr \commit{017b7bf2fee0f09847e64546d27382d2f2b756f4}.
     110             :  *
     111             :  * </ul>
     112             :  *
     113             :  * \section odbc ODBC changes
     114             :  * ODBC is the most complicated library to add a type to.
     115             :  * Usually its data are different from libTDS so you have to add additional
     116             :  * code for conversions which are not required by other libraries.
     117             :  * <ul>
     118             :  * <li>type information. Every type in ODBC have related information.
     119             :  *    These information are set in \c src/odbc/odbc_data.c.
     120             :  *    Depending on the changes you did for data in libTDS you should
     121             :  *    handle the new type.
     122             :  *    <br>Cfr \commit{71e189e206dc9b6f6513e0aa0e4133a4f8dec110}.
     123             :  *
     124             :  * <li>type information test. Related to the previous change there
     125             :  *    is \c src/odbc/unittests/describecol.c test. Add a test case
     126             :  *    for new type. You should attempt to run same test also on
     127             :  *    proprietary library if possible.
     128             :  *    <br>Cfr \commit{8a8ec16a6a514a5d6ac66c2470eff51f6a8d4a53}.
     129             :  *
     130             :  * <li>conversions from odbc. Define how the ODBC type should convert
     131             :  *    to the server and implement the conversion.
     132             :  *    <br>Cfr \commit{29606cbf413c44e49ddfcfb8a93b8a6bd2565a84},
     133             :  *    \commit{87c84e20a594472a72990b12d4a1451b22e6714b}.
     134             :  *
     135             :  * <li>conversions to binary. Binary representation in ODBC are usually
     136             :  *    different from server ones. If so implement the proper conversions.
     137             :  *    <br>Cfr \commit{56009f35d3e0def339a0c5cb98d006e5e710d523}.
     138             :  *
     139             :  * <li>conversions to characters. Same problem for character types.
     140             :  *    <br>Cfr \commit{25ff091880dabc32f28a73f09bf31c01314aca2f}.
     141             :  *
     142             :  * <li>conversion test. You probably want to test ODBC conversions.
     143             :  *    This can be done changing \c src/odbc/unittests/data.c test and
     144             :  *    \c src/odbc/unittests/genparams.c.
     145             :  *    <br>Cfr \commit{e69f7d564dac44884f7c5f0106cceafce4af168b}.
     146             :  * </ul>
     147             :  *
     148             :  * \section ctlib CT-Library changes
     149             :  * This is quite easy as usual the conversion in libTDS are fine for
     150             :  * this library.
     151             :  * <ul>
     152             :  * <li>define type in \c include/cspublic.h
     153             :  * <li>implement conversion in \c src/ctlib/cs.h
     154             :  * <li>set corrent conversion from cs types to server in
     155             :  *    \c src/ctlib/ct.c
     156             :  * </ul>
     157             :  * Cfr \commit{c5e71e5ad4a557038ecedcec457e2531ab02a77b}.
     158             :  *
     159             :  * \section dblib DB-Library changes
     160             :  * A bit more complicated than CT-Library but not that much.
     161             :  * <ul>
     162             :  * <li>add type and binding type to \c include/sybdb.h
     163             :  * <li>add NULL handling in \c dbgetnull, \c dbsetnull
     164             :  *    and \c default_null_representation in
     165             :  *    \c src/dblib/dblib.c
     166             :  * <li>add binding to dbbindtype
     167             :  * <li>add support for conversion from/to server
     168             :  * <li>add printable size
     169             :  * <li>return correct type string
     170             :  * </ul>
     171             :  * Cfr \commit{99dd126e0eb248dd3079b2a7cf97437fe3bcd163}.
     172             :  *
     173             :  * \section apps Applications changes
     174             :  * datacopy application requires some changes too to support new types
     175             :  * so add them to \c src/apps/datacopy.c.
     176             :  * <br>Cfr \commit{e59c48ac39c76abb036651f8ec238090eef321c9}.
     177             :  */
     178             : 
     179             : /**
     180             :  * @file
     181             :  * @brief Handle different data handling from network
     182             :  */
     183             : 
     184             : #include <config.h>
     185             : 
     186             : #include <stdarg.h>
     187             : #include <stdio.h>
     188             : #include <assert.h>
     189             : 
     190             : #if HAVE_STRING_H
     191             : #include <string.h>
     192             : #endif /* HAVE_STRING_H */
     193             : 
     194             : #if HAVE_STDLIB_H
     195             : #include <stdlib.h>
     196             : #endif /* HAVE_STDLIB_H */
     197             : 
     198             : #define TDS_DONT_DEFINE_DEFAULT_FUNCTIONS
     199             : #include <freetds/utils.h>
     200             : #include <freetds/tds.h>
     201             : #include <freetds/bytes.h>
     202             : #include <freetds/iconv.h>
     203             : #include <freetds/checks.h>
     204             : #include <freetds/stream.h>
     205             : #include <freetds/data.h>
     206             : 
     207             : #define USE_ICONV (tds->conn->use_iconv)
     208             : 
     209             : static const TDSCOLUMNFUNCS *tds_get_column_funcs(TDSCONNECTION *conn, int type);
     210             : static void tds_swap_numeric(TDS_NUMERIC *num);
     211             : 
     212             : #undef MIN
     213             : #define MIN(a,b) (((a) < (b)) ? (a) : (b))
     214             : #undef MAX
     215             : #define MAX(a,b) (((a) > (b)) ? (a) : (b))
     216             : 
     217             : /**
     218             :  * Set type of column initializing all dependency.
     219             :  * column_usertype should already be set.
     220             :  * @param curcol column to set
     221             :  * @param type   type to set
     222             :  */
     223             : void
     224       94627 : tds_set_column_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
     225             : {
     226             :         /* set type */
     227       94627 :         curcol->on_server.column_type = type;
     228       94627 :         curcol->funcs = tds_get_column_funcs(conn, type);
     229       94627 :         curcol->column_type = tds_get_cardinal_type(type, curcol->column_usertype);
     230             : 
     231             :         /* set size */
     232       94627 :         curcol->column_cur_size = -1;
     233       94627 :         curcol->column_varint_size = tds_get_varint_size(conn, type);
     234       94627 :         if (curcol->column_varint_size == 0)
     235       25792 :                 curcol->column_cur_size = curcol->on_server.column_size = curcol->column_size = tds_get_size_by_type(type);
     236             : 
     237       94627 : }
     238             : 
     239             : /**
     240             :  * Set type of column initializing all dependency
     241             :  * \param tds    state information for the socket and the TDS protocol
     242             :  * \param curcol column to set
     243             :  * \param type   type to set
     244             :  */
     245             : void
     246       21664 : tds_set_param_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
     247             : {
     248       21664 :         if (IS_TDS7_PLUS(conn)) {
     249       18786 :                 switch (type) {
     250        2644 :                 case SYBVARCHAR:
     251        2644 :                         type = XSYBVARCHAR;
     252        2644 :                         break;
     253        1090 :                 case SYBCHAR:
     254        1090 :                         type = XSYBCHAR;
     255        1090 :                         break;
     256         208 :                 case SYBVARBINARY:
     257         208 :                         type = XSYBVARBINARY;
     258         208 :                         break;
     259        1042 :                 case SYBBINARY:
     260        1042 :                         type = XSYBBINARY;
     261        1042 :                         break;
     262         752 :                 case SYBBIT:
     263         752 :                         type = SYBBITN;
     264         752 :                         break;
     265             :                         /* avoid warning on other types */
     266             :                 default:
     267             :                         break;
     268             :                 }
     269        2878 :         } else if (IS_TDS50(conn)) {
     270        2878 :                 switch (type) {
     271          14 :                 case SYBINT8:
     272          14 :                         type = SYB5INT8;
     273          14 :                         break;
     274             :                         /* avoid warning on other types */
     275             :                 default:
     276             :                         break;
     277             :                 }
     278           0 :         }
     279       21664 :         tds_set_column_type(conn, curcol, type);
     280             : 
     281       21664 :         if (is_collate_type(type) || is_char_type(type)) {
     282        7662 :                 curcol->char_conv = conn->char_convs[is_unicode_type(type) ? client2ucs2 : client2server_chardata];
     283        7662 :                 memcpy(curcol->column_collation, conn->collation, sizeof(conn->collation));
     284             :         }
     285             : 
     286             :         /* special case, GUID, varint != 0 but only a size */
     287             :         /* TODO VARIANT, when supported */
     288       21664 :         switch (type) {
     289          44 :         case SYBUNIQUE:
     290          44 :                 curcol->on_server.column_size = curcol->column_size = sizeof(TDS_UNIQUE);
     291          44 :                 break;
     292         756 :         case SYBBITN:
     293         756 :                 curcol->on_server.column_size = curcol->column_size = sizeof(TDS_TINYINT);
     294         756 :                 break;
     295             :         /* mssql 2005 don't like SYBINT4 as parameter closing connection  */
     296        4840 :         case SYBINT1:
     297             :         case SYBINT2:
     298             :         case SYBINT4:
     299             :         case SYBINT8:
     300        4840 :                 curcol->on_server.column_type = SYBINTN;
     301        4840 :                 curcol->column_varint_size = 1;
     302        4840 :                 curcol->column_cur_size = -1;
     303        4840 :                 break;
     304          60 :         case SYBMONEY4:
     305             :         case SYBMONEY:
     306          60 :                 curcol->on_server.column_type = SYBMONEYN;
     307          60 :                 curcol->column_varint_size = 1;
     308          60 :                 curcol->column_cur_size = -1;
     309          60 :                 break;
     310         810 :         case SYBDATETIME:
     311             :         case SYBDATETIME4:
     312         810 :                 curcol->on_server.column_type = SYBDATETIMN;
     313         810 :                 curcol->column_varint_size = 1;
     314         810 :                 curcol->column_cur_size = -1;
     315         810 :                 break;
     316        2324 :         case SYBFLT8:
     317             :         case SYBREAL:
     318        2324 :                 curcol->on_server.column_type = SYBFLTN;
     319        2324 :                 curcol->column_varint_size = 1;
     320        2324 :                 curcol->column_cur_size = -1;
     321        2324 :                 break;
     322         464 :         case SYBNTEXT:
     323         464 :                 if (IS_TDS72_PLUS(conn)) {
     324         232 :                         curcol->column_varint_size = 8;
     325         232 :                         curcol->on_server.column_type = XSYBNVARCHAR;
     326             :                 }
     327             :                 break;
     328         672 :         case SYBTEXT:
     329         672 :                 if (IS_TDS72_PLUS(conn)) {
     330         304 :                         curcol->column_varint_size = 8;
     331         304 :                         curcol->on_server.column_type = XSYBVARCHAR;
     332             :                 }
     333             :                 break;
     334         186 :         case SYBIMAGE:
     335         186 :                 if (IS_TDS72_PLUS(conn)) {
     336          88 :                         curcol->column_varint_size = 8;
     337          88 :                         curcol->on_server.column_type = XSYBVARBINARY;
     338             :                 }
     339             :                 break;
     340           0 :         case SYB5BIGTIME:
     341             :         case SYB5BIGDATETIME:
     342           0 :                 curcol->column_prec = 6;
     343           0 :                 curcol->column_scale = 6;
     344           0 :                 break;
     345             :         default:
     346             :                 break;
     347             :         }
     348       21664 : }
     349             : 
     350             : TDS_SERVER_TYPE
     351    13127708 : tds_get_cardinal_type(TDS_SERVER_TYPE datatype, int usertype)
     352             : {
     353    13127708 :         switch (datatype) {
     354             :         case XSYBVARBINARY:
     355             :                 return SYBVARBINARY;
     356        7248 :         case XSYBBINARY:
     357        7248 :                 return SYBBINARY;
     358       29622 :         case SYBNTEXT:
     359       29622 :                 return SYBTEXT;
     360     3016942 :         case XSYBNVARCHAR:
     361             :         case XSYBVARCHAR:
     362     3016942 :                 return SYBVARCHAR;
     363     1459275 :         case XSYBNCHAR:
     364             :         case XSYBCHAR:
     365     1459275 :                 return SYBCHAR;
     366         968 :         case SYB5INT8:
     367         968 :                 return SYBINT8;
     368       19210 :         case SYBLONGBINARY:
     369       19210 :                 switch (usertype) {
     370             :                 case USER_UNICHAR_TYPE:
     371             :                 case USER_UNIVARCHAR_TYPE:
     372             :                         return SYBTEXT;
     373             :                 }
     374             :                 break;
     375         412 :         case SYBMSXML:
     376         412 :                 return SYBLONGCHAR;
     377             :         default:
     378             :                 break;
     379             :         }
     380     8580631 :         return datatype;
     381             : }
     382             : 
     383             : TDSRET
     384       67233 : tds_generic_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
     385             : {
     386       67233 :         switch (col->column_varint_size) {
     387          16 :         case 8:
     388          16 :                 col->column_size = 0x7ffffffflu;
     389          16 :                 break;
     390        3346 :         case 5:
     391             :         case 4:
     392        3346 :                 col->column_size = tds_get_int(tds);
     393        3346 :                 if (col->column_size < 0)
     394             :                         return TDS_FAIL;
     395             :                 break;
     396       20831 :         case 2:
     397             :                 /* assure > 0 */
     398       20831 :                 col->column_size = tds_get_smallint(tds);
     399             :                 /* under TDS7.2 this means ?var???(MAX) */
     400       20831 :                 if (col->column_size < 0 && IS_TDS72_PLUS(tds->conn)) {
     401         204 :                         if (is_char_type(col->column_type))
     402         164 :                                 col->column_size = 0x3ffffffflu;
     403             :                         else
     404          40 :                                 col->column_size = 0x7ffffffflu;
     405             : 
     406         204 :                         col->column_varint_size = 8;
     407             :                 }
     408       20831 :                 if (col->column_size < 0)
     409             :                         return TDS_FAIL;
     410             :                 break;
     411       26570 :         case 1:
     412       26570 :                 col->column_size = tds_get_byte(tds);
     413       26570 :                 break;
     414       16470 :         case 0:
     415       16470 :                 col->column_size = tds_get_size_by_type(col->column_type);
     416       16470 :                 break;
     417             :         }
     418             : 
     419       67233 :         if (IS_TDS71_PLUS(tds->conn) && is_collate_type(col->on_server.column_type)) {
     420             :                 /* based on true type as sent by server */
     421             :                 /*
     422             :                  * first 2 bytes are windows code (such as 0x409 for english)
     423             :                  * other 2 bytes ???
     424             :                  * last bytes is id in syscharsets
     425             :                  */
     426       21813 :                 tds_get_n(tds, col->column_collation, 5);
     427       21813 :                 col->char_conv =
     428       21813 :                         tds_iconv_from_collate(tds->conn, col->column_collation);
     429             :         }
     430             : 
     431             :         /* Only read table_name for blob columns (eg. not for SYBLONGBINARY) */
     432       67233 :         if (is_blob_type(col->on_server.column_type)) {
     433             :                 /* discard this additional byte */
     434        2408 :                 if (IS_TDS72_PLUS(tds->conn)) {
     435         972 :                         unsigned char num_parts = tds_get_byte(tds);
     436             :                         /* TODO do not discard first ones */
     437        1944 :                         for (; num_parts; --num_parts) {
     438         972 :                                 tds_dstr_get(tds, &col->table_name, tds_get_usmallint(tds));
     439             :                         }
     440             :                 } else {
     441        1436 :                         tds_dstr_get(tds, &col->table_name, tds_get_usmallint(tds));
     442             :                 }
     443       64825 :         } else if (IS_TDS72_PLUS(tds->conn) && col->on_server.column_type == SYBMSXML) {
     444          16 :                 unsigned char has_schema = tds_get_byte(tds);
     445          16 :                 if (has_schema) {
     446             :                         /* discard schema information */
     447           4 :                         tds_get_string(tds, tds_get_byte(tds), NULL, 0);        /* dbname */
     448           4 :                         tds_get_string(tds, tds_get_byte(tds), NULL, 0);        /* schema owner */
     449           4 :                         tds_get_string(tds, tds_get_usmallint(tds), NULL, 0);    /* schema collection */
     450             :                 }
     451             :         }
     452             :         return TDS_SUCCESS;
     453             : }
     454             : 
     455             : /* tds_generic_row_len support also variant and return size to hold blob */
     456             : TDS_COMPILE_CHECK(variant_size, sizeof(TDSBLOB) >= sizeof(TDSVARIANT));
     457             : 
     458             : TDS_INT
     459      145080 : tds_generic_row_len(TDSCOLUMN *col)
     460             : {
     461      145080 :         CHECK_COLUMN_EXTRA(col);
     462             : 
     463      145080 :         if (is_blob_col(col))
     464             :                 return sizeof(TDSBLOB);
     465      135770 :         return col->column_size;
     466             : }
     467             : 
     468             : static TDSRET
     469        3366 : tds_get_char_dynamic(TDSSOCKET *tds, TDSCOLUMN *curcol, void **pp, size_t allocated, TDSINSTREAM *r_stream)
     470             : {
     471             :         TDSRET res;
     472             :         TDSDYNAMICSTREAM w;
     473             : 
     474             :         /*
     475             :          * Blobs don't use a column's fixed buffer because the official maximum size is 2 GB.
     476             :          * Instead, they're reallocated as necessary, based on the data's size.
     477             :          */
     478        3366 :         TDS_PROPAGATE(tds_dynamic_stream_init(&w, pp, allocated));
     479             : 
     480        3366 :         if (USE_ICONV && curcol->char_conv)
     481         960 :                 res = tds_convert_stream(tds, curcol->char_conv, to_client, r_stream, &w.stream);
     482             :         else
     483        2406 :                 res = tds_copy_stream(r_stream, &w.stream);
     484        3366 :         curcol->column_cur_size = w.size;
     485             :         return res;
     486             : }
     487             : 
     488             : typedef struct tds_varmax_stream {
     489             :         TDSINSTREAM stream;
     490             :         TDSSOCKET *tds;
     491             :         TDS_INT chunk_left;
     492             : } TDSVARMAXSTREAM;
     493             : 
     494             : static int
     495         348 : tds_varmax_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
     496             : {
     497         348 :         TDSVARMAXSTREAM *s = (TDSVARMAXSTREAM *) stream;
     498             : 
     499             :         /* read chunk len if needed */
     500         348 :         if (s->chunk_left == 0) {
     501         348 :                 TDS_INT l = tds_get_int(s->tds);
     502         348 :                 if (l <= 0) l = -1;
     503         348 :                 s->chunk_left = l;
     504             :         }
     505             : 
     506             :         /* no more data ?? */
     507         348 :         if (s->chunk_left < 0)
     508             :                 return 0;
     509             : 
     510             :         /* read part of data */
     511         160 :         if (len > s->chunk_left)
     512         160 :                 len = s->chunk_left;
     513         160 :         s->chunk_left -= len;
     514         160 :         if (tds_get_n(s->tds, ptr, len))
     515         160 :                 return len;
     516             :         return -1;
     517             : }
     518             : 
     519             : static TDSRET
     520         188 : tds72_get_varmax(TDSSOCKET * tds, TDSCOLUMN * curcol)
     521             : {
     522             :         TDS_INT8 len;
     523             :         TDSVARMAXSTREAM r;
     524         188 :         size_t allocated = 0;
     525         188 :         void **pp = (void**) &(((TDSBLOB*) curcol->column_data)->textvalue);
     526             : 
     527         188 :         len = tds_get_int8(tds);
     528             : 
     529             :         /* NULL */
     530         188 :         if (len == -1) {
     531           0 :                 curcol->column_cur_size = -1;
     532           0 :                 return TDS_SUCCESS;
     533             :         }
     534             : 
     535             :         /* try to allocate an initial buffer */
     536             :         if (len > (TDS_INT8) (~((size_t) 0) >> 1))
     537             :                 return TDS_FAIL;
     538         188 :         if (len > 0) {
     539         144 :                 TDS_ZERO_FREE(*pp);
     540         144 :                 allocated = (size_t) len;
     541         144 :                 if (is_unicode_type(curcol->on_server.column_type))
     542          44 :                         allocated /= 2;
     543             :         }
     544             : 
     545         188 :         r.stream.read = tds_varmax_stream_read;
     546         188 :         r.tds = tds;
     547         188 :         r.chunk_left = 0;
     548             : 
     549         188 :         return tds_get_char_dynamic(tds, curcol, pp, allocated, &r.stream);
     550             : }
     551             : 
     552             : TDS_COMPILE_CHECK(tds_variant_size,  sizeof(((TDSVARIANT*)0)->data) == sizeof(((TDSBLOB*)0)->textvalue));
     553             : TDS_COMPILE_CHECK(tds_variant_offset,TDS_OFFSET(TDSVARIANT, data) == TDS_OFFSET(TDSBLOB, textvalue));
     554             : 
     555             : /*
     556             :  * This strange type has following structure 
     557             :  * 0 len (int32) -- NULL 
     558             :  * len (int32), type (int8), data -- ints, date, etc
     559             :  * len (int32), type (int8), 7 (int8), collation, column size (int16) -- [n]char, [n]varchar, binary, varbinary 
     560             :  * BLOBS (text/image) not supported
     561             :  */
     562             : TDSRET
     563         144 : tds_variant_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
     564             : {
     565         144 :         unsigned int colsize = tds_get_uint(tds);
     566             :         int varint;
     567             :         TDS_SERVER_TYPE type;
     568             :         TDS_UCHAR info_len;
     569             :         TDSVARIANT *v;
     570             :         TDSRET rc;
     571             : 
     572             :         /* NULL */
     573         144 :         curcol->column_cur_size = -1;
     574         144 :         if (colsize < 2) {
     575           4 :                 tds_get_n(tds, NULL, colsize);
     576           4 :                 return TDS_SUCCESS;
     577             :         }
     578             : 
     579         140 :         type = (TDS_SERVER_TYPE) tds_get_byte(tds);
     580         140 :         info_len = tds_get_byte(tds);
     581         140 :         if (!is_variant_inner_type(type))
     582             :                 goto error_type;
     583         140 :         v = (TDSVARIANT*) curcol->column_data;
     584         140 :         v->type = type;
     585         140 :         colsize -= 2;
     586         140 :         if (info_len > colsize)
     587             :                 goto error_type;
     588         140 :         if (is_collate_type(type)) {
     589          56 :                 if (sizeof(v->collation) > info_len)
     590             :                         goto error_type;
     591          56 :                 tds_get_n(tds, v->collation, sizeof(v->collation));
     592          56 :                 colsize -= sizeof(v->collation);
     593          56 :                 info_len -= sizeof(v->collation);
     594          56 :                 curcol->char_conv = is_unicode_type(type) ? 
     595          56 :                         tds->conn->char_convs[client2ucs2] : tds_iconv_from_collate(tds->conn, v->collation);
     596             :         }
     597             : 
     598             :         /* special case for numeric */
     599         140 :         if (is_numeric_type(type)) {
     600             :                 TDS_NUMERIC *num;
     601          16 :                 if (info_len != 2)
     602             :                         goto error_type;
     603          16 :                 if (v->data)
     604           8 :                         TDS_ZERO_FREE(v->data);
     605          16 :                 v->data_len = sizeof(TDS_NUMERIC);
     606          16 :                 num = tds_new0(TDS_NUMERIC, 1);
     607          16 :                 if (!num)
     608             :                         goto error_memory;
     609          16 :                 v->data = (TDS_CHAR *) num;
     610          16 :                 num->precision = tds_get_byte(tds);
     611          16 :                 num->scale     = tds_get_byte(tds);
     612          16 :                 colsize -= 2;
     613             :                 /* check prec/scale, don't let server crash us */
     614          16 :                 if (num->precision < 1 || num->precision > MAXPRECISION
     615          16 :                     || num->scale > num->precision)
     616             :                         goto error_type;
     617          16 :                 if (colsize > sizeof(num->array))
     618             :                         goto error_type;
     619          16 :                 curcol->column_cur_size = colsize;
     620          16 :                 tds_get_n(tds, num->array, colsize);
     621          16 :                 if (IS_TDS7_PLUS(tds->conn))
     622             :                         tds_swap_numeric(num);
     623             :                 return TDS_SUCCESS;
     624             :         }
     625             : 
     626             :         /* special case for MS date/time */
     627         124 :         switch (type) {
     628          16 :         case SYBMSTIME:
     629             :         case SYBMSDATETIME2:
     630             :         case SYBMSDATETIMEOFFSET:
     631          16 :                 if (info_len != 1)
     632             :                         goto error_type;
     633          16 :                 curcol->column_scale = curcol->column_prec = tds_get_byte(tds);
     634          16 :                 if (curcol->column_prec > 7)
     635             :                         goto error_type;
     636          16 :                 colsize -= info_len;
     637          16 :                 info_len = 0;
     638             :                 /* fall through */
     639           4 :         case SYBMSDATE:
     640           4 :                 if (info_len != 0)
     641             :                         goto error_type;
     642             :                 /* dirty trick */
     643          20 :                 tds->in_buf[--tds->in_pos] = colsize;
     644          20 :                 if (v->data)
     645           0 :                         TDS_ZERO_FREE(v->data);
     646          20 :                 v->data_len = sizeof(TDS_DATETIMEALL);
     647          20 :                 v->data = tds_new0(TDS_CHAR, sizeof(TDS_DATETIMEALL));
     648          20 :                 curcol->column_type = type;
     649          20 :                 curcol->column_data = (unsigned char *) v->data;
     650             :                 /* trick, call get function */
     651          20 :                 rc = tds_msdatetime_get(tds, curcol);
     652          20 :                 curcol->column_type = SYBVARIANT;
     653          20 :                 curcol->column_data = (unsigned char *) v;
     654          20 :                 return rc;
     655             :         default:
     656             :                 break;
     657             :         }
     658         104 :         varint = (type == SYBUNIQUE) ? 0 : tds_get_varint_size(tds->conn, type);
     659         208 :         if (varint != info_len || varint > 2)
     660             :                 goto error_type;
     661         104 :         switch (varint) {
     662          40 :         case 0:
     663          40 :                 v->size = tds_get_size_by_type(type);
     664          40 :                 break;
     665           0 :         case 1:
     666           0 :                 v->size = tds_get_byte(tds);
     667           0 :                 break;
     668          64 :         case 2:
     669          64 :                 v->size = tds_get_smallint(tds);
     670          64 :                 break;
     671             :         default:
     672             :                 goto error_type;
     673             :         }
     674         104 :         colsize -= info_len;
     675         104 :         curcol->column_cur_size = colsize;
     676         104 :         if (v->data)
     677           8 :                 TDS_ZERO_FREE(v->data);
     678         104 :         if (colsize) {
     679             :                 TDSDATAINSTREAM r;
     680             : 
     681         104 :                 if (USE_ICONV && curcol->char_conv)
     682          16 :                         v->type = tds_get_cardinal_type(type, 0);
     683             : 
     684         104 :                 tds_datain_stream_init(&r, tds, colsize);
     685         104 :                 TDS_PROPAGATE(tds_get_char_dynamic(tds, curcol, (void **) &v->data, colsize, &r.stream));
     686         104 :                 colsize = curcol->column_cur_size;
     687             : #ifdef WORDS_BIGENDIAN
     688             :                 tds_swap_datatype(tds_get_conversion_type(type, colsize), v->data);
     689             : #endif
     690             :         }
     691         104 :         v->data_len = colsize;
     692         104 :         CHECK_COLUMN_EXTRA(curcol);
     693         104 :         return TDS_SUCCESS;
     694             : 
     695           0 : error_type:
     696           0 : error_memory:
     697           0 :         tds_get_n(tds, NULL, colsize);
     698           0 :         return TDS_FAIL;
     699             : }
     700             : 
     701             : /**
     702             :  * Read a data from wire
     703             :  * \param tds state information for the socket and the TDS protocol
     704             :  * \param curcol column where store column information
     705             :  * \return TDS_FAIL on error or TDS_SUCCESS
     706             :  */
     707             : TDSRET
     708     1192941 : tds_generic_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
     709             : {
     710             :         unsigned char *dest;
     711             :         int len, colsize;
     712             :         int fillchar;
     713     1192941 :         TDSBLOB *blob = NULL;
     714             : 
     715     1192941 :         CHECK_TDS_EXTRA(tds);
     716     1192941 :         CHECK_COLUMN_EXTRA(curcol);
     717             : 
     718     1192941 :         tdsdump_log(TDS_DBG_INFO1, "tds_get_data: type %d, varint size %d\n", curcol->column_type, curcol->column_varint_size);
     719     1192941 :         switch (curcol->column_varint_size) {
     720        2680 :         case 4:
     721             :                 /* It's a BLOB... */
     722        2680 :                 len = tds_get_byte(tds);
     723        2680 :                 blob = (TDSBLOB *) curcol->column_data;
     724        2680 :                 if (len == 16) {        /*  Jeff's hack */
     725        2442 :                         tds_get_n(tds, blob->textptr, 16);
     726        2442 :                         tds_get_n(tds, blob->timestamp, 8);
     727        2442 :                         blob->valid_ptr = true;
     728        3438 :                         if (IS_TDS72_PLUS(tds->conn) &&
     729         996 :                             memcmp(blob->textptr, "dummy textptr\0\0",16) == 0)
     730         996 :                                 blob->valid_ptr = false;
     731        2442 :                         colsize = tds_get_int(tds);
     732             :                 } else {
     733             :                         colsize = -1;
     734             :                 }
     735             :                 break;
     736         788 :         case 5:
     737         788 :                 colsize = tds_get_int(tds);
     738         788 :                 if (colsize == 0)
     739           0 :                         colsize = -1;
     740             :                 break;
     741         188 :         case 8:
     742         188 :                 return tds72_get_varmax(tds, curcol);
     743      435548 :         case 2:
     744      435548 :                 colsize = tds_get_smallint(tds);
     745      435548 :                 break;
     746      424799 :         case 1:
     747      424799 :                 colsize = tds_get_byte(tds);
     748      424799 :                 if (colsize == 0)
     749        7770 :                         colsize = -1;
     750             :                 break;
     751      328938 :         case 0:
     752             :                 /* TODO this should be column_size */
     753      328938 :                 colsize = tds_get_size_by_type(curcol->column_type);
     754             :                 break;
     755             :         default:
     756             :                 colsize = -1;
     757             :                 break;
     758             :         }
     759     1192753 :         if (IS_TDSDEAD(tds))
     760             :                 return TDS_FAIL;
     761             : 
     762     1192753 :         tdsdump_log(TDS_DBG_INFO1, "tds_get_data(): wire column size is %d \n", colsize);
     763             :         /* set NULL flag in the row buffer */
     764     1192753 :         if (colsize < 0) {
     765       10924 :                 curcol->column_cur_size = -1;
     766       10924 :                 return TDS_SUCCESS;
     767             :         }
     768             : 
     769             :         /* 
     770             :          * We're now set to read the data from the wire.  For varying types (e.g. char/varchar)
     771             :          * make sure that curcol->column_cur_size reflects the size of the read data, 
     772             :          * after any charset conversion.  tds_get_char_data() does that for you, 
     773             :          * but of course tds_get_n() doesn't.  
     774             :          *
     775             :          * colsize == wire_size, bytes to read
     776             :          * curcol->column_cur_size == sizeof destination buffer, room to write
     777             :          */
     778     1181829 :         dest = curcol->column_data;
     779     1181829 :         if (is_blob_col(curcol)) {
     780             :                 TDSDATAINSTREAM r;
     781             :                 size_t allocated;
     782             :                 TDSRET ret;
     783             : 
     784        3230 :                 blob = (TDSBLOB *) dest;        /* cf. column_varint_size case 4, above */
     785             : 
     786             :                 /* empty string */
     787        3230 :                 if (colsize == 0) {
     788         156 :                         curcol->column_cur_size = 0;
     789         156 :                         if (blob->textvalue)
     790           0 :                                 TDS_ZERO_FREE(blob->textvalue);
     791             :                         return TDS_SUCCESS;
     792             :                 }
     793             : 
     794        3074 :                 allocated = MAX(curcol->column_cur_size, 0);
     795        3074 :                 if (colsize > allocated) {
     796        2738 :                         TDS_ZERO_FREE(blob->textvalue);
     797        2738 :                         allocated = colsize;
     798        2738 :                         if (is_unicode_type(curcol->on_server.column_type))
     799         700 :                                 allocated /= 2;
     800             :                 }
     801             : 
     802        3074 :                 tds_datain_stream_init(&r, tds, colsize);
     803        3074 :                 ret = tds_get_char_dynamic(tds, curcol, (void **) &blob->textvalue, allocated, &r.stream);
     804        3074 :                 if (TDS_FAILED(ret) && TDS_UNLIKELY(r.wire_size > 0)) {
     805           0 :                         tds_get_n(tds, NULL, r.wire_size);
     806           0 :                         return ret;
     807             :                 }
     808             :                 return TDS_SUCCESS;
     809             :         }
     810             : 
     811             :         /* non-numeric and non-blob */
     812             : 
     813     1178599 :         if (USE_ICONV && curcol->char_conv) {
     814      483049 :                 TDS_PROPAGATE(tds_get_char_data(tds, (char *) dest, colsize, curcol));
     815             :         } else {
     816             :                 /*
     817             :                  * special case, some servers seem to return more data in some conditions
     818             :                  * (ASA 7 returning 4 byte nullable integer)
     819             :                  */
     820      695550 :                 int discard_len = 0;
     821      695550 :                 if (colsize > curcol->column_size) {
     822           0 :                         discard_len = colsize - curcol->column_size;
     823           0 :                         colsize = curcol->column_size;
     824             :                 }
     825      695550 :                 if (!tds_get_n(tds, dest, colsize))
     826             :                         return TDS_FAIL;
     827      695550 :                 if (discard_len > 0)
     828           0 :                         tds_get_n(tds, NULL, discard_len);
     829      695550 :                 curcol->column_cur_size = colsize;
     830             :         }
     831             : 
     832             :         /* pad (UNI)CHAR and BINARY types */
     833     1178599 :         fillchar = 0;
     834     1178599 :         switch (curcol->column_type) {
     835             :         /* extra handling for SYBLONGBINARY */
     836           0 :         case SYBLONGBINARY:
     837           0 :                 if (curcol->column_usertype != USER_UNICHAR_TYPE)
     838             :                         break;
     839             :         case SYBCHAR:
     840             :         case XSYBCHAR:
     841      208810 :                 if (curcol->column_size != curcol->on_server.column_size)
     842             :                         break;
     843             :                 /* FIXME use client charset */
     844             :                 fillchar = ' ';
     845      208085 :         case SYBBINARY:
     846             :         case XSYBBINARY:
     847      208085 :                 if (colsize < curcol->column_size)
     848          90 :                         memset(dest + colsize, fillchar, curcol->column_size - colsize);
     849      208085 :                 colsize = curcol->column_size;
     850      208085 :                 break;
     851             :         default:
     852             :                 break;
     853             :         }
     854             : 
     855             : #ifdef WORDS_BIGENDIAN
     856             :         tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n", tds_get_conversion_type(curcol->column_type, colsize));
     857             :         tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), dest);
     858             : #endif
     859         847 :         return TDS_SUCCESS;
     860             : }
     861             : 
     862             : /**
     863             :  * Put data information to wire
     864             :  * \param tds   state information for the socket and the TDS protocol
     865             :  * \param col   column where to store information
     866             :  * \return TDS_SUCCESS or TDS_FAIL
     867             :  */
     868             : TDSRET
     869       11164 : tds_generic_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
     870             : {
     871             :         size_t size;
     872             : 
     873       11164 :         CHECK_TDS_EXTRA(tds);
     874       11164 :         CHECK_COLUMN_EXTRA(col);
     875             : 
     876       11164 :         size = tds_fix_column_size(tds, col);
     877       11164 :         switch (col->column_varint_size) {
     878             :         case 0:
     879             :                 break;
     880        4810 :         case 1:
     881        4810 :                 tds_put_byte(tds, size);
     882        4810 :                 break;
     883        4268 :         case 2:
     884        4268 :                 tds_put_smallint(tds, size);
     885        4268 :                 break;
     886         640 :         case 5:
     887             :         case 4:
     888         640 :                 tds_put_int(tds, size);
     889         640 :                 break;
     890         628 :         case 8:
     891         628 :                 tds_put_smallint(tds, 0xffff);
     892         628 :                 break;
     893             :         }
     894             : 
     895             :         /* TDS5 wants a table name for LOBs */
     896       11164 :         if (IS_TDS50(tds->conn) && is_blob_type(col->on_server.column_type))
     897           0 :                 tds_put_smallint(tds, 0);
     898             : 
     899             :         /* TDS7.1 output collate information */
     900       11164 :         if (IS_TDS71_PLUS(tds->conn) && is_collate_type(col->on_server.column_type))
     901        5094 :                 tds_put_n(tds, tds->conn->collation, 5);
     902             : 
     903       11164 :         return TDS_SUCCESS;
     904             : }
     905             : 
     906             : /**
     907             :  * Write data to wire
     908             :  * \param tds state information for the socket and the TDS protocol
     909             :  * \param curcol column where store column information
     910             :  * \return TDS_FAIL on error or TDS_SUCCESS
     911             :  */
     912             : TDSRET
     913       19272 : tds_generic_put(TDSSOCKET * tds, TDSCOLUMN * curcol, int bcp7)
     914             : {
     915             :         unsigned char *src;
     916       19272 :         TDSBLOB *blob = NULL;
     917             :         size_t colsize, size;
     918             : 
     919             :         const char *s;
     920       19272 :         int converted = 0;
     921             : 
     922       19272 :         CHECK_TDS_EXTRA(tds);
     923       19272 :         CHECK_COLUMN_EXTRA(curcol);
     924             : 
     925       19272 :         tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: colsize = %d\n", (int) curcol->column_cur_size);
     926             : 
     927             :         /* output NULL data */
     928       19272 :         if (curcol->column_cur_size < 0) {
     929        5132 :                 tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: null param\n");
     930        5132 :                 switch (curcol->column_varint_size) {
     931           0 :                 case 5:
     932           0 :                         tds_put_int(tds, 0);
     933           0 :                         break;
     934          90 :                 case 4:
     935          90 :                         if ((bcp7 || !IS_TDS7_PLUS(tds->conn)) && is_blob_type(curcol->on_server.column_type))
     936          18 :                                 tds_put_byte(tds, 0);
     937             :                         else
     938          72 :                                 tds_put_int(tds, -1);
     939             :                         break;
     940        1400 :                 case 2:
     941        1400 :                         tds_put_smallint(tds, -1);
     942        1400 :                         break;
     943          80 :                 case 8:
     944          80 :                         tds_put_int8(tds, -1);
     945          80 :                         break;
     946        3562 :                 default:
     947        3562 :                         assert(curcol->column_varint_size);
     948             :                         /* FIXME not good for SYBLONGBINARY/SYBLONGCHAR (still not supported) */
     949        3562 :                         tds_put_byte(tds, 0);
     950        3562 :                         break;
     951             :                 }
     952             :                 return TDS_SUCCESS;
     953             :         }
     954       14140 :         colsize = curcol->column_cur_size;
     955             : 
     956       14140 :         size = tds_fix_column_size(tds, curcol);
     957             : 
     958       14140 :         src = curcol->column_data;
     959       14140 :         if (is_blob_col(curcol)) {
     960        1148 :                 blob = (TDSBLOB *) src;
     961        1148 :                 src = (unsigned char *) blob->textvalue;
     962             :         }
     963             : 
     964       14140 :         s = (char *) src;
     965             : 
     966             :         /* convert string if needed */
     967       14140 :         if (!bcp7 && curcol->char_conv && curcol->char_conv->flags != TDS_ENCODING_MEMCPY && colsize) {
     968             :                 size_t output_size;
     969             : #if 0
     970             :                 /* TODO this case should be optimized */
     971             :                 /* we know converted bytes */
     972             :                 if (curcol->char_conv->client_charset.min_bytes_per_char == curcol->char_conv->client_charset.max_bytes_per_char 
     973             :                     && curcol->char_conv->server_charset.min_bytes_per_char == curcol->char_conv->server_charset.max_bytes_per_char) {
     974             :                         converted_size = colsize * curcol->char_conv->server_charset.min_bytes_per_char / curcol->char_conv->client_charset.min_bytes_per_char;
     975             : 
     976             :                 } else {
     977             : #endif
     978             :                 /* we need to convert data before */
     979             :                 /* TODO this can be a waste of memory... */
     980        3166 :                 converted = 1;
     981        3166 :                 s = tds_convert_string(tds, curcol->char_conv, s, colsize, &output_size);
     982        3166 :                 colsize = (TDS_INT)output_size;
     983        3166 :                 if (!s) {
     984             :                         /* on conversion error put a empty string */
     985             :                         /* TODO on memory failure we should compute converted size and use chunks */
     986           0 :                         colsize = 0;
     987           0 :                         converted = -1;
     988             :                 }
     989             :         }
     990             : 
     991             :         /*
     992             :          * TODO here we limit data sent with MIN, should mark somewhere
     993             :          * and inform client ??
     994             :          * Test proprietary behavior
     995             :          */
     996       14140 :         if (IS_TDS7_PLUS(tds->conn)) {
     997       13502 :                 tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: not null param varint_size = %d\n",
     998           0 :                             curcol->column_varint_size);
     999             : 
    1000       13502 :                 switch (curcol->column_varint_size) {
    1001         560 :                 case 8:
    1002             :                         /* this difference for BCP operation is due to
    1003             :                          * a bug in different server version that does
    1004             :                          * not accept a length here */
    1005         560 :                         tds_put_int8(tds, bcp7 ? (TDS_INT8) -2 : (TDS_INT8) colsize);
    1006         560 :                         tds_put_int(tds, colsize);
    1007         560 :                         break;
    1008         584 :                 case 4: /* It's a BLOB... */
    1009         584 :                         colsize = MIN(colsize, size);
    1010             :                         /* mssql require only size */
    1011         584 :                         if (bcp7 && is_blob_type(curcol->on_server.column_type)) {
    1012             :                                 static const unsigned char textptr[] = {
    1013             :                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    1014             :                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    1015             :                                 };
    1016          62 :                                 tds_put_byte(tds, 16);
    1017          62 :                                 tds_put_n(tds, textptr, 16);
    1018          62 :                                 tds_put_n(tds, textptr, 8);
    1019             :                         }
    1020         584 :                         tds_put_int(tds, colsize);
    1021         584 :                         break;
    1022        4736 :                 case 2:
    1023        4736 :                         colsize = MIN(colsize, size);
    1024        4736 :                         tds_put_smallint(tds, colsize);
    1025        4736 :                         break;
    1026        3538 :                 case 1:
    1027        3538 :                         colsize = MIN(colsize, size);
    1028        3538 :                         tds_put_byte(tds, colsize);
    1029        3538 :                         break;
    1030        4084 :                 case 0:
    1031             :                         /* TODO should be column_size */
    1032        4084 :                         colsize = tds_get_size_by_type(curcol->on_server.column_type);
    1033        4084 :                         break;
    1034             :                 }
    1035             : 
    1036             :                 /* conversion error, exit with an error */
    1037       13502 :                 if (converted < 0)
    1038             :                         return TDS_FAIL;
    1039             : 
    1040             :                 /* put real data */
    1041       13502 :                 if (blob) {
    1042        1144 :                         tds_put_n(tds, s, colsize);
    1043             :                 } else {
    1044             : #ifdef WORDS_BIGENDIAN
    1045             :                         unsigned char buf[64];
    1046             : 
    1047             :                         if (!converted && colsize < 64) {
    1048             :                                 tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n",
    1049             :                                             tds_get_conversion_type(curcol->column_type, colsize));
    1050             :                                 memcpy(buf, s, colsize);
    1051             :                                 tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf);
    1052             :                                 s = (char *) buf;
    1053             :                         }
    1054             : #endif
    1055       12358 :                         tds_put_n(tds, s, colsize);
    1056             :                 }
    1057             :                 /* finish chunk for varchar/varbinary(max) */
    1058       13502 :                 if (curcol->column_varint_size == 8 && colsize)
    1059         544 :                         tds_put_int(tds, 0);
    1060             :         } else {
    1061             :                 /* TODO ICONV handle charset conversions for data */
    1062             :                 /* put size of data */
    1063         638 :                 switch (curcol->column_varint_size) {
    1064           4 :                 case 5: /* It's a LONGBINARY */
    1065           4 :                         colsize = MIN(colsize, 0x7fffffff);
    1066           4 :                         tds_put_int(tds, colsize);
    1067           4 :                         break;
    1068           0 :                 case 4: /* It's a BLOB... */
    1069           0 :                         tds_put_byte(tds, 16);
    1070           0 :                         tds_put_n(tds, blob->textptr, 16);
    1071           0 :                         tds_put_n(tds, blob->timestamp, 8);
    1072           0 :                         colsize = MIN(colsize, 0x7fffffff);
    1073           0 :                         tds_put_int(tds, colsize);
    1074           0 :                         break;
    1075           0 :                 case 2:
    1076           0 :                         colsize = MIN(colsize, 8000);
    1077           0 :                         tds_put_smallint(tds, colsize);
    1078           0 :                         break;
    1079         622 :                 case 1:
    1080         622 :                         if (!colsize) {
    1081          10 :                                 tds_put_byte(tds, 1);
    1082          10 :                                 if (is_char_type(curcol->column_type))
    1083          10 :                                         tds_put_byte(tds, ' ');
    1084             :                                 else
    1085           0 :                                         tds_put_byte(tds, 0);
    1086          10 :                                 if (converted > 0)
    1087           0 :                                         tds_convert_string_free((char*)src, s);
    1088             :                                 return TDS_SUCCESS;
    1089             :                         }
    1090         612 :                         colsize = MIN(colsize, 255);
    1091         612 :                         tds_put_byte(tds, colsize);
    1092         612 :                         break;
    1093          12 :                 case 0:
    1094             :                         /* TODO should be column_size */
    1095          12 :                         colsize = tds_get_size_by_type(curcol->column_type);
    1096          12 :                         break;
    1097             :                 }
    1098             : 
    1099             :                 /* conversion error, exit with an error */
    1100         628 :                 if (converted < 0)
    1101             :                         return TDS_FAIL;
    1102             : 
    1103             :                 /* put real data */
    1104         628 :                 if (blob) {
    1105           4 :                         tds_put_n(tds, s, colsize);
    1106             :                 } else {
    1107             : #ifdef WORDS_BIGENDIAN
    1108             :                         unsigned char buf[64];
    1109             : 
    1110             :                         if (!converted && colsize < 64) {
    1111             :                                 tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n",
    1112             :                                             tds_get_conversion_type(curcol->column_type, colsize));
    1113             :                                 memcpy(buf, s, colsize);
    1114             :                                 tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf);
    1115             :                                 s = (char *) buf;
    1116             :                         }
    1117             : #endif
    1118         624 :                         tds_put_n(tds, s, colsize);
    1119             :                 }
    1120             :         }
    1121       14130 :         if (converted > 0)
    1122        3166 :                 tds_convert_string_free((char*)src, s);
    1123             :         return TDS_SUCCESS;
    1124             : }
    1125             : 
    1126             : TDSRET
    1127        1524 : tds_numeric_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
    1128             : {
    1129        1524 :         col->column_size = tds_get_byte(tds);
    1130        1524 :         col->column_prec = tds_get_byte(tds);        /* precision */
    1131        1524 :         col->column_scale = tds_get_byte(tds);       /* scale */
    1132             : 
    1133             :         /* check prec/scale, don't let server crash us */
    1134        1524 :         if (col->column_prec < 1 || col->column_prec > MAXPRECISION
    1135        1524 :             || col->column_scale > col->column_prec)
    1136             :                 return TDS_FAIL;
    1137             : 
    1138        1524 :         return TDS_SUCCESS;
    1139             : }
    1140             : 
    1141             : TDS_INT
    1142        3558 : tds_numeric_row_len(TDSCOLUMN *col TDS_UNUSED)
    1143             : {
    1144        3558 :         return sizeof(TDS_NUMERIC);
    1145             : }
    1146             : 
    1147             : TDSRET
    1148        1804 : tds_numeric_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
    1149             : {
    1150             :         int colsize;
    1151             :         TDS_NUMERIC *num;
    1152             : 
    1153        1804 :         CHECK_TDS_EXTRA(tds);
    1154        1804 :         CHECK_COLUMN_EXTRA(curcol);
    1155             : 
    1156        1804 :         colsize = tds_get_byte(tds);
    1157             : 
    1158             :         /* set NULL flag in the row buffer */
    1159        1804 :         if (colsize <= 0) {
    1160         344 :                 curcol->column_cur_size = -1;
    1161         344 :                 return TDS_SUCCESS;
    1162             :         }
    1163             : 
    1164             :         /* 
    1165             :          * Since these can be passed around independent
    1166             :          * of the original column they came from, we embed the TDS_NUMERIC datatype in the row buffer
    1167             :          * instead of using the wire representation, even though it uses a few more bytes.  
    1168             :          */
    1169        1460 :         num = (TDS_NUMERIC *) curcol->column_data;
    1170        1460 :         memset(num, '\0', sizeof(TDS_NUMERIC));
    1171             :         /* TODO perhaps it would be fine to change format ?? */
    1172        1460 :         num->precision = curcol->column_prec;
    1173        1460 :         num->scale = curcol->column_scale;
    1174             : 
    1175             :         /* server is going to crash freetds ?? */
    1176             :         /* TODO close connection it server try to do so ?? */
    1177        1460 :         if (colsize > sizeof(num->array))
    1178             :                 return TDS_FAIL;
    1179        1460 :         tds_get_n(tds, num->array, colsize);
    1180             : 
    1181        1460 :         if (IS_TDS7_PLUS(tds->conn))
    1182             :                 tds_swap_numeric(num);
    1183             : 
    1184             :         /* corrected colsize for column_cur_size */
    1185        1460 :         curcol->column_cur_size = sizeof(TDS_NUMERIC);
    1186             : 
    1187        1460 :         return TDS_SUCCESS;
    1188             : }
    1189             : 
    1190             : TDSRET
    1191         698 : tds_numeric_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1192             : {
    1193         698 :         CHECK_TDS_EXTRA(tds);
    1194         698 :         CHECK_COLUMN_EXTRA(col);
    1195             : 
    1196             : #if 1
    1197         698 :         tds_put_byte(tds, tds_numeric_bytes_per_prec[col->column_prec]);
    1198         698 :         tds_put_byte(tds, col->column_prec);
    1199         698 :         tds_put_byte(tds, col->column_scale);
    1200             : #else
    1201             :         TDS_NUMERIC *num = (TDS_NUMERIC *) col->column_data;
    1202             :         tds_put_byte(tds, tds_numeric_bytes_per_prec[num->precision]);
    1203             :         tds_put_byte(tds, num->precision);
    1204             :         tds_put_byte(tds, num->scale);
    1205             : #endif
    1206             : 
    1207         698 :         return TDS_SUCCESS;
    1208             : }
    1209             : 
    1210             : TDSRET
    1211        1934 : tds_numeric_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7 TDS_UNUSED)
    1212             : {
    1213        1934 :         TDS_NUMERIC *num = (TDS_NUMERIC *) col->column_data, buf;
    1214             :         unsigned char colsize;
    1215             : 
    1216        1934 :         if (col->column_cur_size < 0) {
    1217         802 :                 tds_put_byte(tds, 0);
    1218         802 :                 return TDS_SUCCESS;
    1219             :         }
    1220        1132 :         colsize = tds_numeric_bytes_per_prec[num->precision];
    1221        1132 :         tds_put_byte(tds, colsize);
    1222             : 
    1223        1132 :         buf = *num;
    1224        1132 :         if (IS_TDS7_PLUS(tds->conn))
    1225             :                 tds_swap_numeric(&buf);
    1226        1132 :         tds_put_n(tds, buf.array, colsize);
    1227        1132 :         return TDS_SUCCESS;
    1228             : }
    1229             : 
    1230             : TDSRET
    1231           0 : tds_variant_put_info(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * col TDS_UNUSED)
    1232             : {
    1233             :         /* TODO */
    1234           0 :         return TDS_FAIL;
    1235             : }
    1236             : 
    1237             : TDSRET
    1238           0 : tds_variant_put(TDSSOCKET *tds TDS_UNUSED, TDSCOLUMN *col TDS_UNUSED, int bcp7 TDS_UNUSED)
    1239             : {
    1240             :         /* TODO */
    1241           0 :         return TDS_FAIL;
    1242             : }
    1243             : 
    1244             : TDSRET
    1245         260 : tds_msdatetime_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1246             : {
    1247         260 :         col->column_scale = col->column_prec = 0;
    1248         260 :         if (col->column_type != SYBMSDATE) {
    1249         188 :                 col->column_scale = col->column_prec = tds_get_byte(tds);
    1250         188 :                 if (col->column_prec > 7)
    1251             :                         return TDS_FAIL;
    1252             :         }
    1253         260 :         col->on_server.column_size = col->column_size = sizeof(TDS_DATETIMEALL);
    1254         260 :         return TDS_SUCCESS;
    1255             : }
    1256             : 
    1257             : TDS_INT
    1258        1396 : tds_msdatetime_row_len(TDSCOLUMN *col TDS_UNUSED)
    1259             : {
    1260        1396 :         return sizeof(TDS_DATETIMEALL);
    1261             : }
    1262             : 
    1263             : TDSRET
    1264         260 : tds_msdatetime_get(TDSSOCKET * tds, TDSCOLUMN * col)
    1265             : {
    1266         260 :         TDS_DATETIMEALL *dt = (TDS_DATETIMEALL*) col->column_data;
    1267         260 :         int size = tds_get_byte(tds);
    1268             : 
    1269         260 :         if (size == 0) {
    1270           0 :                 col->column_cur_size = -1;
    1271           0 :                 return TDS_SUCCESS;
    1272             :         }
    1273             : 
    1274         260 :         memset(dt, 0, sizeof(*dt));
    1275             : 
    1276         260 :         if (col->column_type == SYBMSDATETIMEOFFSET)
    1277          20 :                 size -= 2;
    1278         260 :         if (col->column_type != SYBMSTIME)
    1279         184 :                 size -= 3;
    1280         260 :         if (size < 0)
    1281             :                 return TDS_FAIL;
    1282             : 
    1283         260 :         dt->time_prec = col->column_prec;
    1284             : 
    1285             :         /* get time part */
    1286         260 :         if (col->column_type != SYBMSDATE) {
    1287             :                 TDS_UINT8 u8;
    1288             :                 int i;
    1289             : 
    1290         188 :                 if (size < 3 || size > 5)
    1291           0 :                         return TDS_FAIL;
    1292         188 :                 u8 = 0;
    1293         188 :                 tds_get_n(tds, &u8, size);
    1294             : #ifdef WORDS_BIGENDIAN
    1295             :                 tds_swap_bytes(&u8, 8);
    1296             : #endif
    1297         432 :                 for (i = col->column_prec; i < 7; ++i)
    1298         244 :                         u8 *= 10;
    1299         188 :                 dt->time = u8;
    1300         188 :                 dt->has_time = 1;
    1301          72 :         } else if (size != 0)
    1302             :                 return TDS_FAIL;
    1303             : 
    1304             :         /* get date part */
    1305         260 :         if (col->column_type != SYBMSTIME) {
    1306             :                 TDS_UINT ui;
    1307             : 
    1308         184 :                 ui = 0;
    1309         184 :                 tds_get_n(tds, &ui, 3);
    1310             : #ifdef WORDS_BIGENDIAN
    1311             :                 tds_swap_bytes(&ui, 4);
    1312             : #endif
    1313         184 :                 dt->has_date = 1;
    1314         184 :                 dt->date = ui - 693595;
    1315             :         }
    1316             : 
    1317             :         /* get time offset */
    1318         260 :         if (col->column_type == SYBMSDATETIMEOFFSET) {
    1319          20 :                 dt->offset = tds_get_smallint(tds);
    1320          20 :                 if (dt->offset > 840 || dt->offset < -840)
    1321             :                         return TDS_FAIL;
    1322          20 :                 dt->has_offset = 1;
    1323             :         }
    1324         260 :         col->column_cur_size = sizeof(TDS_DATETIMEALL);
    1325         260 :         return TDS_SUCCESS;
    1326             : }
    1327             : 
    1328             : TDSRET
    1329         348 : tds_msdatetime_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1330             : {
    1331             :         /* TODO precision */
    1332         348 :         if (col->on_server.column_type != SYBMSDATE)
    1333         324 :                 tds_put_byte(tds, 7);
    1334         348 :         return TDS_SUCCESS;
    1335             : }
    1336             : 
    1337             : TDSRET
    1338         348 : tds_msdatetime_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7 TDS_UNUSED)
    1339             : {
    1340         348 :         const TDS_DATETIMEALL *dta = (const TDS_DATETIMEALL *) col->column_data;
    1341             :         unsigned char buf[12], *p;
    1342             : 
    1343         348 :         if (col->column_cur_size < 0) {
    1344          72 :                 tds_put_byte(tds, 0);
    1345          72 :                 return TDS_SUCCESS;
    1346             :         }
    1347             : 
    1348             :         /* TODO precision */
    1349         276 :         p = buf + 1;
    1350         276 :         if (col->on_server.column_type != SYBMSDATE) {
    1351         252 :                 TDS_PUT_UA4LE(p, (TDS_UINT) dta->time);
    1352         252 :                 p[4] = (unsigned char) (dta->time >> 32);
    1353         252 :                 p += 5;
    1354             :         }
    1355         276 :         if (col->on_server.column_type != SYBMSTIME) {
    1356         252 :                 TDS_UINT ui = dta->date + 693595;
    1357         252 :                 TDS_PUT_UA4LE(p, ui);
    1358         252 :                 p += 3;
    1359             :         }
    1360         276 :         if (col->on_server.column_type == SYBMSDATETIMEOFFSET) {
    1361           0 :                 TDS_PUT_UA2LE(p, dta->offset);
    1362           0 :                 p += 2;
    1363             :         }
    1364         276 :         buf[0] = p - buf - 1;
    1365         276 :         tds_put_n(tds, buf, p - buf);
    1366             : 
    1367         276 :         return TDS_SUCCESS;
    1368             : }
    1369             : 
    1370             : TDSRET
    1371           8 : tds_clrudt_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1372             : {
    1373             :         /* TODO save fields */
    1374             :         /* FIXME support RPC */
    1375             : 
    1376             :         /* MAX_BYTE_SIZE */
    1377           8 :         tds_get_usmallint(tds);
    1378             : 
    1379             :         /* DB_NAME */
    1380           8 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1381             : 
    1382             :         /* SCHEMA_NAME */
    1383           8 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1384             : 
    1385             :         /* TYPE_NAME */
    1386           8 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1387             : 
    1388             :         /* UDT_METADATA */
    1389           8 :         tds_get_string(tds, tds_get_usmallint(tds), NULL, 0);
    1390             : 
    1391           8 :         col->column_size = 0x7ffffffflu;
    1392           8 :         col->column_varint_size = 8;
    1393             : 
    1394           8 :         return TDS_SUCCESS;
    1395             : }
    1396             : 
    1397             : TDS_INT
    1398          16 : tds_clrudt_row_len(TDSCOLUMN *col)
    1399             : {
    1400          16 :         col->column_varint_size = 8;
    1401             :         /* TODO save other fields */
    1402          16 :         return sizeof(TDSBLOB);
    1403             : }
    1404             : 
    1405             : TDSRET
    1406           0 : tds_clrudt_put_info(TDSSOCKET * tds, TDSCOLUMN * col TDS_UNUSED)
    1407             : {
    1408             :         /* FIXME support properly */
    1409           0 :         tds_put_byte(tds, 0);   /* db_name */
    1410           0 :         tds_put_byte(tds, 0);   /* schema_name */
    1411           0 :         tds_put_byte(tds, 0);   /* type_name */
    1412             : 
    1413           0 :         return TDS_SUCCESS;
    1414             : }
    1415             : 
    1416             : TDSRET
    1417           0 : tds_sybbigtime_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1418             : {
    1419           0 :         col->column_scale = col->column_prec = 6;
    1420           0 :         tds_get_byte(tds); /* 8, size */
    1421           0 :         tds_get_byte(tds); /* 6, precision ?? */
    1422           0 :         col->on_server.column_size = col->column_size = sizeof(TDS_UINT8);
    1423           0 :         return TDS_SUCCESS;
    1424             : }
    1425             : 
    1426             : TDS_INT
    1427          60 : tds_sybbigtime_row_len(TDSCOLUMN *col TDS_UNUSED)
    1428             : {
    1429          60 :         return sizeof(TDS_UINT8);
    1430             : }
    1431             : 
    1432             : TDSRET
    1433           0 : tds_sybbigtime_get(TDSSOCKET * tds, TDSCOLUMN * col)
    1434             : {
    1435           0 :         TDS_UINT8 *dt = (TDS_UINT8 *) col->column_data;
    1436           0 :         int size = tds_get_byte(tds);
    1437             : 
    1438           0 :         if (size == 0) {
    1439           0 :                 col->column_cur_size = -1;
    1440           0 :                 return TDS_SUCCESS;
    1441             :         }
    1442             : 
    1443           0 :         col->column_cur_size = sizeof(TDS_UINT8);
    1444           0 :         *dt = tds_get_int8(tds);
    1445             : 
    1446           0 :         return TDS_SUCCESS;
    1447             : }
    1448             : 
    1449             : TDSRET
    1450           0 : tds_sybbigtime_put_info(TDSSOCKET * tds, TDSCOLUMN * col TDS_UNUSED)
    1451             : {
    1452           0 :         tds_put_byte(tds, 8);
    1453           0 :         tds_put_byte(tds, 6);
    1454           0 :         return TDS_SUCCESS;
    1455             : }
    1456             : 
    1457             : TDSRET
    1458           0 : tds_sybbigtime_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7 TDS_UNUSED)
    1459             : {
    1460           0 :         const TDS_UINT8 *dt = (const TDS_UINT8 *) col->column_data;
    1461             : 
    1462           0 :         if (col->column_cur_size < 0) {
    1463           0 :                 tds_put_byte(tds, 0);
    1464           0 :                 return TDS_SUCCESS;
    1465             :         }
    1466             : 
    1467           0 :         tds_put_byte(tds, 8);
    1468           0 :         tds_put_int8(tds, *dt);
    1469             : 
    1470           0 :         return TDS_SUCCESS;
    1471             : }
    1472             : 
    1473             : TDSRET
    1474           0 : tds_mstabletype_get_info(TDSSOCKET *tds TDS_UNUSED, TDSCOLUMN *col TDS_UNUSED)
    1475             : {
    1476             :         /* Table type is strictly only an input variable */
    1477           0 :         return TDS_FAIL;
    1478             : }
    1479             : 
    1480             : TDS_INT
    1481           8 : tds_mstabletype_row_len(TDSCOLUMN *col TDS_UNUSED)
    1482             : {
    1483           8 :         return sizeof(TDS_TVP);
    1484             : }
    1485             : 
    1486             : TDSRET
    1487           0 : tds_mstabletype_get(TDSSOCKET *tds TDS_UNUSED, TDSCOLUMN *col TDS_UNUSED)
    1488             : {
    1489             :         /* Table type is strictly only an input variable */
    1490           0 :         return TDS_FAIL;
    1491             : }
    1492             : 
    1493             : TDSRET
    1494           8 : tds_mstabletype_put_info(TDSSOCKET *tds, TDSCOLUMN *col)
    1495             : {
    1496           8 :         TDS_TVP *table = (TDS_TVP *) col->column_data;
    1497             :         TDSFREEZE current_freeze[1];
    1498             :         size_t written;
    1499             : 
    1500             :         /* TVP_TYPENAME */
    1501           8 :         tds_put_byte(tds, 0); /* Empty DB name */
    1502             : 
    1503           8 :         tds_freeze(tds, current_freeze, 1);
    1504           8 :         tds_put_string(tds, table->schema, -1);
    1505           8 :         written = tds_freeze_written(current_freeze) / 2;
    1506           8 :         tds_freeze_close_len(current_freeze, written);
    1507             : 
    1508           8 :         tds_freeze(tds, current_freeze, 1);
    1509           8 :         tds_put_string(tds, table->name, -1);
    1510           8 :         written = tds_freeze_written(current_freeze) / 2;
    1511           8 :         tds_freeze_close_len(current_freeze, written);
    1512             : 
    1513           8 :         return TDS_SUCCESS;
    1514             : }
    1515             : 
    1516             : TDSRET
    1517           8 : tds_mstabletype_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7 TDS_UNUSED)
    1518             : {
    1519           8 :         TDS_TVP *table = (TDS_TVP *) col->column_data;
    1520             :         TDSPARAMINFO *params;
    1521             :         TDSCOLUMN *tds_col;
    1522             :         TDS_TVP_ROW *row;
    1523             :         int i;
    1524           8 :         TDS_USMALLINT num_cols = table->metadata ? table->metadata->num_cols : 0;
    1525             : 
    1526             :         /* COL_METADATA */
    1527           8 :         if (num_cols == 0)
    1528           0 :                 tds_put_smallint(tds, 0xffff); /* TVP_NULL_TOKEN */
    1529             :         else {
    1530           8 :                 tds_put_smallint(tds, num_cols);
    1531             : 
    1532           8 :                 params = table->metadata;
    1533          24 :                 for (i = 0; i < num_cols; i++) {
    1534          16 :                         tds_col = params->columns[i];
    1535             : 
    1536             :                         /* UserType*/
    1537          16 :                         tds_put_int(tds, tds_col->column_usertype);
    1538             :                         /* Flags */
    1539          16 :                         tds_put_smallint(tds, tds_col->column_flags);
    1540             :                         /* TYPE_INFO */
    1541          16 :                         tds_put_byte(tds, tds_col->on_server.column_type);
    1542          16 :                         TDS_PROPAGATE(tds_col->funcs->put_info(tds, tds_col));
    1543             : 
    1544             :                         /* ColName - Empty string */
    1545          16 :                         tds_put_byte(tds, 0x00);
    1546             :                 }
    1547             :         }
    1548             : 
    1549             :         /* TVP_END_TOKEN */
    1550           8 :         tds_put_byte(tds, 0x00);
    1551             : 
    1552          44 :         for (row = table->row; row != NULL; row = row->next) {
    1553             :                 /* TVP_ROW_TOKEN */
    1554          36 :                 tds_put_byte(tds, 0x01);
    1555             : 
    1556          36 :                 params = row->params;
    1557         108 :                 for (i = 0; i < num_cols; i++) {
    1558          72 :                         tds_col = params->columns[i];
    1559          72 :                         TDS_PROPAGATE(tds_col->funcs->put_data(tds, tds_col, 0));
    1560             :                 }
    1561             :         }
    1562             : 
    1563             :         /* TVP_END_TOKEN */
    1564           8 :         tds_put_byte(tds, 0x00);
    1565             : 
    1566           8 :         return TDS_SUCCESS;
    1567             : }
    1568             : 
    1569             : TDSRET
    1570           0 : tds_invalid_get_info(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * col TDS_UNUSED)
    1571             : {
    1572           0 :         return TDS_FAIL;
    1573             : }
    1574             : 
    1575             : TDS_INT
    1576           0 : tds_invalid_row_len(TDSCOLUMN *col TDS_UNUSED)
    1577             : {
    1578           0 :         return 0;
    1579             : }
    1580             : 
    1581             : TDSRET
    1582           0 : tds_invalid_get(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * col TDS_UNUSED)
    1583             : {
    1584           0 :         return TDS_FAIL;
    1585             : }
    1586             : 
    1587             : TDSRET
    1588           0 : tds_invalid_put_info(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * col TDS_UNUSED)
    1589             : {
    1590           0 :         return TDS_FAIL;
    1591             : }
    1592             : 
    1593             : TDSRET
    1594           0 : tds_invalid_put(TDSSOCKET *tds TDS_UNUSED, TDSCOLUMN *col TDS_UNUSED, int bcp7 TDS_UNUSED)
    1595             : {
    1596           0 :         return TDS_FAIL;
    1597             : }
    1598             : 
    1599             : #if ENABLE_EXTRA_CHECKS
    1600             : int
    1601    13027583 : tds_generic_check(const TDSCOLUMN *col TDS_UNUSED)
    1602             : {
    1603    13027583 :         return 0;
    1604             : }
    1605             : 
    1606             : int
    1607         140 : tds_sybbigtime_check(const TDSCOLUMN *col)
    1608             : {
    1609         140 :         assert(col->column_type == col->on_server.column_type);
    1610         140 :         assert(col->on_server.column_size == col->column_size);
    1611         140 :         assert(!is_numeric_type(col->column_type));
    1612         140 :         assert(!is_fixed_type(col->column_type));
    1613         140 :         assert(!is_blob_type(col->column_type));
    1614         140 :         assert(!is_variable_type(col->column_type));
    1615         140 :         assert(is_nullable_type(col->column_type));
    1616         140 :         assert(col->column_varint_size == 1);
    1617         140 :         assert(col->column_prec == 6);
    1618         140 :         assert(col->column_scale == col->column_prec);
    1619             : 
    1620         140 :         return 1;
    1621             : }
    1622             : 
    1623             : int
    1624          24 : tds_mstabletype_check(const TDSCOLUMN *col TDS_UNUSED)
    1625             : {
    1626          24 :         return 0;
    1627             : }
    1628             : 
    1629             : int
    1630         168 : tds_clrudt_check(const TDSCOLUMN *col TDS_UNUSED)
    1631             : {
    1632         168 :         return 0;
    1633             : }
    1634             : 
    1635             : int
    1636        6964 : tds_msdatetime_check(const TDSCOLUMN *col)
    1637             : {
    1638        6964 :         assert(col->column_type == col->on_server.column_type);
    1639        6964 :         assert(col->on_server.column_size == col->column_size);
    1640        6964 :         assert(!is_numeric_type(col->column_type));
    1641        6964 :         if (col->column_type == SYBMSDATE) {
    1642        1894 :                 assert(is_fixed_type(col->column_type));
    1643             :         } else {
    1644        5070 :                 assert(!is_fixed_type(col->column_type));
    1645             :         }
    1646        6964 :         assert(!is_blob_type(col->column_type));
    1647        6964 :         assert(!is_variable_type(col->column_type));
    1648        6964 :         assert(is_nullable_type(col->column_type));
    1649        6964 :         assert(col->column_varint_size == 1);
    1650        6964 :         assert(col->column_prec >= 0 && col->column_prec <= 7);
    1651        6964 :         assert(col->column_scale == col->column_prec);
    1652             : 
    1653        6964 :         return 1;
    1654             : }
    1655             : 
    1656             : int
    1657        9342 : tds_variant_check(const TDSCOLUMN *col TDS_UNUSED)
    1658             : {
    1659        9342 :         return 0;
    1660             : }
    1661             : 
    1662             : int
    1663       81912 : tds_numeric_check(const TDSCOLUMN *col)
    1664             : {
    1665       81912 :         assert(col->column_type == col->on_server.column_type);
    1666       81912 :         assert(col->on_server.column_size == col->column_size);
    1667       81912 :         assert(is_numeric_type(col->column_type));
    1668       81912 :         assert(!is_fixed_type(col->column_type));
    1669       81912 :         assert(!is_blob_type(col->column_type));
    1670       81912 :         assert(!is_variable_type(col->column_type));
    1671       81912 :         assert(col->column_varint_size == 1);
    1672       81912 :         assert(col->column_prec >= 1 && col->column_prec <= MAXPRECISION);
    1673       81912 :         assert(col->column_scale <= col->column_prec);
    1674             : 
    1675       81912 :         return 1;
    1676             : }
    1677             : 
    1678             : int
    1679           0 : tds_invalid_check(const TDSCOLUMN *col TDS_UNUSED)
    1680             : {
    1681           0 :         return 1;
    1682             : }
    1683             : #endif
    1684             : 
    1685             : 
    1686             : #define TDS_DECLARE_FUNCS(name) \
    1687             :      extern const TDSCOLUMNFUNCS tds_ ## name ## _funcs
    1688             : 
    1689             : #include <freetds/pushvis.h>
    1690             : TDS_DECLARE_FUNCS(generic);
    1691             : TDS_DECLARE_FUNCS(numeric);
    1692             : TDS_DECLARE_FUNCS(variant);
    1693             : TDS_DECLARE_FUNCS(msdatetime);
    1694             : TDS_DECLARE_FUNCS(clrudt);
    1695             : TDS_DECLARE_FUNCS(sybbigtime);
    1696             : TDS_DECLARE_FUNCS(invalid);
    1697             : TDS_DECLARE_FUNCS(mstabletype);
    1698             : #include <freetds/popvis.h>
    1699             : 
    1700             : static const TDSCOLUMNFUNCS *
    1701       94627 : tds_get_column_funcs(TDSCONNECTION *conn, int type)
    1702             : {
    1703       94627 :         switch (type) {
    1704             :         case SYBNUMERIC:
    1705             :         case SYBDECIMAL:
    1706             :                 return &tds_numeric_funcs;
    1707           8 :         case SYBMSUDT:
    1708           8 :                 return &tds_clrudt_funcs;
    1709        1602 :         case SYBVARIANT:
    1710        1602 :                 if (IS_TDS7_PLUS(conn))
    1711             :                         return &tds_variant_funcs;
    1712             :                 break;
    1713        2456 :         case SYBMSDATE:
    1714             :         case SYBMSTIME:
    1715             :         case SYBMSDATETIME2:
    1716             :         case SYBMSDATETIMEOFFSET:
    1717        2456 :                 return &tds_msdatetime_funcs;
    1718          60 :         case SYB5BIGTIME:
    1719             :         case SYB5BIGDATETIME:
    1720          60 :                 return &tds_sybbigtime_funcs;
    1721           8 :         case SYBMSTABLE:
    1722           8 :                 return &tds_mstabletype_funcs;
    1723             :         }
    1724       87697 :         return &tds_generic_funcs;
    1725             : }
    1726             : #include "tds_types.h"
    1727             : 
    1728             : #ifdef WORDS_BIGENDIAN
    1729             : void
    1730             : tds_swap_datatype(int coltype, void *b)
    1731             : {
    1732             :         unsigned char *buf = (unsigned char *) b;
    1733             : 
    1734             :         switch (coltype) {
    1735             :         case SYBDATETIME4:
    1736             :                 tds_swap_bytes(&buf[2], 2);
    1737             :         case SYBINT2:
    1738             :         case SYBUINT2:
    1739             :                 tds_swap_bytes(buf, 2);
    1740             :                 break;
    1741             :         case SYBMONEY:
    1742             :         case SYBDATETIME:
    1743             :                 tds_swap_bytes(&buf[4], 4);
    1744             :         case SYBINT4:
    1745             :         case SYBUINT4:
    1746             :         case SYBMONEY4:
    1747             :         case SYBREAL:
    1748             :         case SYBDATE:
    1749             :         case SYBTIME:
    1750             :                 tds_swap_bytes(buf, 4);
    1751             :                 break;
    1752             :         case SYBINT8:
    1753             :         case SYBUINT8:
    1754             :         case SYBFLT8:
    1755             :         case SYB5BIGTIME:
    1756             :         case SYB5BIGDATETIME:
    1757             :                 tds_swap_bytes(buf, 8);
    1758             :                 break;
    1759             :         case SYBUNIQUE:
    1760             :                 tds_swap_bytes(buf, 4);
    1761             :                 tds_swap_bytes(&buf[4], 2);
    1762             :                 tds_swap_bytes(&buf[6], 2);
    1763             :                 break;
    1764             :         }
    1765             : }
    1766             : #endif
    1767             : 
    1768             : /**
    1769             :  * Converts numeric from Microsoft representation to internal one (Sybase).
    1770             :  * \param num numeric data to convert
    1771             :  */
    1772             : static void
    1773             : tds_swap_numeric(TDS_NUMERIC *num)
    1774             : {
    1775             :         /* swap the sign */
    1776        2300 :         num->array[0] = (num->array[0] == 0) ? 1 : 0;
    1777             :         /* swap the data */
    1778        2300 :         tds_swap_bytes(&(num->array[1]), tds_numeric_bytes_per_prec[num->precision] - 1);
    1779             : }
    1780             : 

Generated by: LCOV version 1.13