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

Generated by: LCOV version 1.13