LCOV - code coverage report
Current view: top level - src/tds - data.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 642 745 86.2 %
Date: 2025-01-18 12:13:41 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       73316 : tds_set_column_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
     225             : {
     226             :         /* set type */
     227       73316 :         curcol->on_server.column_type = type;
     228       73316 :         curcol->funcs = tds_get_column_funcs(conn, type);
     229       73316 :         curcol->column_type = tds_get_cardinal_type(type, curcol->column_usertype);
     230             : 
     231             :         /* set size */
     232       73316 :         curcol->column_cur_size = -1;
     233       73316 :         curcol->column_varint_size = tds_get_varint_size(conn, type);
     234       73316 :         if (curcol->column_varint_size == 0)
     235       20406 :                 curcol->column_cur_size = curcol->on_server.column_size = curcol->column_size = tds_get_size_by_type(type);
     236             : 
     237       73316 : }
     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       16470 : tds_set_param_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
     247             : {
     248       16470 :         if (IS_TDS7_PLUS(conn)) {
     249       13958 :                 switch (type) {
     250        1740 :                 case SYBVARCHAR:
     251        1740 :                         type = XSYBVARCHAR;
     252        1740 :                         break;
     253         858 :                 case SYBCHAR:
     254         858 :                         type = XSYBCHAR;
     255         858 :                         break;
     256         156 :                 case SYBVARBINARY:
     257         156 :                         type = XSYBVARBINARY;
     258         156 :                         break;
     259         826 :                 case SYBBINARY:
     260         826 :                         type = XSYBBINARY;
     261         826 :                         break;
     262         594 :                 case SYBBIT:
     263         594 :                         type = SYBBITN;
     264         594 :                         break;
     265             :                         /* avoid warning on other types */
     266             :                 default:
     267             :                         break;
     268             :                 }
     269        2512 :         } else if (IS_TDS50(conn)) {
     270        2512 :                 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       16470 :         tds_set_column_type(conn, curcol, type);
     280             : 
     281       16470 :         if (is_collate_type(type) || is_char_type(type)) {
     282        5712 :                 curcol->char_conv = conn->char_convs[is_unicode_type(type) ? client2ucs2 : client2server_chardata];
     283        5712 :                 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       16470 :         switch (type) {
     289          34 :         case SYBUNIQUE:
     290          34 :                 curcol->on_server.column_size = curcol->column_size = sizeof(TDS_UNIQUE);
     291          34 :                 break;
     292         596 :         case SYBBITN:
     293         596 :                 curcol->on_server.column_size = curcol->column_size = sizeof(TDS_TINYINT);
     294         596 :                 break;
     295             :         /* mssql 2005 don't like SYBINT4 as parameter closing connection  */
     296        3502 :         case SYBINT1:
     297             :         case SYBINT2:
     298             :         case SYBINT4:
     299             :         case SYBINT8:
     300        3502 :                 curcol->on_server.column_type = SYBINTN;
     301        3502 :                 curcol->column_varint_size = 1;
     302        3502 :                 curcol->column_cur_size = -1;
     303        3502 :                 break;
     304          48 :         case SYBMONEY4:
     305             :         case SYBMONEY:
     306          48 :                 curcol->on_server.column_type = SYBMONEYN;
     307          48 :                 curcol->column_varint_size = 1;
     308          48 :                 curcol->column_cur_size = -1;
     309          48 :                 break;
     310         726 :         case SYBDATETIME:
     311             :         case SYBDATETIME4:
     312         726 :                 curcol->on_server.column_type = SYBDATETIMN;
     313         726 :                 curcol->column_varint_size = 1;
     314         726 :                 curcol->column_cur_size = -1;
     315         726 :                 break;
     316        1846 :         case SYBFLT8:
     317             :         case SYBREAL:
     318        1846 :                 curcol->on_server.column_type = SYBFLTN;
     319        1846 :                 curcol->column_varint_size = 1;
     320        1846 :                 curcol->column_cur_size = -1;
     321        1846 :                 break;
     322         348 :         case SYBNTEXT:
     323         348 :                 if (IS_TDS72_PLUS(conn)) {
     324         116 :                         curcol->column_varint_size = 8;
     325         116 :                         curcol->on_server.column_type = XSYBNVARCHAR;
     326             :                 }
     327             :                 break;
     328         520 :         case SYBTEXT:
     329         520 :                 if (IS_TDS72_PLUS(conn)) {
     330         152 :                         curcol->column_varint_size = 8;
     331         152 :                         curcol->on_server.column_type = XSYBVARCHAR;
     332             :                 }
     333             :                 break;
     334         142 :         case SYBIMAGE:
     335         142 :                 if (IS_TDS72_PLUS(conn)) {
     336          44 :                         curcol->column_varint_size = 8;
     337          44 :                         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       16470 : }
     349             : 
     350             : TDS_SERVER_TYPE
     351     9819357 : tds_get_cardinal_type(TDS_SERVER_TYPE datatype, int usertype)
     352             : {
     353     9819357 :         switch (datatype) {
     354             :         case XSYBVARBINARY:
     355             :                 return SYBVARBINARY;
     356        5492 :         case XSYBBINARY:
     357        5492 :                 return SYBBINARY;
     358       22412 :         case SYBNTEXT:
     359       22412 :                 return SYBTEXT;
     360     1957964 :         case XSYBNVARCHAR:
     361             :         case XSYBVARCHAR:
     362     1957964 :                 return SYBVARCHAR;
     363     1075925 :         case XSYBNCHAR:
     364             :         case XSYBCHAR:
     365     1075925 :                 return SYBCHAR;
     366         942 :         case SYB5INT8:
     367         942 :                 return SYBINT8;
     368       18936 :         case SYBLONGBINARY:
     369       18936 :                 switch (usertype) {
     370             :                 case USER_UNICHAR_TYPE:
     371             :                 case USER_UNIVARCHAR_TYPE:
     372             :                         return SYBTEXT;
     373             :                 }
     374             :                 break;
     375         208 :         case SYBMSXML:
     376         208 :                 return SYBLONGCHAR;
     377             :         default:
     378             :                 break;
     379             :         }
     380     6728182 :         return datatype;
     381             : }
     382             : 
     383             : TDSRET
     384       52574 : tds_generic_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
     385             : {
     386       52574 :         switch (col->column_varint_size) {
     387           8 :         case 8:
     388           8 :                 col->column_size = 0x7ffffffflu;
     389           8 :                 break;
     390        2802 :         case 5:
     391             :         case 4:
     392        2802 :                 col->column_size = tds_get_int(tds);
     393        2802 :                 if (col->column_size < 0)
     394             :                         return TDS_FAIL;
     395             :                 break;
     396       15436 :         case 2:
     397             :                 /* assure > 0 */
     398       15436 :                 col->column_size = tds_get_smallint(tds);
     399             :                 /* under TDS7.2 this means ?var???(MAX) */
     400       15436 :                 if (col->column_size < 0 && IS_TDS72_PLUS(tds->conn)) {
     401          92 :                         if (is_char_type(col->column_type))
     402          72 :                                 col->column_size = 0x3ffffffflu;
     403             :                         else
     404          20 :                                 col->column_size = 0x7ffffffflu;
     405             : 
     406          92 :                         col->column_varint_size = 8;
     407             :                 }
     408       15436 :                 if (col->column_size < 0)
     409             :                         return TDS_FAIL;
     410             :                 break;
     411       21080 :         case 1:
     412       21080 :                 col->column_size = tds_get_byte(tds);
     413       21080 :                 break;
     414       13248 :         case 0:
     415       13248 :                 col->column_size = tds_get_size_by_type(col->column_type);
     416       13248 :                 break;
     417             :         }
     418             : 
     419       52574 :         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       16204 :                 tds_get_n(tds, col->column_collation, 5);
     427       16204 :                 col->char_conv =
     428       16204 :                         tds_iconv_from_collate(tds->conn, col->column_collation);
     429             :         }
     430             : 
     431             :         /* Only read table_name for blob columns (eg. not for SYBLONGBINARY) */
     432       52574 :         if (is_blob_type(col->on_server.column_type)) {
     433             :                 /* discard this additional byte */
     434        1912 :                 if (IS_TDS72_PLUS(tds->conn)) {
     435         486 :                         unsigned char num_parts = tds_get_byte(tds);
     436             :                         /* TODO do not discard first ones */
     437         972 :                         for (; num_parts; --num_parts) {
     438         486 :                                 tds_dstr_get(tds, &col->table_name, tds_get_usmallint(tds));
     439             :                         }
     440             :                 } else {
     441        1426 :                         tds_dstr_get(tds, &col->table_name, tds_get_usmallint(tds));
     442             :                 }
     443       50662 :         } else if (IS_TDS72_PLUS(tds->conn) && col->on_server.column_type == SYBMSXML) {
     444           8 :                 unsigned char has_schema = tds_get_byte(tds);
     445           8 :                 if (has_schema) {
     446             :                         /* discard schema informations */
     447           2 :                         tds_get_string(tds, tds_get_byte(tds), NULL, 0);        /* dbname */
     448           2 :                         tds_get_string(tds, tds_get_byte(tds), NULL, 0);        /* schema owner */
     449           2 :                         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      114000 : tds_generic_row_len(TDSCOLUMN *col)
     460             : {
     461      114000 :         CHECK_COLUMN_EXTRA(col);
     462             : 
     463      114000 :         if (is_blob_col(col))
     464             :                 return sizeof(TDSBLOB);
     465      106470 :         return col->column_size;
     466             : }
     467             : 
     468             : static TDSRET
     469        2756 : 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        2756 :         res = tds_dynamic_stream_init(&w, pp, allocated);
     479        2756 :         if (TDS_FAILED(res))
     480             :                 return res;
     481             : 
     482        2756 :         if (USE_ICONV && curcol->char_conv)
     483         820 :                 res = tds_convert_stream(tds, curcol->char_conv, to_client, r_stream, &w.stream);
     484             :         else
     485        1936 :                 res = tds_copy_stream(r_stream, &w.stream);
     486        2756 :         curcol->column_cur_size = w.size;
     487             :         return res;
     488             : }
     489             : 
     490             : typedef struct tds_varmax_stream {
     491             :         TDSINSTREAM stream;
     492             :         TDSSOCKET *tds;
     493             :         TDS_INT chunk_left;
     494             : } TDSVARMAXSTREAM;
     495             : 
     496             : static int
     497         162 : tds_varmax_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
     498             : {
     499         162 :         TDSVARMAXSTREAM *s = (TDSVARMAXSTREAM *) stream;
     500             : 
     501             :         /* read chunk len if needed */
     502         162 :         if (s->chunk_left == 0) {
     503         162 :                 TDS_INT l = tds_get_int(s->tds);
     504         162 :                 if (l <= 0) l = -1;
     505         162 :                 s->chunk_left = l;
     506             :         }
     507             : 
     508             :         /* no more data ?? */
     509         162 :         if (s->chunk_left < 0)
     510             :                 return 0;
     511             : 
     512             :         /* read part of data */
     513          74 :         if (len > s->chunk_left)
     514          74 :                 len = s->chunk_left;
     515          74 :         s->chunk_left -= len;
     516          74 :         if (tds_get_n(s->tds, ptr, len))
     517          74 :                 return len;
     518             :         return -1;
     519             : }
     520             : 
     521             : static TDSRET
     522          88 : tds72_get_varmax(TDSSOCKET * tds, TDSCOLUMN * curcol)
     523             : {
     524             :         TDS_INT8 len;
     525             :         TDSVARMAXSTREAM r;
     526          88 :         size_t allocated = 0;
     527          88 :         void **pp = (void**) &(((TDSBLOB*) curcol->column_data)->textvalue);
     528             : 
     529          88 :         len = tds_get_int8(tds);
     530             : 
     531             :         /* NULL */
     532          88 :         if (len == -1) {
     533           0 :                 curcol->column_cur_size = -1;
     534           0 :                 return TDS_SUCCESS;
     535             :         }
     536             : 
     537             :         /* try to allocate an initial buffer */
     538             :         if (len > (TDS_INT8) (~((size_t) 0) >> 1))
     539             :                 return TDS_FAIL;
     540          88 :         if (len > 0) {
     541          66 :                 TDS_ZERO_FREE(*pp);
     542          66 :                 allocated = (size_t) len;
     543          66 :                 if (is_unicode_type(curcol->on_server.column_type))
     544          16 :                         allocated /= 2;
     545             :         }
     546             : 
     547          88 :         r.stream.read = tds_varmax_stream_read;
     548          88 :         r.tds = tds;
     549          88 :         r.chunk_left = 0;
     550             : 
     551          88 :         return tds_get_char_dynamic(tds, curcol, pp, allocated, &r.stream);
     552             : }
     553             : 
     554             : TDS_COMPILE_CHECK(tds_variant_size,  sizeof(((TDSVARIANT*)0)->data) == sizeof(((TDSBLOB*)0)->textvalue));
     555             : TDS_COMPILE_CHECK(tds_variant_offset,TDS_OFFSET(TDSVARIANT, data) == TDS_OFFSET(TDSBLOB, textvalue));
     556             : 
     557             : /*
     558             :  * This strange type has following structure 
     559             :  * 0 len (int32) -- NULL 
     560             :  * len (int32), type (int8), data -- ints, date, etc
     561             :  * len (int32), type (int8), 7 (int8), collation, column size (int16) -- [n]char, [n]varchar, binary, varbinary 
     562             :  * BLOBS (text/image) not supported
     563             :  */
     564             : TDSRET
     565         104 : tds_variant_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
     566             : {
     567         104 :         unsigned int colsize = tds_get_uint(tds);
     568             :         int varint;
     569             :         TDS_SERVER_TYPE type;
     570             :         TDS_UCHAR info_len;
     571             :         TDSVARIANT *v;
     572             :         TDSRET rc;
     573             : 
     574             :         /* NULL */
     575         104 :         curcol->column_cur_size = -1;
     576         104 :         if (colsize < 2) {
     577           4 :                 tds_get_n(tds, NULL, colsize);
     578           4 :                 return TDS_SUCCESS;
     579             :         }
     580             : 
     581         100 :         type = (TDS_SERVER_TYPE) tds_get_byte(tds);
     582         100 :         info_len = tds_get_byte(tds);
     583         100 :         if (!is_variant_inner_type(type))
     584             :                 goto error_type;
     585         100 :         v = (TDSVARIANT*) curcol->column_data;
     586         100 :         v->type = type;
     587         100 :         colsize -= 2;
     588         100 :         if (info_len > colsize)
     589             :                 goto error_type;
     590         100 :         if (is_collate_type(type)) {
     591          42 :                 if (sizeof(v->collation) > info_len)
     592             :                         goto error_type;
     593          42 :                 tds_get_n(tds, v->collation, sizeof(v->collation));
     594          42 :                 colsize -= sizeof(v->collation);
     595          42 :                 info_len -= sizeof(v->collation);
     596          42 :                 curcol->char_conv = is_unicode_type(type) ? 
     597          42 :                         tds->conn->char_convs[client2ucs2] : tds_iconv_from_collate(tds->conn, v->collation);
     598             :         }
     599             : 
     600             :         /* special case for numeric */
     601         100 :         if (is_numeric_type(type)) {
     602             :                 TDS_NUMERIC *num;
     603          12 :                 if (info_len != 2)
     604             :                         goto error_type;
     605          12 :                 if (v->data)
     606           6 :                         TDS_ZERO_FREE(v->data);
     607          12 :                 v->data_len = sizeof(TDS_NUMERIC);
     608          12 :                 num = tds_new0(TDS_NUMERIC, 1);
     609          12 :                 if (!num)
     610             :                         goto error_memory;
     611          12 :                 v->data = (TDS_CHAR *) num;
     612          12 :                 num->precision = tds_get_byte(tds);
     613          12 :                 num->scale     = tds_get_byte(tds);
     614          12 :                 colsize -= 2;
     615             :                 /* check prec/scale, don't let server crash us */
     616          12 :                 if (num->precision < 1 || num->precision > MAXPRECISION
     617          12 :                     || num->scale > num->precision)
     618             :                         goto error_type;
     619          12 :                 if (colsize > sizeof(num->array))
     620             :                         goto error_type;
     621          12 :                 curcol->column_cur_size = colsize;
     622          12 :                 tds_get_n(tds, num->array, colsize);
     623          12 :                 if (IS_TDS7_PLUS(tds->conn))
     624             :                         tds_swap_numeric(num);
     625             :                 return TDS_SUCCESS;
     626             :         }
     627             : 
     628             :         /* special case for MS date/time */
     629          88 :         switch (type) {
     630           8 :         case SYBMSTIME:
     631             :         case SYBMSDATETIME2:
     632             :         case SYBMSDATETIMEOFFSET:
     633           8 :                 if (info_len != 1)
     634             :                         goto error_type;
     635           8 :                 curcol->column_scale = curcol->column_prec = tds_get_byte(tds);
     636           8 :                 if (curcol->column_prec > 7)
     637             :                         goto error_type;
     638           8 :                 colsize -= info_len;
     639           8 :                 info_len = 0;
     640             :                 /* fall through */
     641           2 :         case SYBMSDATE:
     642           2 :                 if (info_len != 0)
     643             :                         goto error_type;
     644             :                 /* dirty trick */
     645          10 :                 tds->in_buf[--tds->in_pos] = colsize;
     646          10 :                 if (v->data)
     647           0 :                         TDS_ZERO_FREE(v->data);
     648          10 :                 v->data_len = sizeof(TDS_DATETIMEALL);
     649          10 :                 v->data = tds_new0(TDS_CHAR, sizeof(TDS_DATETIMEALL));
     650          10 :                 curcol->column_type = type;
     651          10 :                 curcol->column_data = (unsigned char *) v->data;
     652             :                 /* trick, call get function */
     653          10 :                 rc = tds_msdatetime_get(tds, curcol);
     654          10 :                 curcol->column_type = SYBVARIANT;
     655          10 :                 curcol->column_data = (unsigned char *) v;
     656          10 :                 return rc;
     657             :         default:
     658             :                 break;
     659             :         }
     660          78 :         varint = (type == SYBUNIQUE) ? 0 : tds_get_varint_size(tds->conn, type);
     661         156 :         if (varint != info_len || varint > 2)
     662             :                 goto error_type;
     663          78 :         switch (varint) {
     664          30 :         case 0:
     665          30 :                 v->size = tds_get_size_by_type(type);
     666          30 :                 break;
     667           0 :         case 1:
     668           0 :                 v->size = tds_get_byte(tds);
     669           0 :                 break;
     670          48 :         case 2:
     671          48 :                 v->size = tds_get_smallint(tds);
     672          48 :                 break;
     673             :         default:
     674             :                 goto error_type;
     675             :         }
     676          78 :         colsize -= info_len;
     677          78 :         curcol->column_cur_size = colsize;
     678          78 :         if (v->data)
     679           6 :                 TDS_ZERO_FREE(v->data);
     680          78 :         if (colsize) {
     681             :                 TDSRET res;
     682             :                 TDSDATAINSTREAM r;
     683             : 
     684          78 :                 if (USE_ICONV && curcol->char_conv)
     685          12 :                         v->type = tds_get_cardinal_type(type, 0);
     686             : 
     687          78 :                 tds_datain_stream_init(&r, tds, colsize);
     688          78 :                 res = tds_get_char_dynamic(tds, curcol, (void **) &v->data, colsize, &r.stream);
     689          78 :                 if (TDS_FAILED(res))
     690           0 :                         return res;
     691          78 :                 colsize = curcol->column_cur_size;
     692             : #ifdef WORDS_BIGENDIAN
     693             :                 tds_swap_datatype(tds_get_conversion_type(type, colsize), v->data);
     694             : #endif
     695             :         }
     696          78 :         v->data_len = colsize;
     697          78 :         CHECK_COLUMN_EXTRA(curcol);
     698          78 :         return TDS_SUCCESS;
     699             : 
     700           0 : error_type:
     701           0 : error_memory:
     702           0 :         tds_get_n(tds, NULL, colsize);
     703           0 :         return TDS_FAIL;
     704             : }
     705             : 
     706             : /**
     707             :  * Read a data from wire
     708             :  * \param tds state information for the socket and the TDS protocol
     709             :  * \param curcol column where store column information
     710             :  * \return TDS_FAIL on error or TDS_SUCCESS
     711             :  */
     712             : TDSRET
     713      850662 : tds_generic_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
     714             : {
     715             :         unsigned char *dest;
     716             :         int len, colsize;
     717             :         int fillchar;
     718      850662 :         TDSBLOB *blob = NULL;
     719             : 
     720      850662 :         CHECK_TDS_EXTRA(tds);
     721      850662 :         CHECK_COLUMN_EXTRA(curcol);
     722             : 
     723      850662 :         tdsdump_log(TDS_DBG_INFO1, "tds_get_data: type %d, varint size %d\n", curcol->column_type, curcol->column_varint_size);
     724      850662 :         switch (curcol->column_varint_size) {
     725        2126 :         case 4:
     726             :                 /* It's a BLOB... */
     727        2126 :                 len = tds_get_byte(tds);
     728        2126 :                 blob = (TDSBLOB *) curcol->column_data;
     729        2126 :                 if (len == 16) {        /*  Jeff's hack */
     730        1938 :                         tds_get_n(tds, blob->textptr, 16);
     731        1938 :                         tds_get_n(tds, blob->timestamp, 8);
     732        1938 :                         blob->valid_ptr = true;
     733        2436 :                         if (IS_TDS72_PLUS(tds->conn) &&
     734         498 :                             memcmp(blob->textptr, "dummy textptr\0\0",16) == 0)
     735         498 :                                 blob->valid_ptr = false;
     736        1938 :                         colsize = tds_get_int(tds);
     737             :                 } else {
     738             :                         colsize = -1;
     739             :                 }
     740             :                 break;
     741         778 :         case 5:
     742         778 :                 colsize = tds_get_int(tds);
     743         778 :                 if (colsize == 0)
     744           0 :                         colsize = -1;
     745             :                 break;
     746          88 :         case 8:
     747          88 :                 return tds72_get_varmax(tds, curcol);
     748      279673 :         case 2:
     749      279673 :                 colsize = tds_get_smallint(tds);
     750      279673 :                 break;
     751      297322 :         case 1:
     752      297322 :                 colsize = tds_get_byte(tds);
     753      297322 :                 if (colsize == 0)
     754        7620 :                         colsize = -1;
     755             :                 break;
     756      270675 :         case 0:
     757             :                 /* TODO this should be column_size */
     758      270675 :                 colsize = tds_get_size_by_type(curcol->column_type);
     759             :                 break;
     760             :         default:
     761             :                 colsize = -1;
     762             :                 break;
     763             :         }
     764      850574 :         if (IS_TDSDEAD(tds))
     765             :                 return TDS_FAIL;
     766             : 
     767      850574 :         tdsdump_log(TDS_DBG_INFO1, "tds_get_data(): wire column size is %d \n", colsize);
     768             :         /* set NULL flag in the row buffer */
     769      850574 :         if (colsize < 0) {
     770       10690 :                 curcol->column_cur_size = -1;
     771       10690 :                 return TDS_SUCCESS;
     772             :         }
     773             : 
     774             :         /* 
     775             :          * We're now set to read the data from the wire.  For varying types (e.g. char/varchar)
     776             :          * make sure that curcol->column_cur_size reflects the size of the read data, 
     777             :          * after any charset conversion.  tds_get_char_data() does that for you, 
     778             :          * but of course tds_get_n() doesn't.  
     779             :          *
     780             :          * colsize == wire_size, bytes to read
     781             :          * curcol->column_cur_size == sizeof destination buffer, room to write
     782             :          */
     783      839884 :         dest = curcol->column_data;
     784      839884 :         if (is_blob_col(curcol)) {
     785             :                 TDSDATAINSTREAM r;
     786             :                 size_t allocated;
     787             :                 TDSRET ret;
     788             : 
     789        2716 :                 blob = (TDSBLOB *) dest;        /* cf. column_varint_size case 4, above */
     790             : 
     791             :                 /* empty string */
     792        2716 :                 if (colsize == 0) {
     793         126 :                         curcol->column_cur_size = 0;
     794         126 :                         if (blob->textvalue)
     795           0 :                                 TDS_ZERO_FREE(blob->textvalue);
     796             :                         return TDS_SUCCESS;
     797             :                 }
     798             : 
     799        2590 :                 allocated = MAX(curcol->column_cur_size, 0);
     800        2590 :                 if (colsize > allocated) {
     801        2322 :                         TDS_ZERO_FREE(blob->textvalue);
     802        2322 :                         allocated = colsize;
     803        2322 :                         if (is_unicode_type(curcol->on_server.column_type))
     804         528 :                                 allocated /= 2;
     805             :                 }
     806             : 
     807        2590 :                 tds_datain_stream_init(&r, tds, colsize);
     808        2590 :                 ret = tds_get_char_dynamic(tds, curcol, (void **) &blob->textvalue, allocated, &r.stream);
     809        2590 :                 if (TDS_FAILED(ret) && TDS_UNLIKELY(r.wire_size > 0)) {
     810           0 :                         tds_get_n(tds, NULL, r.wire_size);
     811           0 :                         return ret;
     812             :                 }
     813             :                 return TDS_SUCCESS;
     814             :         }
     815             : 
     816             :         /* non-numeric and non-blob */
     817             : 
     818      837168 :         if (USE_ICONV && curcol->char_conv) {
     819      382283 :                 if (TDS_FAILED(tds_get_char_data(tds, (char *) dest, colsize, curcol)))
     820             :                         return TDS_FAIL;
     821             :         } else {
     822             :                 /*
     823             :                  * special case, some servers seem to return more data in some conditions
     824             :                  * (ASA 7 returning 4 byte nullable integer)
     825             :                  */
     826      454885 :                 int discard_len = 0;
     827      454885 :                 if (colsize > curcol->column_size) {
     828           0 :                         discard_len = colsize - curcol->column_size;
     829           0 :                         colsize = curcol->column_size;
     830             :                 }
     831      454885 :                 if (!tds_get_n(tds, dest, colsize))
     832             :                         return TDS_FAIL;
     833      454885 :                 if (discard_len > 0)
     834           0 :                         tds_get_n(tds, NULL, discard_len);
     835      454885 :                 curcol->column_cur_size = colsize;
     836             :         }
     837             : 
     838             :         /* pad (UNI)CHAR and BINARY types */
     839      837168 :         fillchar = 0;
     840      837168 :         switch (curcol->column_type) {
     841             :         /* extra handling for SYBLONGBINARY */
     842           0 :         case SYBLONGBINARY:
     843           0 :                 if (curcol->column_usertype != USER_UNICHAR_TYPE)
     844             :                         break;
     845             :         case SYBCHAR:
     846             :         case XSYBCHAR:
     847      166144 :                 if (curcol->column_size != curcol->on_server.column_size)
     848             :                         break;
     849             :                 /* FIXME use client charset */
     850             :                 fillchar = ' ';
     851      165787 :         case SYBBINARY:
     852             :         case XSYBBINARY:
     853      165787 :                 if (colsize < curcol->column_size)
     854          70 :                         memset(dest + colsize, fillchar, curcol->column_size - colsize);
     855      165787 :                 colsize = curcol->column_size;
     856      165787 :                 break;
     857             :         default:
     858             :                 break;
     859             :         }
     860             : 
     861             : #ifdef WORDS_BIGENDIAN
     862             :         tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n", tds_get_conversion_type(curcol->column_type, colsize));
     863             :         tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), dest);
     864             : #endif
     865         453 :         return TDS_SUCCESS;
     866             : }
     867             : 
     868             : /**
     869             :  * Put data information to wire
     870             :  * \param tds   state information for the socket and the TDS protocol
     871             :  * \param col   column where to store information
     872             :  * \return TDS_SUCCESS or TDS_FAIL
     873             :  */
     874             : TDSRET
     875        8138 : tds_generic_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
     876             : {
     877             :         size_t size;
     878             : 
     879        8138 :         CHECK_TDS_EXTRA(tds);
     880        8138 :         CHECK_COLUMN_EXTRA(col);
     881             : 
     882        8138 :         size = tds_fix_column_size(tds, col);
     883        8138 :         switch (col->column_varint_size) {
     884             :         case 0:
     885             :                 break;
     886        3532 :         case 1:
     887        3532 :                 tds_put_byte(tds, size);
     888        3532 :                 break;
     889        3056 :         case 2:
     890        3056 :                 tds_put_smallint(tds, size);
     891        3056 :                 break;
     892         628 :         case 5:
     893             :         case 4:
     894         628 :                 tds_put_int(tds, size);
     895         628 :                 break;
     896         312 :         case 8:
     897         312 :                 tds_put_smallint(tds, 0xffff);
     898         312 :                 break;
     899             :         }
     900             : 
     901             :         /* TDS5 wants a table name for LOBs */
     902        8138 :         if (IS_TDS50(tds->conn) && is_blob_type(col->on_server.column_type))
     903           0 :                 tds_put_smallint(tds, 0);
     904             : 
     905             :         /* TDS7.1 output collate information */
     906        8138 :         if (IS_TDS71_PLUS(tds->conn) && is_collate_type(col->on_server.column_type))
     907        3674 :                 tds_put_n(tds, tds->conn->collation, 5);
     908             : 
     909        8138 :         return TDS_SUCCESS;
     910             : }
     911             : 
     912             : /**
     913             :  * Write data to wire
     914             :  * \param tds state information for the socket and the TDS protocol
     915             :  * \param curcol column where store column information
     916             :  * \return TDS_FAIL on error or TDS_SUCCESS
     917             :  */
     918             : TDSRET
     919       14036 : tds_generic_put(TDSSOCKET * tds, TDSCOLUMN * curcol, int bcp7)
     920             : {
     921             :         unsigned char *src;
     922       14036 :         TDSBLOB *blob = NULL;
     923             :         size_t colsize, size;
     924             : 
     925             :         const char *s;
     926       14036 :         int converted = 0;
     927             : 
     928       14036 :         CHECK_TDS_EXTRA(tds);
     929       14036 :         CHECK_COLUMN_EXTRA(curcol);
     930             : 
     931       14036 :         tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: colsize = %d\n", (int) curcol->column_cur_size);
     932             : 
     933             :         /* output NULL data */
     934       14036 :         if (curcol->column_cur_size < 0) {
     935        3872 :                 tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: null param\n");
     936        3872 :                 switch (curcol->column_varint_size) {
     937           0 :                 case 5:
     938           0 :                         tds_put_int(tds, 0);
     939           0 :                         break;
     940          84 :                 case 4:
     941          84 :                         if ((bcp7 || !IS_TDS7_PLUS(tds->conn)) && is_blob_type(curcol->on_server.column_type))
     942          12 :                                 tds_put_byte(tds, 0);
     943             :                         else
     944          72 :                                 tds_put_int(tds, -1);
     945             :                         break;
     946        1050 :                 case 2:
     947        1050 :                         tds_put_smallint(tds, -1);
     948        1050 :                         break;
     949          38 :                 case 8:
     950          38 :                         tds_put_int8(tds, -1);
     951          38 :                         break;
     952        2700 :                 default:
     953        2700 :                         assert(curcol->column_varint_size);
     954             :                         /* FIXME not good for SYBLONGBINARY/SYBLONGCHAR (still not supported) */
     955        2700 :                         tds_put_byte(tds, 0);
     956        2700 :                         break;
     957             :                 }
     958             :                 return TDS_SUCCESS;
     959             :         }
     960       10164 :         colsize = curcol->column_cur_size;
     961             : 
     962       10164 :         size = tds_fix_column_size(tds, curcol);
     963             : 
     964       10164 :         src = curcol->column_data;
     965       10164 :         if (is_blob_col(curcol)) {
     966         842 :                 blob = (TDSBLOB *) src;
     967         842 :                 src = (unsigned char *) blob->textvalue;
     968             :         }
     969             : 
     970       10164 :         s = (char *) src;
     971             : 
     972             :         /* convert string if needed */
     973       10164 :         if (!bcp7 && curcol->char_conv && curcol->char_conv->flags != TDS_ENCODING_MEMCPY && colsize) {
     974             :                 size_t output_size;
     975             : #if 0
     976             :                 /* TODO this case should be optimized */
     977             :                 /* we know converted bytes */
     978             :                 if (curcol->char_conv->client_charset.min_bytes_per_char == curcol->char_conv->client_charset.max_bytes_per_char 
     979             :                     && curcol->char_conv->server_charset.min_bytes_per_char == curcol->char_conv->server_charset.max_bytes_per_char) {
     980             :                         converted_size = colsize * curcol->char_conv->server_charset.min_bytes_per_char / curcol->char_conv->client_charset.min_bytes_per_char;
     981             : 
     982             :                 } else {
     983             : #endif
     984             :                 /* we need to convert data before */
     985             :                 /* TODO this can be a waste of memory... */
     986        2258 :                 converted = 1;
     987        2258 :                 s = tds_convert_string(tds, curcol->char_conv, s, colsize, &output_size);
     988        2258 :                 colsize = (TDS_INT)output_size;
     989        2258 :                 if (!s) {
     990             :                         /* on conversion error put a empty string */
     991             :                         /* TODO on memory failure we should compute converted size and use chunks */
     992           0 :                         colsize = 0;
     993           0 :                         converted = -1;
     994             :                 }
     995             :         }
     996             : 
     997             :         /*
     998             :          * TODO here we limit data sent with MIN, should mark somewhere
     999             :          * and inform client ??
    1000             :          * Test proprietary behavior
    1001             :          */
    1002       10164 :         if (IS_TDS7_PLUS(tds->conn)) {
    1003        9530 :                 tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: not null param varint_size = %d\n",
    1004           0 :                             curcol->column_varint_size);
    1005             : 
    1006        9530 :                 switch (curcol->column_varint_size) {
    1007         274 :                 case 8:
    1008             :                         /* this difference for BCP operation is due to
    1009             :                          * a bug in different server version that does
    1010             :                          * not accept a length here */
    1011         274 :                         tds_put_int8(tds, bcp7 ? (TDS_INT8) -2 : (TDS_INT8) colsize);
    1012         274 :                         tds_put_int(tds, colsize);
    1013         274 :                         break;
    1014         564 :                 case 4: /* It's a BLOB... */
    1015         564 :                         colsize = MIN(colsize, size);
    1016             :                         /* mssql require only size */
    1017         564 :                         if (bcp7 && is_blob_type(curcol->on_server.column_type)) {
    1018             :                                 static const unsigned char textptr[] = {
    1019             :                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    1020             :                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    1021             :                                 };
    1022          42 :                                 tds_put_byte(tds, 16);
    1023          42 :                                 tds_put_n(tds, textptr, 16);
    1024          42 :                                 tds_put_n(tds, textptr, 8);
    1025             :                         }
    1026         564 :                         tds_put_int(tds, colsize);
    1027         564 :                         break;
    1028        3348 :                 case 2:
    1029        3348 :                         colsize = MIN(colsize, size);
    1030        3348 :                         tds_put_smallint(tds, colsize);
    1031        3348 :                         break;
    1032        2300 :                 case 1:
    1033        2300 :                         colsize = MIN(colsize, size);
    1034        2300 :                         tds_put_byte(tds, colsize);
    1035        2300 :                         break;
    1036        3044 :                 case 0:
    1037             :                         /* TODO should be column_size */
    1038        3044 :                         colsize = tds_get_size_by_type(curcol->on_server.column_type);
    1039        3044 :                         break;
    1040             :                 }
    1041             : 
    1042             :                 /* conversion error, exit with an error */
    1043        9530 :                 if (converted < 0)
    1044             :                         return TDS_FAIL;
    1045             : 
    1046             :                 /* put real data */
    1047        9530 :                 if (blob) {
    1048         838 :                         tds_put_n(tds, s, colsize);
    1049             :                 } else {
    1050             : #ifdef WORDS_BIGENDIAN
    1051             :                         unsigned char buf[64];
    1052             : 
    1053             :                         if (!converted && colsize < 64) {
    1054             :                                 tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n",
    1055             :                                             tds_get_conversion_type(curcol->column_type, colsize));
    1056             :                                 memcpy(buf, s, colsize);
    1057             :                                 tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf);
    1058             :                                 s = (char *) buf;
    1059             :                         }
    1060             : #endif
    1061        8692 :                         tds_put_n(tds, s, colsize);
    1062             :                 }
    1063             :                 /* finish chunk for varchar/varbinary(max) */
    1064        9530 :                 if (curcol->column_varint_size == 8 && colsize)
    1065         266 :                         tds_put_int(tds, 0);
    1066             :         } else {
    1067             :                 /* TODO ICONV handle charset conversions for data */
    1068             :                 /* put size of data */
    1069         634 :                 switch (curcol->column_varint_size) {
    1070           4 :                 case 5: /* It's a LONGBINARY */
    1071           4 :                         colsize = MIN(colsize, 0x7fffffff);
    1072           4 :                         tds_put_int(tds, colsize);
    1073           4 :                         break;
    1074           0 :                 case 4: /* It's a BLOB... */
    1075           0 :                         tds_put_byte(tds, 16);
    1076           0 :                         tds_put_n(tds, blob->textptr, 16);
    1077           0 :                         tds_put_n(tds, blob->timestamp, 8);
    1078           0 :                         colsize = MIN(colsize, 0x7fffffff);
    1079           0 :                         tds_put_int(tds, colsize);
    1080           0 :                         break;
    1081           0 :                 case 2:
    1082           0 :                         colsize = MIN(colsize, 8000);
    1083           0 :                         tds_put_smallint(tds, colsize);
    1084           0 :                         break;
    1085         618 :                 case 1:
    1086         618 :                         if (!colsize) {
    1087          10 :                                 tds_put_byte(tds, 1);
    1088          10 :                                 if (is_char_type(curcol->column_type))
    1089          10 :                                         tds_put_byte(tds, ' ');
    1090             :                                 else
    1091           0 :                                         tds_put_byte(tds, 0);
    1092          10 :                                 if (converted > 0)
    1093           0 :                                         tds_convert_string_free((char*)src, s);
    1094             :                                 return TDS_SUCCESS;
    1095             :                         }
    1096         608 :                         colsize = MIN(colsize, 255);
    1097         608 :                         tds_put_byte(tds, colsize);
    1098         608 :                         break;
    1099          12 :                 case 0:
    1100             :                         /* TODO should be column_size */
    1101          12 :                         colsize = tds_get_size_by_type(curcol->column_type);
    1102          12 :                         break;
    1103             :                 }
    1104             : 
    1105             :                 /* conversion error, exit with an error */
    1106         624 :                 if (converted < 0)
    1107             :                         return TDS_FAIL;
    1108             : 
    1109             :                 /* put real data */
    1110         624 :                 if (blob) {
    1111           4 :                         tds_put_n(tds, s, colsize);
    1112             :                 } else {
    1113             : #ifdef WORDS_BIGENDIAN
    1114             :                         unsigned char buf[64];
    1115             : 
    1116             :                         if (!converted && colsize < 64) {
    1117             :                                 tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n",
    1118             :                                             tds_get_conversion_type(curcol->column_type, colsize));
    1119             :                                 memcpy(buf, s, colsize);
    1120             :                                 tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf);
    1121             :                                 s = (char *) buf;
    1122             :                         }
    1123             : #endif
    1124         620 :                         tds_put_n(tds, s, colsize);
    1125             :                 }
    1126             :         }
    1127       10154 :         if (converted > 0)
    1128        2258 :                 tds_convert_string_free((char*)src, s);
    1129             :         return TDS_SUCCESS;
    1130             : }
    1131             : 
    1132             : TDSRET
    1133        1176 : tds_numeric_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
    1134             : {
    1135        1176 :         col->column_size = tds_get_byte(tds);
    1136        1176 :         col->column_prec = tds_get_byte(tds);        /* precision */
    1137        1176 :         col->column_scale = tds_get_byte(tds);       /* scale */
    1138             : 
    1139             :         /* check prec/scale, don't let server crash us */
    1140        1176 :         if (col->column_prec < 1 || col->column_prec > MAXPRECISION
    1141        1176 :             || col->column_scale > col->column_prec)
    1142             :                 return TDS_FAIL;
    1143             : 
    1144        1176 :         return TDS_SUCCESS;
    1145             : }
    1146             : 
    1147             : TDS_INT
    1148        2760 : tds_numeric_row_len(TDSCOLUMN *col)
    1149             : {
    1150        2760 :         return sizeof(TDS_NUMERIC);
    1151             : }
    1152             : 
    1153             : TDSRET
    1154        1476 : tds_numeric_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
    1155             : {
    1156             :         int colsize;
    1157             :         TDS_NUMERIC *num;
    1158             : 
    1159        1476 :         CHECK_TDS_EXTRA(tds);
    1160        1476 :         CHECK_COLUMN_EXTRA(curcol);
    1161             : 
    1162        1476 :         colsize = tds_get_byte(tds);
    1163             : 
    1164             :         /* set NULL flag in the row buffer */
    1165        1476 :         if (colsize <= 0) {
    1166         334 :                 curcol->column_cur_size = -1;
    1167         334 :                 return TDS_SUCCESS;
    1168             :         }
    1169             : 
    1170             :         /* 
    1171             :          * Since these can be passed around independent
    1172             :          * of the original column they came from, we embed the TDS_NUMERIC datatype in the row buffer
    1173             :          * instead of using the wire representation, even though it uses a few more bytes.  
    1174             :          */
    1175        1142 :         num = (TDS_NUMERIC *) curcol->column_data;
    1176        1142 :         memset(num, '\0', sizeof(TDS_NUMERIC));
    1177             :         /* TODO perhaps it would be fine to change format ?? */
    1178        1142 :         num->precision = curcol->column_prec;
    1179        1142 :         num->scale = curcol->column_scale;
    1180             : 
    1181             :         /* server is going to crash freetds ?? */
    1182             :         /* TODO close connection it server try to do so ?? */
    1183        1142 :         if (colsize > sizeof(num->array))
    1184             :                 return TDS_FAIL;
    1185        1142 :         tds_get_n(tds, num->array, colsize);
    1186             : 
    1187        1142 :         if (IS_TDS7_PLUS(tds->conn))
    1188             :                 tds_swap_numeric(num);
    1189             : 
    1190             :         /* corrected colsize for column_cur_size */
    1191        1142 :         curcol->column_cur_size = sizeof(TDS_NUMERIC);
    1192             : 
    1193        1142 :         return TDS_SUCCESS;
    1194             : }
    1195             : 
    1196             : TDSRET
    1197         538 : tds_numeric_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1198             : {
    1199         538 :         CHECK_TDS_EXTRA(tds);
    1200         538 :         CHECK_COLUMN_EXTRA(col);
    1201             : 
    1202             : #if 1
    1203         538 :         tds_put_byte(tds, tds_numeric_bytes_per_prec[col->column_prec]);
    1204         538 :         tds_put_byte(tds, col->column_prec);
    1205         538 :         tds_put_byte(tds, col->column_scale);
    1206             : #else
    1207             :         TDS_NUMERIC *num = (TDS_NUMERIC *) col->column_data;
    1208             :         tds_put_byte(tds, tds_numeric_bytes_per_prec[num->precision]);
    1209             :         tds_put_byte(tds, num->precision);
    1210             :         tds_put_byte(tds, num->scale);
    1211             : #endif
    1212             : 
    1213         538 :         return TDS_SUCCESS;
    1214             : }
    1215             : 
    1216             : TDSRET
    1217        1462 : tds_numeric_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
    1218             : {
    1219        1462 :         TDS_NUMERIC *num = (TDS_NUMERIC *) col->column_data, buf;
    1220             :         unsigned char colsize;
    1221             : 
    1222        1462 :         if (col->column_cur_size < 0) {
    1223         606 :                 tds_put_byte(tds, 0);
    1224         606 :                 return TDS_SUCCESS;
    1225             :         }
    1226         856 :         colsize = tds_numeric_bytes_per_prec[num->precision];
    1227         856 :         tds_put_byte(tds, colsize);
    1228             : 
    1229         856 :         buf = *num;
    1230         856 :         if (IS_TDS7_PLUS(tds->conn))
    1231             :                 tds_swap_numeric(&buf);
    1232         856 :         tds_put_n(tds, buf.array, colsize);
    1233         856 :         return TDS_SUCCESS;
    1234             : }
    1235             : 
    1236             : TDSRET
    1237           0 : tds_variant_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1238             : {
    1239             :         /* TODO */
    1240           0 :         return TDS_FAIL;
    1241             : }
    1242             : 
    1243             : TDSRET
    1244           0 : tds_variant_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
    1245             : {
    1246             :         /* TODO */
    1247           0 :         return TDS_FAIL;
    1248             : }
    1249             : 
    1250             : TDSRET
    1251         130 : tds_msdatetime_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1252             : {
    1253         130 :         col->column_scale = col->column_prec = 0;
    1254         130 :         if (col->column_type != SYBMSDATE) {
    1255          94 :                 col->column_scale = col->column_prec = tds_get_byte(tds);
    1256          94 :                 if (col->column_prec > 7)
    1257             :                         return TDS_FAIL;
    1258             :         }
    1259         130 :         col->on_server.column_size = col->column_size = sizeof(TDS_DATETIMEALL);
    1260         130 :         return TDS_SUCCESS;
    1261             : }
    1262             : 
    1263             : TDS_INT
    1264         896 : tds_msdatetime_row_len(TDSCOLUMN *col)
    1265             : {
    1266         896 :         return sizeof(TDS_DATETIMEALL);
    1267             : }
    1268             : 
    1269             : TDSRET
    1270         130 : tds_msdatetime_get(TDSSOCKET * tds, TDSCOLUMN * col)
    1271             : {
    1272         130 :         TDS_DATETIMEALL *dt = (TDS_DATETIMEALL*) col->column_data;
    1273         130 :         int size = tds_get_byte(tds);
    1274             : 
    1275         130 :         if (size == 0) {
    1276           0 :                 col->column_cur_size = -1;
    1277           0 :                 return TDS_SUCCESS;
    1278             :         }
    1279             : 
    1280         130 :         memset(dt, 0, sizeof(*dt));
    1281             : 
    1282         130 :         if (col->column_type == SYBMSDATETIMEOFFSET)
    1283          10 :                 size -= 2;
    1284         130 :         if (col->column_type != SYBMSTIME)
    1285          92 :                 size -= 3;
    1286         130 :         if (size < 0)
    1287             :                 return TDS_FAIL;
    1288             : 
    1289         130 :         dt->time_prec = col->column_prec;
    1290             : 
    1291             :         /* get time part */
    1292         130 :         if (col->column_type != SYBMSDATE) {
    1293             :                 TDS_UINT8 u8;
    1294             :                 int i;
    1295             : 
    1296          94 :                 if (size < 3 || size > 5)
    1297           0 :                         return TDS_FAIL;
    1298          94 :                 u8 = 0;
    1299          94 :                 tds_get_n(tds, &u8, size);
    1300             : #ifdef WORDS_BIGENDIAN
    1301             :                 tds_swap_bytes(&u8, 8);
    1302             : #endif
    1303         216 :                 for (i = col->column_prec; i < 7; ++i)
    1304         122 :                         u8 *= 10;
    1305          94 :                 dt->time = u8;
    1306          94 :                 dt->has_time = 1;
    1307          36 :         } else if (size != 0)
    1308             :                 return TDS_FAIL;
    1309             : 
    1310             :         /* get date part */
    1311         130 :         if (col->column_type != SYBMSTIME) {
    1312             :                 TDS_UINT ui;
    1313             : 
    1314          92 :                 ui = 0;
    1315          92 :                 tds_get_n(tds, &ui, 3);
    1316             : #ifdef WORDS_BIGENDIAN
    1317             :                 tds_swap_bytes(&ui, 4);
    1318             : #endif
    1319          92 :                 dt->has_date = 1;
    1320          92 :                 dt->date = ui - 693595;
    1321             :         }
    1322             : 
    1323             :         /* get time offset */
    1324         130 :         if (col->column_type == SYBMSDATETIMEOFFSET) {
    1325          10 :                 dt->offset = tds_get_smallint(tds);
    1326          10 :                 if (dt->offset > 840 || dt->offset < -840)
    1327             :                         return TDS_FAIL;
    1328          10 :                 dt->has_offset = 1;
    1329             :         }
    1330         130 :         col->column_cur_size = sizeof(TDS_DATETIMEALL);
    1331         130 :         return TDS_SUCCESS;
    1332             : }
    1333             : 
    1334             : TDSRET
    1335         174 : tds_msdatetime_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1336             : {
    1337             :         /* TODO precision */
    1338         174 :         if (col->on_server.column_type != SYBMSDATE)
    1339         162 :                 tds_put_byte(tds, 7);
    1340         174 :         return TDS_SUCCESS;
    1341             : }
    1342             : 
    1343             : TDSRET
    1344         174 : tds_msdatetime_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
    1345             : {
    1346         174 :         const TDS_DATETIMEALL *dta = (const TDS_DATETIMEALL *) col->column_data;
    1347             :         unsigned char buf[12], *p;
    1348             : 
    1349         174 :         if (col->column_cur_size < 0) {
    1350          36 :                 tds_put_byte(tds, 0);
    1351          36 :                 return TDS_SUCCESS;
    1352             :         }
    1353             : 
    1354             :         /* TODO precision */
    1355         138 :         p = buf + 1;
    1356         138 :         if (col->on_server.column_type != SYBMSDATE) {
    1357         126 :                 TDS_PUT_UA4LE(p, (TDS_UINT) dta->time);
    1358         126 :                 p[4] = (unsigned char) (dta->time >> 32);
    1359         126 :                 p += 5;
    1360             :         }
    1361         138 :         if (col->on_server.column_type != SYBMSTIME) {
    1362         126 :                 TDS_UINT ui = dta->date + 693595;
    1363         126 :                 TDS_PUT_UA4LE(p, ui);
    1364         126 :                 p += 3;
    1365             :         }
    1366         138 :         if (col->on_server.column_type == SYBMSDATETIMEOFFSET) {
    1367           0 :                 TDS_PUT_UA2LE(p, dta->offset);
    1368           0 :                 p += 2;
    1369             :         }
    1370         138 :         buf[0] = p - buf - 1;
    1371         138 :         tds_put_n(tds, buf, p - buf);
    1372             : 
    1373         138 :         return TDS_SUCCESS;
    1374             : }
    1375             : 
    1376             : TDSRET
    1377           4 : tds_clrudt_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1378             : {
    1379             :         /* TODO save fields */
    1380             :         /* FIXME support RPC */
    1381             : 
    1382             :         /* MAX_BYTE_SIZE */
    1383           4 :         tds_get_usmallint(tds);
    1384             : 
    1385             :         /* DB_NAME */
    1386           4 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1387             : 
    1388             :         /* SCHEMA_NAME */
    1389           4 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1390             : 
    1391             :         /* TYPE_NAME */
    1392           4 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1393             : 
    1394             :         /* UDT_METADATA */
    1395           4 :         tds_get_string(tds, tds_get_usmallint(tds), NULL, 0);
    1396             : 
    1397           4 :         col->column_size = 0x7ffffffflu;
    1398           4 :         col->column_varint_size = 8;
    1399             : 
    1400           4 :         return TDS_SUCCESS;
    1401             : }
    1402             : 
    1403             : TDS_INT
    1404           8 : tds_clrudt_row_len(TDSCOLUMN *col)
    1405             : {
    1406           8 :         col->column_varint_size = 8;
    1407             :         /* TODO save other fields */
    1408           8 :         return sizeof(TDSBLOB);
    1409             : }
    1410             : 
    1411             : TDSRET
    1412           0 : tds_clrudt_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1413             : {
    1414             :         /* FIXME support properly*/
    1415           0 :         tds_put_byte(tds, 0);   /* db_name */
    1416           0 :         tds_put_byte(tds, 0);   /* schema_name */
    1417           0 :         tds_put_byte(tds, 0);   /* type_name */
    1418             : 
    1419           0 :         return TDS_SUCCESS;
    1420             : }
    1421             : 
    1422             : TDSRET
    1423           0 : tds_sybbigtime_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1424             : {
    1425           0 :         col->column_scale = col->column_prec = 6;
    1426           0 :         tds_get_byte(tds); /* 8, size */
    1427           0 :         tds_get_byte(tds); /* 6, precision ?? */
    1428           0 :         col->on_server.column_size = col->column_size = sizeof(TDS_UINT8);
    1429           0 :         return TDS_SUCCESS;
    1430             : }
    1431             : 
    1432             : TDS_INT
    1433          48 : tds_sybbigtime_row_len(TDSCOLUMN *col)
    1434             : {
    1435          48 :         return sizeof(TDS_UINT8);
    1436             : }
    1437             : 
    1438             : TDSRET
    1439           0 : tds_sybbigtime_get(TDSSOCKET * tds, TDSCOLUMN * col)
    1440             : {
    1441           0 :         TDS_UINT8 *dt = (TDS_UINT8 *) col->column_data;
    1442           0 :         int size = tds_get_byte(tds);
    1443             : 
    1444           0 :         if (size == 0) {
    1445           0 :                 col->column_cur_size = -1;
    1446           0 :                 return TDS_SUCCESS;
    1447             :         }
    1448             : 
    1449           0 :         col->column_cur_size = sizeof(TDS_UINT8);
    1450           0 :         *dt = tds_get_int8(tds);
    1451             : 
    1452           0 :         return TDS_SUCCESS;
    1453             : }
    1454             : 
    1455             : TDSRET
    1456           0 : tds_sybbigtime_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1457             : {
    1458           0 :         tds_put_byte(tds, 8);
    1459           0 :         tds_put_byte(tds, 6);
    1460           0 :         return TDS_SUCCESS;
    1461             : }
    1462             : 
    1463             : TDSRET
    1464           0 : tds_sybbigtime_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
    1465             : {
    1466           0 :         const TDS_UINT8 *dt = (const TDS_UINT8 *) col->column_data;
    1467             : 
    1468           0 :         if (col->column_cur_size < 0) {
    1469           0 :                 tds_put_byte(tds, 0);
    1470           0 :                 return TDS_SUCCESS;
    1471             :         }
    1472             : 
    1473           0 :         tds_put_byte(tds, 8);
    1474           0 :         tds_put_int8(tds, *dt);
    1475             : 
    1476           0 :         return TDS_SUCCESS;
    1477             : }
    1478             : 
    1479             : TDSRET
    1480           0 : tds_mstabletype_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
    1481             : {
    1482             :         /* Table type is strictly only an input variable */
    1483           0 :         return TDS_FAIL;
    1484             : }
    1485             : 
    1486             : TDS_INT
    1487           4 : tds_mstabletype_row_len(TDSCOLUMN *col)
    1488             : {
    1489           4 :         return sizeof(TDS_TVP);
    1490             : }
    1491             : 
    1492             : TDSRET
    1493           0 : tds_mstabletype_get(TDSSOCKET *tds, TDSCOLUMN *col)
    1494             : {
    1495             :         /* Table type is strictly only an input variable */
    1496           0 :         return TDS_FAIL;
    1497             : }
    1498             : 
    1499             : TDSRET
    1500           4 : tds_mstabletype_put_info(TDSSOCKET *tds, TDSCOLUMN *col)
    1501             : {
    1502           4 :         TDS_TVP *table = (TDS_TVP *) col->column_data;
    1503             :         TDSFREEZE current_freeze[1];
    1504             :         size_t written;
    1505             : 
    1506             :         /* TVP_TYPENAME */
    1507           4 :         tds_put_byte(tds, 0); /* Empty DB name */
    1508             : 
    1509           4 :         tds_freeze(tds, current_freeze, 1);
    1510           4 :         tds_put_string(tds, table->schema, -1);
    1511           4 :         written = tds_freeze_written(current_freeze) / 2;
    1512           4 :         tds_freeze_close_len(current_freeze, written);
    1513             : 
    1514           4 :         tds_freeze(tds, current_freeze, 1);
    1515           4 :         tds_put_string(tds, table->name, -1);
    1516           4 :         written = tds_freeze_written(current_freeze) / 2;
    1517           4 :         tds_freeze_close_len(current_freeze, written);
    1518             : 
    1519           4 :         return TDS_SUCCESS;
    1520             : }
    1521             : 
    1522             : TDSRET
    1523           4 : tds_mstabletype_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
    1524             : {
    1525           4 :         TDS_TVP *table = (TDS_TVP *) col->column_data;
    1526             :         TDSPARAMINFO *params;
    1527             :         TDSCOLUMN *tds_col;
    1528             :         TDS_TVP_ROW *row;
    1529             :         TDSRET ret;
    1530             :         int i;
    1531           4 :         TDS_USMALLINT num_cols = table->metadata ? table->metadata->num_cols : 0;
    1532             : 
    1533             :         /* COL_METADATA */
    1534           4 :         if (num_cols == 0)
    1535           0 :                 tds_put_smallint(tds, 0xffff); /* TVP_NULL_TOKEN */
    1536             :         else {
    1537           4 :                 tds_put_smallint(tds, num_cols);
    1538             : 
    1539           4 :                 params = table->metadata;
    1540          12 :                 for (i = 0; i < num_cols; i++) {
    1541           8 :                         tds_col = params->columns[i];
    1542             : 
    1543             :                         /* UserType*/
    1544           8 :                         tds_put_int(tds, tds_col->column_usertype);
    1545             :                         /* Flags */
    1546           8 :                         tds_put_smallint(tds, tds_col->column_flags);
    1547             :                         /* TYPE_INFO */
    1548           8 :                         tds_put_byte(tds, tds_col->on_server.column_type);
    1549           8 :                         ret = tds_col->funcs->put_info(tds, tds_col);
    1550           8 :                         if (TDS_FAILED(ret))
    1551             :                                 return ret;
    1552             : 
    1553             :                         /* ColName - Empty string */
    1554           8 :                         tds_put_byte(tds, 0x00);
    1555             :                 }
    1556             :         }
    1557             : 
    1558             :         /* TVP_END_TOKEN */
    1559           4 :         tds_put_byte(tds, 0x00);
    1560             : 
    1561          22 :         for (row = table->row; row != NULL; row = row->next) {
    1562             :                 /* TVP_ROW_TOKEN */
    1563          18 :                 tds_put_byte(tds, 0x01);
    1564             : 
    1565          18 :                 params = row->params;
    1566          54 :                 for (i = 0; i < num_cols; i++) {
    1567          36 :                         tds_col = params->columns[i];
    1568          36 :                         ret = tds_col->funcs->put_data(tds, tds_col, 0);
    1569          36 :                         if (TDS_FAILED(ret))
    1570             :                                 return ret;
    1571             :                 }
    1572             :         }
    1573             : 
    1574             :         /* TVP_END_TOKEN */
    1575           4 :         tds_put_byte(tds, 0x00);
    1576             : 
    1577           4 :         return TDS_SUCCESS;
    1578             : }
    1579             : 
    1580             : TDSRET
    1581           0 : tds_invalid_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1582             : {
    1583           0 :         return TDS_FAIL;
    1584             : }
    1585             : 
    1586             : TDS_INT
    1587           0 : tds_invalid_row_len(TDSCOLUMN *col)
    1588             : {
    1589           0 :         return 0;
    1590             : }
    1591             : 
    1592             : TDSRET
    1593           0 : tds_invalid_get(TDSSOCKET * tds, TDSCOLUMN * col)
    1594             : {
    1595           0 :         return TDS_FAIL;
    1596             : }
    1597             : 
    1598             : TDSRET
    1599           0 : tds_invalid_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1600             : {
    1601           0 :         return TDS_FAIL;
    1602             : }
    1603             : 
    1604             : TDSRET
    1605           0 : tds_invalid_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
    1606             : {
    1607           0 :         return TDS_FAIL;
    1608             : }
    1609             : 
    1610             : #if ENABLE_EXTRA_CHECKS
    1611             : int
    1612     9740733 : tds_generic_check(const TDSCOLUMN *col)
    1613             : {
    1614     9740733 :         return 0;
    1615             : }
    1616             : 
    1617             : int
    1618         112 : tds_sybbigtime_check(const TDSCOLUMN *col)
    1619             : {
    1620         112 :         assert(col->column_type == col->on_server.column_type);
    1621         112 :         assert(col->on_server.column_size == col->column_size);
    1622         112 :         assert(!is_numeric_type(col->column_type));
    1623         112 :         assert(!is_fixed_type(col->column_type));
    1624         112 :         assert(!is_blob_type(col->column_type));
    1625         112 :         assert(!is_variable_type(col->column_type));
    1626         112 :         assert(is_nullable_type(col->column_type));
    1627         112 :         assert(col->column_varint_size == 1);
    1628         112 :         assert(col->column_prec == 6);
    1629         112 :         assert(col->column_scale == col->column_prec);
    1630             : 
    1631         112 :         return 1;
    1632             : }
    1633             : 
    1634             : int
    1635          12 : tds_mstabletype_check(const TDSCOLUMN *col)
    1636             : {
    1637          12 :         return 0;
    1638             : }
    1639             : 
    1640             : int
    1641          84 : tds_clrudt_check(const TDSCOLUMN *col)
    1642             : {
    1643          84 :         return 0;
    1644             : }
    1645             : 
    1646             : int
    1647        3726 : tds_msdatetime_check(const TDSCOLUMN *col)
    1648             : {
    1649        3726 :         assert(col->column_type == col->on_server.column_type);
    1650        3726 :         assert(col->on_server.column_size == col->column_size);
    1651        3726 :         assert(!is_numeric_type(col->column_type));
    1652        3726 :         if (col->column_type == SYBMSDATE) {
    1653        1076 :                 assert(is_fixed_type(col->column_type));
    1654             :         } else {
    1655        2650 :                 assert(!is_fixed_type(col->column_type));
    1656             :         }
    1657        3726 :         assert(!is_blob_type(col->column_type));
    1658        3726 :         assert(!is_variable_type(col->column_type));
    1659        3726 :         assert(is_nullable_type(col->column_type));
    1660        3726 :         assert(col->column_varint_size == 1);
    1661        3726 :         assert(col->column_prec >= 0 && col->column_prec <= 7);
    1662        3726 :         assert(col->column_scale == col->column_prec);
    1663             : 
    1664        3726 :         return 1;
    1665             : }
    1666             : 
    1667             : int
    1668        7226 : tds_variant_check(const TDSCOLUMN *col)
    1669             : {
    1670        7226 :         return 0;
    1671             : }
    1672             : 
    1673             : int
    1674       64193 : tds_numeric_check(const TDSCOLUMN *col)
    1675             : {
    1676       64193 :         assert(col->column_type == col->on_server.column_type);
    1677       64193 :         assert(col->on_server.column_size == col->column_size);
    1678       64193 :         assert(is_numeric_type(col->column_type));
    1679       64193 :         assert(!is_fixed_type(col->column_type));
    1680       64193 :         assert(!is_blob_type(col->column_type));
    1681       64193 :         assert(!is_variable_type(col->column_type));
    1682       64193 :         assert(col->column_varint_size == 1);
    1683       64193 :         assert(col->column_prec >= 1 && col->column_prec <= MAXPRECISION);
    1684       64193 :         assert(col->column_scale <= col->column_prec);
    1685             : 
    1686       64193 :         return 1;
    1687             : }
    1688             : 
    1689             : int
    1690           0 : tds_invalid_check(const TDSCOLUMN *col)
    1691             : {
    1692           0 :         return 1;
    1693             : }
    1694             : #endif
    1695             : 
    1696             : 
    1697             : #define TDS_DECLARE_FUNCS(name) \
    1698             :      extern const TDSCOLUMNFUNCS tds_ ## name ## _funcs
    1699             : 
    1700             : #include <freetds/pushvis.h>
    1701             : TDS_DECLARE_FUNCS(generic);
    1702             : TDS_DECLARE_FUNCS(numeric);
    1703             : TDS_DECLARE_FUNCS(variant);
    1704             : TDS_DECLARE_FUNCS(msdatetime);
    1705             : TDS_DECLARE_FUNCS(clrudt);
    1706             : TDS_DECLARE_FUNCS(sybbigtime);
    1707             : TDS_DECLARE_FUNCS(invalid);
    1708             : TDS_DECLARE_FUNCS(mstabletype);
    1709             : #include <freetds/popvis.h>
    1710             : 
    1711             : static const TDSCOLUMNFUNCS *
    1712       73316 : tds_get_column_funcs(TDSCONNECTION *conn, int type)
    1713             : {
    1714       73316 :         switch (type) {
    1715             :         case SYBNUMERIC:
    1716             :         case SYBDECIMAL:
    1717             :                 return &tds_numeric_funcs;
    1718           4 :         case SYBMSUDT:
    1719           4 :                 return &tds_clrudt_funcs;
    1720        1270 :         case SYBVARIANT:
    1721        1270 :                 if (IS_TDS7_PLUS(conn))
    1722             :                         return &tds_variant_funcs;
    1723             :                 break;
    1724        1786 :         case SYBMSDATE:
    1725             :         case SYBMSTIME:
    1726             :         case SYBMSDATETIME2:
    1727             :         case SYBMSDATETIMEOFFSET:
    1728        1786 :                 return &tds_msdatetime_funcs;
    1729          48 :         case SYB5BIGTIME:
    1730             :         case SYB5BIGDATETIME:
    1731          48 :                 return &tds_sybbigtime_funcs;
    1732           4 :         case SYBMSTABLE:
    1733           4 :                 return &tds_mstabletype_funcs;
    1734             :         }
    1735       68024 :         return &tds_generic_funcs;
    1736             : }
    1737             : #include "tds_types.h"
    1738             : 
    1739             : #ifdef WORDS_BIGENDIAN
    1740             : void
    1741             : tds_swap_datatype(int coltype, void *b)
    1742             : {
    1743             :         unsigned char *buf = (unsigned char *) b;
    1744             : 
    1745             :         switch (coltype) {
    1746             :         case SYBDATETIME4:
    1747             :                 tds_swap_bytes(&buf[2], 2);
    1748             :         case SYBINT2:
    1749             :         case SYBUINT2:
    1750             :                 tds_swap_bytes(buf, 2);
    1751             :                 break;
    1752             :         case SYBMONEY:
    1753             :         case SYBDATETIME:
    1754             :                 tds_swap_bytes(&buf[4], 4);
    1755             :         case SYBINT4:
    1756             :         case SYBUINT4:
    1757             :         case SYBMONEY4:
    1758             :         case SYBREAL:
    1759             :         case SYBDATE:
    1760             :         case SYBTIME:
    1761             :                 tds_swap_bytes(buf, 4);
    1762             :                 break;
    1763             :         case SYBINT8:
    1764             :         case SYBUINT8:
    1765             :         case SYBFLT8:
    1766             :         case SYB5BIGTIME:
    1767             :         case SYB5BIGDATETIME:
    1768             :                 tds_swap_bytes(buf, 8);
    1769             :                 break;
    1770             :         case SYBUNIQUE:
    1771             :                 tds_swap_bytes(buf, 4);
    1772             :                 tds_swap_bytes(&buf[4], 2);
    1773             :                 tds_swap_bytes(&buf[6], 2);
    1774             :                 break;
    1775             :         }
    1776             : }
    1777             : #endif
    1778             : 
    1779             : /**
    1780             :  * Converts numeric from Microsoft representation to internal one (Sybase).
    1781             :  * \param num numeric data to convert
    1782             :  */
    1783             : static void
    1784             : tds_swap_numeric(TDS_NUMERIC *num)
    1785             : {
    1786             :         /* swap the sign */
    1787        1706 :         num->array[0] = (num->array[0] == 0) ? 1 : 0;
    1788             :         /* swap the data */
    1789        1706 :         tds_swap_bytes(&(num->array[1]), tds_numeric_bytes_per_prec[num->precision] - 1);
    1790             : }
    1791             : 

Generated by: LCOV version 1.13