LCOV - code coverage report
Current view: top level - src/tds - data.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 640 741 86.4 %
Date: 2026-03-24 22:22:09 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/tds/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/tds/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/tds/iconv.h>
     203             : #include <freetds/tds/checks.h>
     204             : #include <freetds/tds/stream.h>
     205             : #include <freetds/tds/data.h>
     206             : 
     207             : #define USE_ICONV_IN (tds->conn->use_iconv_in)
     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       95745 : tds_set_column_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
     220             : {
     221             :         /* set type */
     222       95745 :         curcol->on_server.column_type = type;
     223       95745 :         curcol->funcs = tds_get_column_funcs(conn, type);
     224       95745 :         curcol->column_type = tds_get_cardinal_type(type, curcol->column_usertype);
     225             : 
     226             :         /* set size */
     227       95745 :         curcol->column_cur_size = -1;
     228       95745 :         curcol->column_varint_size = tds_get_varint_size(conn, type);
     229       95745 :         if (curcol->column_varint_size == 0)
     230       26194 :                 curcol->column_cur_size = curcol->on_server.column_size = curcol->column_size = tds_get_size_by_type(type);
     231             : 
     232       95745 : }
     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       21774 : tds_set_param_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
     242             : {
     243       21774 :         if (IS_TDS7_PLUS(conn)) {
     244       18882 :                 switch (type) {
     245        2676 :                 case SYBVARCHAR:
     246        2676 :                         type = XSYBVARCHAR;
     247        2676 :                         break;
     248        1110 :                 case SYBCHAR:
     249        1110 :                         type = XSYBCHAR;
     250        1110 :                         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        2892 :         } else if (IS_TDS50(conn)) {
     265        2892 :                 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       21774 :         tds_set_column_type(conn, curcol, type);
     275             : 
     276       21774 :         if (is_collate_type(type) || is_char_type(type)) {
     277        7722 :                 curcol->char_conv = conn->char_convs[is_unicode_type(type) ? client2ucs2 : client2server_chardata];
     278        7722 :                 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       21774 :         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        4890 :         case SYBINT1:
     292             :         case SYBINT2:
     293             :         case SYBINT4:
     294             :         case SYBINT8:
     295        4890 :                 curcol->on_server.column_type = SYBINTN;
     296        4890 :                 curcol->column_varint_size = 1;
     297        4890 :                 curcol->column_cur_size = -1;
     298        4890 :                 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       21774 : }
     344             : 
     345             : TDS_SERVER_TYPE
     346    13375071 : tds_get_cardinal_type(TDS_SERVER_TYPE datatype, int usertype)
     347             : {
     348    13375071 :         switch (datatype) {
     349             :         case XSYBVARBINARY:
     350             :                 return SYBVARBINARY;
     351        7248 :         case XSYBBINARY:
     352        7248 :                 return SYBBINARY;
     353       29910 :         case SYBNTEXT:
     354       29910 :                 return SYBTEXT;
     355     3188468 :         case XSYBNVARCHAR:
     356             :         case XSYBVARCHAR:
     357     3188468 :                 return SYBVARCHAR;
     358     1460227 :         case XSYBNCHAR:
     359             :         case XSYBCHAR:
     360     1460227 :                 return SYBCHAR;
     361         968 :         case SYB5INT8:
     362         968 :                 return SYBINT8;
     363       19342 :         case SYBLONGBINARY:
     364       19342 :                 switch (usertype) {
     365             :                 case USER_UNICHAR_TYPE:
     366             :                 case USER_UNIVARCHAR_TYPE:
     367             :                         return SYBTEXT;
     368             :                 }
     369             :                 break;
     370         932 :         case SYBMSXML:
     371         932 :                 return SYBLONGCHAR;
     372             :         default:
     373             :                 break;
     374             :         }
     375     8654604 :         return datatype;
     376             : }
     377             : 
     378             : TDSRET
     379       68205 : tds_generic_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
     380             : {
     381       68205 :         switch (col->column_varint_size) {
     382          36 :         case 8:
     383          36 :                 col->column_size = 0x7ffffffflu;
     384          36 :                 break;
     385        3462 :         case 5:
     386             :         case 4:
     387        3462 :                 col->column_size = tds_get_int(tds);
     388        3462 :                 if (col->column_size < 0)
     389             :                         return TDS_FAIL;
     390             :                 break;
     391       21063 :         case 2:
     392             :                 /* assure > 0 */
     393       21063 :                 col->column_size = tds_get_smallint(tds);
     394             :                 /* under TDS7.2 this means ?var???(MAX) */
     395       21063 :                 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       21063 :                 if (col->column_size < 0)
     404             :                         return TDS_FAIL;
     405             :                 break;
     406       26822 :         case 1:
     407       26822 :                 col->column_size = tds_get_byte(tds);
     408       26822 :                 break;
     409       16822 :         case 0:
     410       16822 :                 col->column_size = tds_get_size_by_type(col->column_type);
     411       16822 :                 break;
     412             :         }
     413             : 
     414       68205 :         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       22139 :                 tds_get_n(tds, col->column_collation, 5);
     422       22139 :                 col->char_conv =
     423       22139 :                         tds_iconv_from_collate(tds->conn, col->column_collation);
     424             :         }
     425             : 
     426             :         /* Only read table_name for blob columns (eg. not for SYBLONGBINARY) */
     427       68205 :         if (type_has_textptr(col->on_server.column_type)) {
     428             :                 /* discard this additional byte */
     429        2518 :                 if (IS_TDS72_PLUS(tds->conn)) {
     430        1012 :                         unsigned char num_parts = tds_get_byte(tds);
     431             :                         /* TODO do not discard first ones */
     432        2024 :                         for (; num_parts; --num_parts) {
     433        1012 :                                 tds_dstr_get(tds, &col->table_name, tds_get_usmallint(tds));
     434             :                         }
     435             :                 } else {
     436        1506 :                         tds_dstr_get(tds, &col->table_name, tds_get_usmallint(tds));
     437             :                 }
     438       65687 :         } else if (IS_TDS72_PLUS(tds->conn) && col->on_server.column_type == SYBMSXML) {
     439          36 :                 unsigned char has_schema = tds_get_byte(tds);
     440          36 :                 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      147054 : tds_generic_row_len(TDSCOLUMN *col)
     455             : {
     456      147054 :         CHECK_COLUMN_EXTRA(col);
     457             : 
     458      147054 :         if (is_blob_col(col))
     459             :                 return sizeof(TDSBLOB);
     460      137472 :         return col->column_size;
     461             : }
     462             : 
     463             : static TDSRET
     464        3380 : 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        3380 :         TDS_PROPAGATE(tds_dynamic_stream_init(&w, pp, allocated));
     474             : 
     475        3380 :         if (USE_ICONV_IN && curcol->char_conv)
     476         996 :                 res = tds_convert_stream(tds, curcol->char_conv, to_client, r_stream, &w.stream);
     477             :         else
     478        2384 :                 res = tds_copy_stream(r_stream, &w.stream);
     479        3380 :         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         368 : tds_varmax_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
     491             : {
     492         368 :         TDSVARMAXSTREAM *s = (TDSVARMAXSTREAM *) stream;
     493             : 
     494             :         /* read chunk len if needed */
     495         368 :         if (s->chunk_left == 0) {
     496         368 :                 TDS_INT l = tds_get_int(s->tds);
     497         368 :                 if (l <= 0) l = -1;
     498         368 :                 s->chunk_left = l;
     499             :         }
     500             : 
     501             :         /* no more data ?? */
     502         368 :         if (s->chunk_left < 0)
     503             :                 return 0;
     504             : 
     505             :         /* read part of data */
     506         168 :         if (len > s->chunk_left)
     507         168 :                 len = s->chunk_left;
     508         168 :         s->chunk_left -= (TDS_INT) len;
     509         168 :         if (tds_get_n(s->tds, ptr, len))
     510         168 :                 return len;
     511             :         return -1;
     512             : }
     513             : 
     514             : static TDSRET
     515         200 : tds72_get_varmax(TDSSOCKET * tds, TDSCOLUMN * curcol)
     516             : {
     517             :         TDS_INT8 len;
     518             :         TDSVARMAXSTREAM r;
     519         200 :         size_t allocated = 0;
     520         200 :         void **pp = (void**) &(((TDSBLOB*) curcol->column_data)->textvalue);
     521             : 
     522         200 :         len = tds_get_int8(tds);
     523             : 
     524             :         /* NULL */
     525         200 :         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         200 :         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         200 :         r.stream.read = tds_varmax_stream_read;
     541         200 :         r.tds = tds;
     542         200 :         r.chunk_left = 0;
     543             : 
     544         200 :         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_IN && 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     1264442 : tds_generic_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
     704             : {
     705             :         unsigned char *dest;
     706             :         int len, colsize;
     707             :         int fillchar;
     708     1264442 :         TDSBLOB *blob = NULL;
     709             : 
     710     1264442 :         CHECK_TDS_EXTRA(tds);
     711     1264442 :         CHECK_COLUMN_EXTRA(curcol);
     712             : 
     713     1264442 :         tdsdump_log(TDS_DBG_INFO1, "tds_get_data: type %d, varint size %d\n", curcol->column_type, curcol->column_varint_size);
     714     1264442 :         switch (curcol->column_varint_size) {
     715        2740 :         case 5:
     716             :                 /* It's a BLOB... */
     717        2740 :                 len = tds_get_byte(tds);
     718        2740 :                 blob = (TDSBLOB *) curcol->column_data;
     719        2740 :                 if (len == 16) {        /*  Jeff's hack */
     720        2428 :                         tds_get_n(tds, blob->textptr, 16);
     721        2428 :                         tds_get_n(tds, blob->timestamp, 8);
     722        2428 :                         blob->valid_ptr = true;
     723        3428 :                         if (IS_TDS72_PLUS(tds->conn) &&
     724        1000 :                             memcmp(blob->textptr, "dummy textptr\0\0",16) == 0)
     725        1000 :                                 blob->valid_ptr = false;
     726        2428 :                         colsize = tds_get_int(tds);
     727             :                 } else {
     728             :                         colsize = -1;
     729             :                 }
     730             :                 break;
     731         794 :         case 4:
     732         794 :                 colsize = tds_get_int(tds);
     733         794 :                 if (colsize == 0)
     734           0 :                         colsize = -1;
     735             :                 break;
     736         200 :         case 8:
     737         200 :                 return tds72_get_varmax(tds, curcol);
     738      489534 :         case 2:
     739      489534 :                 colsize = tds_get_smallint(tds);
     740      489534 :                 break;
     741      441107 :         case 1:
     742      441107 :                 colsize = tds_get_byte(tds);
     743      441107 :                 if (colsize == 0)
     744        7840 :                         colsize = -1;
     745             :                 break;
     746      330067 :         case 0:
     747             :                 /* TODO this should be column_size */
     748      330067 :                 colsize = tds_get_size_by_type(curcol->column_type);
     749             :                 break;
     750             :         default:
     751             :                 colsize = -1;
     752             :                 break;
     753             :         }
     754     1264242 :         if (IS_TDSDEAD(tds))
     755             :                 return TDS_FAIL;
     756             : 
     757     1264242 :         tdsdump_log(TDS_DBG_INFO1, "tds_get_data(): wire column size is %d\n", colsize);
     758             :         /* set NULL flag in the row buffer */
     759     1264242 :         if (colsize < 0) {
     760       11097 :                 curcol->column_cur_size = -1;
     761       11097 :                 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     1253145 :         dest = curcol->column_data;
     774     1253145 :         if (is_blob_col(curcol)) {
     775             :                 TDSDATAINSTREAM r;
     776             :                 int allocated;
     777             :                 TDSRET ret;
     778             : 
     779        3222 :                 blob = (TDSBLOB *) dest;        /* cf. column_varint_size case 4, above */
     780             : 
     781             :                 /* empty string */
     782        3222 :                 if (colsize == 0) {
     783         146 :                         curcol->column_cur_size = 0;
     784         146 :                         if (blob->textvalue)
     785           2 :                                 TDS_ZERO_FREE(blob->textvalue);
     786             :                         return TDS_SUCCESS;
     787             :                 }
     788             : 
     789        3076 :                 allocated = TDS_MAX(curcol->column_cur_size, 0);
     790        3076 :                 if (colsize > allocated) {
     791        2752 :                         TDS_ZERO_FREE(blob->textvalue);
     792        2752 :                         allocated = colsize;
     793        2752 :                         if (is_unicode_type(curcol->on_server.column_type))
     794         704 :                                 allocated /= 2;
     795             :                 }
     796             : 
     797        3076 :                 tds_datain_stream_init(&r, tds, colsize);
     798        3076 :                 ret = tds_get_char_dynamic(tds, curcol, (void **) &blob->textvalue, allocated, &r.stream);
     799        3076 :                 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     1249923 :         if (USE_ICONV_IN && curcol->char_conv) {
     809      473381 :                 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      776542 :                 int discard_len = 0;
     816      776542 :                 if (colsize > curcol->column_size) {
     817           0 :                         discard_len = colsize - curcol->column_size;
     818           0 :                         colsize = curcol->column_size;
     819             :                 }
     820      776542 :                 if (!tds_get_n(tds, dest, colsize))
     821             :                         return TDS_FAIL;
     822      776542 :                 if (discard_len > 0)
     823           0 :                         tds_get_n(tds, NULL, discard_len);
     824      776542 :                 curcol->column_cur_size = colsize;
     825             :         }
     826             : 
     827             :         /* pad (UNI)CHAR and BINARY types */
     828     1249923 :         fillchar = 0;
     829     1249923 :         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      208848 :                 if (curcol->column_size != curcol->on_server.column_size)
     837             :                         break;
     838             :                 /* FIXME use client charset */
     839             :                 fillchar = ' ';
     840      208120 :         case SYBBINARY:
     841             :         case XSYBBINARY:
     842      208120 :                 if (colsize < curcol->column_size)
     843          90 :                         memset(dest + colsize, fillchar, curcol->column_size - colsize);
     844      208120 :                 colsize = curcol->column_size;
     845      208120 :                 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         850 :         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       11354 : tds_generic_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
     865             : {
     866             :         size_t size;
     867             : 
     868       11354 :         CHECK_TDS_EXTRA(tds);
     869       11354 :         CHECK_COLUMN_EXTRA(col);
     870             : 
     871       11354 :         size = tds_fix_column_size(tds, col);
     872       11354 :         switch (col->column_varint_size) {
     873             :         case 0:
     874             :                 break;
     875        4860 :         case 1:
     876        4860 :                 if (col->column_output && col->column_size <= 0 && is_char_type(col->column_type))
     877           0 :                         size = 255;
     878        4860 :                 TDS_PUT_BYTE(tds, size);
     879        4860 :                 break;
     880        4354 :         case 2:
     881        4354 :                 TDS_PUT_SMALLINT(tds, size);
     882        4354 :                 break;
     883         658 :         case 5:
     884             :         case 4:
     885         658 :                 TDS_PUT_INT(tds, size);
     886         658 :                 break;
     887         632 :         case 8:
     888         632 :                 tds_put_smallint(tds, 0xffff);
     889         632 :                 break;
     890             :         }
     891             : 
     892             :         /* TDS5 wants a table name for LOBs */
     893       11354 :         if (IS_TDS50(tds->conn) && type_has_textptr(col->on_server.column_type))
     894           0 :                 tds_put_smallint(tds, 0);
     895             : 
     896             :         /* TDS7.1 output collate information */
     897       11354 :         if (IS_TDS71_PLUS(tds->conn) && is_collate_type(col->on_server.column_type))
     898        5202 :                 tds_put_n(tds, tds->conn->collation, 5);
     899             : 
     900       11354 :         return TDS_SUCCESS;
     901             : }
     902             : 
     903             : /**
     904             :  * Write data to wire
     905             :  * \param tds state information for the socket and the TDS protocol
     906             :  * \param curcol column where store column information
     907             :  * \return TDS_FAIL on error or TDS_SUCCESS
     908             :  */
     909             : TDSRET
     910       19668 : tds_generic_put(TDSSOCKET * tds, TDSCOLUMN * curcol, bool bcp7)
     911             : {
     912             :         unsigned char *src;
     913       19668 :         TDSBLOB *blob = NULL;
     914             :         size_t colsize, size;
     915             : 
     916             :         const char *s;
     917       19668 :         int converted = 0;
     918             : 
     919       19668 :         CHECK_TDS_EXTRA(tds);
     920       19668 :         CHECK_COLUMN_EXTRA(curcol);
     921             : 
     922       19668 :         tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: colsize = %d\n", (int) curcol->column_cur_size);
     923             : 
     924             :         /* output NULL data */
     925       19668 :         if (curcol->column_cur_size < 0) {
     926        5226 :                 tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: null param\n");
     927        5226 :                 switch (curcol->column_varint_size) {
     928         124 :                 case 5:
     929         124 :                         if ((bcp7 || !IS_TDS7_PLUS(tds->conn)) && type_has_textptr(curcol->on_server.column_type))
     930          52 :                                 tds_put_byte(tds, 0);
     931             :                         else
     932          72 :                                 tds_put_int(tds, -1);
     933             :                         break;
     934           0 :                 case 4:
     935           0 :                         tds_put_int(tds, 0);
     936           0 :                         break;
     937        1432 :                 case 2:
     938        1432 :                         tds_put_smallint(tds, -1);
     939        1432 :                         break;
     940          84 :                 case 8:
     941          84 :                         tds_put_int8(tds, -1);
     942          84 :                         break;
     943        3586 :                 default:
     944        3586 :                         assert(curcol->column_varint_size);
     945             :                         /* FIXME not good for SYBLONGBINARY/SYBLONGCHAR (still not supported) */
     946        3586 :                         tds_put_byte(tds, 0);
     947        3586 :                         break;
     948             :                 }
     949             :                 return TDS_SUCCESS;
     950             :         }
     951       14442 :         colsize = curcol->column_cur_size;
     952             : 
     953       14442 :         size = tds_fix_column_size(tds, curcol);
     954             : 
     955       14442 :         src = curcol->column_data;
     956       14442 :         if (is_blob_col(curcol)) {
     957        1182 :                 blob = (TDSBLOB *) src;
     958        1182 :                 src = (unsigned char *) blob->textvalue;
     959             :         }
     960             : 
     961       14442 :         s = (char *) src;
     962             : 
     963             :         /* convert string if needed */
     964       14442 :         if (curcol->use_iconv_out && curcol->char_conv && curcol->char_conv->flags != TDS_ENCODING_MEMCPY && colsize) {
     965             :                 size_t output_size;
     966             : #if 0
     967             :                 /* TODO this case should be optimized */
     968             :                 /* we know converted bytes */
     969             :                 if (curcol->char_conv->client_charset.min_bytes_per_char == curcol->char_conv->client_charset.max_bytes_per_char 
     970             :                     && curcol->char_conv->server_charset.min_bytes_per_char == curcol->char_conv->server_charset.max_bytes_per_char) {
     971             :                         converted_size = colsize * curcol->char_conv->server_charset.min_bytes_per_char / curcol->char_conv->client_charset.min_bytes_per_char;
     972             : 
     973             :                 } else {
     974             :                 }
     975             : #endif
     976             :                 /* we need to convert data before */
     977             :                 /* TODO this can be a waste of memory... */
     978        3202 :                 converted = 1;
     979        3202 :                 s = tds_convert_string(tds, curcol->char_conv, s, colsize, &output_size);
     980        3202 :                 colsize = (TDS_INT)output_size;
     981        3202 :                 if (!s) {
     982             :                         /* on conversion error put a empty string */
     983             :                         /* TODO on memory failure we should compute converted size and use chunks */
     984           0 :                         colsize = 0;
     985           0 :                         converted = -1;
     986             :                 }
     987             :         }
     988             : 
     989             :         /*
     990             :          * TODO here we limit data sent with TDS_MIN, should mark somewhere
     991             :          * and inform client ??
     992             :          * Test proprietary behavior
     993             :          */
     994       14442 :         if (IS_TDS7_PLUS(tds->conn)) {
     995       13804 :                 tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: not null param varint_size = %d\n",
     996             :                             curcol->column_varint_size);
     997             : 
     998       13804 :                 switch (curcol->column_varint_size) {
     999         572 :                 case 8:
    1000             :                         /* this difference for BCP operation is due to
    1001             :                          * a bug in different server version that does
    1002             :                          * not accept a length here */
    1003         572 :                         tds_put_int8(tds, bcp7 ? (TDS_INT8) -2 : (TDS_INT8) colsize);
    1004         572 :                         tds_put_int(tds, colsize);
    1005         572 :                         break;
    1006         606 :                 case 5: /* It's a BLOB... */
    1007         606 :                         colsize = TDS_MIN(colsize, size);
    1008             :                         /* mssql require only size */
    1009         606 :                         if (bcp7 && type_has_textptr(curcol->on_server.column_type)) {
    1010             :                                 static const unsigned char textptr[] = {
    1011             :                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    1012             :                                         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    1013             :                                 };
    1014          84 :                                 tds_put_byte(tds, 16);
    1015          84 :                                 tds_put_n(tds, textptr, 16);
    1016          84 :                                 tds_put_n(tds, textptr, 8);
    1017             :                         }
    1018         606 :                         TDS_PUT_INT(tds, colsize);
    1019         606 :                         break;
    1020        4858 :                 case 2:
    1021        4858 :                         colsize = TDS_MIN(colsize, size);
    1022        4858 :                         TDS_PUT_SMALLINT(tds, colsize);
    1023        4858 :                         break;
    1024        3582 :                 case 1:
    1025        3582 :                         colsize = TDS_MIN(colsize, size);
    1026        3582 :                         TDS_PUT_BYTE(tds, colsize);
    1027        3582 :                         break;
    1028        4186 :                 case 0:
    1029             :                         /* TODO should be column_size */
    1030        4186 :                         colsize = tds_get_size_by_type(curcol->on_server.column_type);
    1031        4186 :                         break;
    1032             :                 }
    1033             : 
    1034             :                 /* conversion error, exit with an error */
    1035       13804 :                 if (converted < 0)
    1036             :                         return TDS_FAIL;
    1037             : 
    1038             :                 /* put real data */
    1039       13804 :                 if (blob) {
    1040        1178 :                         tds_put_n(tds, s, colsize);
    1041             :                 } else {
    1042             : #ifdef WORDS_BIGENDIAN
    1043             :                         unsigned char buf[64];
    1044             : 
    1045             :                         if (!converted && colsize < 64) {
    1046             :                                 tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n",
    1047             :                                             tds_get_conversion_type(curcol->column_type, colsize));
    1048             :                                 memcpy(buf, s, colsize);
    1049             :                                 tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf);
    1050             :                                 s = (char *) buf;
    1051             :                         }
    1052             : #endif
    1053       12626 :                         tds_put_n(tds, s, colsize);
    1054             :                 }
    1055             :                 /* finish chunk for varchar/varbinary(max) */
    1056       13804 :                 if (curcol->column_varint_size == 8 && colsize)
    1057         552 :                         tds_put_int(tds, 0);
    1058             :         } else {
    1059             :                 /* TODO ICONV handle charset conversions for data */
    1060             :                 /* put size of data */
    1061         638 :                 switch (curcol->column_varint_size) {
    1062           0 :                 case 5: /* It's a BLOB... */
    1063           0 :                         tds_put_byte(tds, 16);
    1064           0 :                         tds_put_n(tds, blob->textptr, 16);
    1065           0 :                         tds_put_n(tds, blob->timestamp, 8);
    1066           0 :                         colsize = TDS_MIN(colsize, 0x7fffffff);
    1067           0 :                         TDS_PUT_INT(tds, colsize);
    1068           0 :                         break;
    1069           4 :                 case 4: /* It's a LONGBINARY */
    1070           4 :                         colsize = TDS_MIN(colsize, 0x7fffffff);
    1071           4 :                         TDS_PUT_INT(tds, colsize);
    1072           4 :                         break;
    1073           0 :                 case 2:
    1074           0 :                         colsize = TDS_MIN(colsize, 8000);
    1075           0 :                         TDS_PUT_SMALLINT(tds, colsize);
    1076           0 :                         break;
    1077         622 :                 case 1:
    1078         622 :                         if (!colsize) {
    1079          10 :                                 tds_put_byte(tds, 1);
    1080          10 :                                 if (is_char_type(curcol->column_type))
    1081          10 :                                         tds_put_byte(tds, ' ');
    1082             :                                 else
    1083           0 :                                         tds_put_byte(tds, 0);
    1084          10 :                                 if (converted > 0)
    1085           0 :                                         tds_convert_string_free((char*)src, s);
    1086             :                                 return TDS_SUCCESS;
    1087             :                         }
    1088         612 :                         colsize = TDS_MIN(colsize, 255);
    1089         612 :                         TDS_PUT_BYTE(tds, colsize);
    1090         612 :                         break;
    1091          12 :                 case 0:
    1092             :                         /* TODO should be column_size */
    1093          12 :                         colsize = tds_get_size_by_type(curcol->column_type);
    1094          12 :                         break;
    1095             :                 }
    1096             : 
    1097             :                 /* conversion error, exit with an error */
    1098         628 :                 if (converted < 0)
    1099             :                         return TDS_FAIL;
    1100             : 
    1101             :                 /* put real data */
    1102         628 :                 if (blob) {
    1103           4 :                         tds_put_n(tds, s, colsize);
    1104             :                 } else {
    1105             : #ifdef WORDS_BIGENDIAN
    1106             :                         unsigned char buf[64];
    1107             : 
    1108             :                         if (!converted && colsize < 64) {
    1109             :                                 tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n",
    1110             :                                             tds_get_conversion_type(curcol->column_type, colsize));
    1111             :                                 memcpy(buf, s, colsize);
    1112             :                                 tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf);
    1113             :                                 s = (char *) buf;
    1114             :                         }
    1115             : #endif
    1116         624 :                         tds_put_n(tds, s, colsize);
    1117             :                 }
    1118             :         }
    1119       14432 :         if (converted > 0)
    1120        3202 :                 tds_convert_string_free((char*)src, s);
    1121             :         return TDS_SUCCESS;
    1122             : }
    1123             : 
    1124             : TDSRET
    1125        1540 : tds_numeric_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
    1126             : {
    1127        1540 :         col->column_size = tds_get_byte(tds);
    1128        1540 :         col->column_prec = tds_get_byte(tds);        /* precision */
    1129        1540 :         col->column_scale = tds_get_byte(tds);       /* scale */
    1130             : 
    1131             :         /* check prec/scale, don't let server crash us */
    1132        1540 :         if (col->column_prec < 1 || col->column_prec > MAXPRECISION
    1133        1540 :             || col->column_scale > col->column_prec)
    1134             :                 return TDS_FAIL;
    1135             : 
    1136        1540 :         return TDS_SUCCESS;
    1137             : }
    1138             : 
    1139             : TDS_INT
    1140        3590 : tds_numeric_row_len(TDSCOLUMN *col TDS_UNUSED)
    1141             : {
    1142        3590 :         return sizeof(TDS_NUMERIC);
    1143             : }
    1144             : 
    1145             : TDSRET
    1146        1814 : tds_numeric_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
    1147             : {
    1148             :         int colsize;
    1149             :         TDS_NUMERIC *num;
    1150             : 
    1151        1814 :         CHECK_TDS_EXTRA(tds);
    1152        1814 :         CHECK_COLUMN_EXTRA(curcol);
    1153             : 
    1154        1814 :         colsize = tds_get_byte(tds);
    1155             : 
    1156             :         /* set NULL flag in the row buffer */
    1157        1814 :         if (colsize <= 0) {
    1158         350 :                 curcol->column_cur_size = -1;
    1159         350 :                 return TDS_SUCCESS;
    1160             :         }
    1161             : 
    1162             :         /* 
    1163             :          * Since these can be passed around independent
    1164             :          * of the original column they came from, we embed the TDS_NUMERIC datatype in the row buffer
    1165             :          * instead of using the wire representation, even though it uses a few more bytes.  
    1166             :          */
    1167        1464 :         num = (TDS_NUMERIC *) curcol->column_data;
    1168        1464 :         memset(num, '\0', sizeof(TDS_NUMERIC));
    1169             :         /* TODO perhaps it would be fine to change format ?? */
    1170        1464 :         num->precision = curcol->column_prec;
    1171        1464 :         num->scale = curcol->column_scale;
    1172             : 
    1173             :         /* server is going to crash freetds ?? */
    1174             :         /* TODO close connection it server try to do so ?? */
    1175        1464 :         if (colsize > sizeof(num->array))
    1176             :                 return TDS_FAIL;
    1177        1464 :         tds_get_n(tds, num->array, colsize);
    1178             : 
    1179        1464 :         if (IS_TDS7_PLUS(tds->conn))
    1180             :                 tds_swap_numeric(num);
    1181             : 
    1182             :         /* corrected colsize for column_cur_size */
    1183        1464 :         curcol->column_cur_size = sizeof(TDS_NUMERIC);
    1184             : 
    1185        1464 :         return TDS_SUCCESS;
    1186             : }
    1187             : 
    1188             : TDSRET
    1189         698 : tds_numeric_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1190             : {
    1191         698 :         CHECK_TDS_EXTRA(tds);
    1192         698 :         CHECK_COLUMN_EXTRA(col);
    1193             : 
    1194             : #if 1
    1195         698 :         tds_put_byte(tds, tds_numeric_bytes_per_prec[col->column_prec]);
    1196         698 :         tds_put_byte(tds, col->column_prec);
    1197         698 :         tds_put_byte(tds, col->column_scale);
    1198             : #else
    1199             :         TDS_NUMERIC *num = (TDS_NUMERIC *) col->column_data;
    1200             :         tds_put_byte(tds, tds_numeric_bytes_per_prec[num->precision]);
    1201             :         tds_put_byte(tds, num->precision);
    1202             :         tds_put_byte(tds, num->scale);
    1203             : #endif
    1204             : 
    1205         698 :         return TDS_SUCCESS;
    1206             : }
    1207             : 
    1208             : TDSRET
    1209        1934 : tds_numeric_put(TDSSOCKET *tds, TDSCOLUMN *col, bool bcp7 TDS_UNUSED)
    1210             : {
    1211        1934 :         TDS_NUMERIC *num = (TDS_NUMERIC *) col->column_data, buf;
    1212             :         unsigned char colsize;
    1213             : 
    1214        1934 :         if (col->column_cur_size < 0) {
    1215         802 :                 tds_put_byte(tds, 0);
    1216         802 :                 return TDS_SUCCESS;
    1217             :         }
    1218        1132 :         colsize = tds_numeric_bytes_per_prec[num->precision];
    1219        1132 :         tds_put_byte(tds, colsize);
    1220             : 
    1221        1132 :         buf = *num;
    1222        1132 :         if (IS_TDS7_PLUS(tds->conn))
    1223             :                 tds_swap_numeric(&buf);
    1224        1132 :         tds_put_n(tds, buf.array, colsize);
    1225        1132 :         return TDS_SUCCESS;
    1226             : }
    1227             : 
    1228             : TDSRET
    1229           0 : tds_variant_put_info(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * col TDS_UNUSED)
    1230             : {
    1231             :         /* TODO */
    1232           0 :         return TDS_FAIL;
    1233             : }
    1234             : 
    1235             : TDSRET
    1236           0 : tds_variant_put(TDSSOCKET *tds TDS_UNUSED, TDSCOLUMN *col TDS_UNUSED, bool bcp7 TDS_UNUSED)
    1237             : {
    1238             :         /* TODO */
    1239           0 :         return TDS_FAIL;
    1240             : }
    1241             : 
    1242             : TDSRET
    1243         280 : tds_msdatetime_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1244             : {
    1245         280 :         col->column_scale = col->column_prec = 0;
    1246         280 :         if (col->column_type != SYBMSDATE) {
    1247         208 :                 col->column_scale = col->column_prec = tds_get_byte(tds);
    1248         208 :                 if (col->column_prec > 7)
    1249             :                         return TDS_FAIL;
    1250             :         }
    1251         280 :         col->on_server.column_size = col->column_size = sizeof(TDS_DATETIMEALL);
    1252         280 :         return TDS_SUCCESS;
    1253             : }
    1254             : 
    1255             : TDS_INT
    1256        1436 : tds_msdatetime_row_len(TDSCOLUMN *col TDS_UNUSED)
    1257             : {
    1258        1436 :         return sizeof(TDS_DATETIMEALL);
    1259             : }
    1260             : 
    1261             : TDSRET
    1262         268 : tds_msdatetime_get(TDSSOCKET * tds, TDSCOLUMN * col)
    1263             : {
    1264         268 :         TDS_DATETIMEALL *dt = (TDS_DATETIMEALL*) col->column_data;
    1265         268 :         int size = tds_get_byte(tds);
    1266             : 
    1267         268 :         if (size == 0) {
    1268           0 :                 col->column_cur_size = -1;
    1269           0 :                 return TDS_SUCCESS;
    1270             :         }
    1271             : 
    1272         268 :         memset(dt, 0, sizeof(*dt));
    1273             : 
    1274         268 :         if (col->column_type == SYBMSDATETIMEOFFSET)
    1275          20 :                 size -= 2;
    1276         268 :         if (col->column_type != SYBMSTIME)
    1277         192 :                 size -= 3;
    1278         268 :         if (size < 0)
    1279             :                 return TDS_FAIL;
    1280             : 
    1281         268 :         dt->time_prec = col->column_prec;
    1282             : 
    1283             :         /* get time part */
    1284         268 :         if (col->column_type != SYBMSDATE) {
    1285             :                 TDS_UINT8 u8;
    1286             :                 int i;
    1287             : 
    1288         196 :                 if (size < 3 || size > 5)
    1289           0 :                         return TDS_FAIL;
    1290         196 :                 u8 = 0;
    1291         196 :                 tds_get_n(tds, &u8, size);
    1292             : #ifdef WORDS_BIGENDIAN
    1293             :                 tds_swap_bytes(&u8, 8);
    1294             : #endif
    1295         440 :                 for (i = col->column_prec; i < 7; ++i)
    1296         244 :                         u8 *= 10;
    1297         196 :                 dt->time = u8;
    1298         196 :                 dt->has_time = 1;
    1299          72 :         } else if (size != 0)
    1300             :                 return TDS_FAIL;
    1301             : 
    1302             :         /* get date part */
    1303         268 :         if (col->column_type != SYBMSTIME) {
    1304             :                 TDS_UINT ui;
    1305             : 
    1306         192 :                 ui = 0;
    1307         192 :                 tds_get_n(tds, &ui, 3);
    1308             : #ifdef WORDS_BIGENDIAN
    1309             :                 tds_swap_bytes(&ui, 4);
    1310             : #endif
    1311         192 :                 dt->has_date = 1;
    1312         192 :                 dt->date = ui - 693595;
    1313             :         }
    1314             : 
    1315             :         /* get time offset */
    1316         268 :         if (col->column_type == SYBMSDATETIMEOFFSET) {
    1317          20 :                 dt->offset = tds_get_smallint(tds);
    1318          20 :                 if (dt->offset > 840 || dt->offset < -840)
    1319             :                         return TDS_FAIL;
    1320          20 :                 dt->has_offset = 1;
    1321             :         }
    1322         268 :         col->column_cur_size = sizeof(TDS_DATETIMEALL);
    1323         268 :         return TDS_SUCCESS;
    1324             : }
    1325             : 
    1326             : TDSRET
    1327         352 : tds_msdatetime_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1328             : {
    1329             :         /* TODO precision */
    1330         352 :         if (col->on_server.column_type != SYBMSDATE)
    1331         328 :                 tds_put_byte(tds, 7);
    1332         352 :         return TDS_SUCCESS;
    1333             : }
    1334             : 
    1335             : TDSRET
    1336         356 : tds_msdatetime_put(TDSSOCKET *tds, TDSCOLUMN *col, bool bcp7 TDS_UNUSED)
    1337             : {
    1338         356 :         const TDS_DATETIMEALL *dta = (const TDS_DATETIMEALL *) col->column_data;
    1339             :         unsigned char buf[12], *p;
    1340             : 
    1341         356 :         if (col->column_cur_size < 0) {
    1342          72 :                 tds_put_byte(tds, 0);
    1343          72 :                 return TDS_SUCCESS;
    1344             :         }
    1345             : 
    1346             :         /* TODO precision */
    1347         284 :         p = buf + 1;
    1348         284 :         if (col->on_server.column_type != SYBMSDATE) {
    1349         260 :                 TDS_PUT_UA4LE(p, (TDS_UINT) dta->time);
    1350         260 :                 p[4] = (unsigned char) (dta->time >> 32);
    1351         260 :                 p += 5;
    1352             :         }
    1353         284 :         if (col->on_server.column_type != SYBMSTIME) {
    1354         260 :                 TDS_UINT ui = dta->date + 693595;
    1355         260 :                 TDS_PUT_UA4LE(p, ui);
    1356         260 :                 p += 3;
    1357             :         }
    1358         284 :         if (col->on_server.column_type == SYBMSDATETIMEOFFSET) {
    1359           0 :                 TDS_PUT_UA2LE(p, dta->offset);
    1360           0 :                 p += 2;
    1361             :         }
    1362         284 :         buf[0] = (unsigned char) (p - buf - 1);
    1363         284 :         tds_put_n(tds, buf, p - buf);
    1364             : 
    1365         284 :         return TDS_SUCCESS;
    1366             : }
    1367             : 
    1368             : TDSRET
    1369           8 : tds_clrudt_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1370             : {
    1371             :         /* TODO save fields */
    1372             :         /* FIXME support RPC */
    1373             : 
    1374             :         /* MAX_BYTE_SIZE */
    1375           8 :         tds_get_usmallint(tds);
    1376             : 
    1377             :         /* DB_NAME */
    1378           8 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1379             : 
    1380             :         /* SCHEMA_NAME */
    1381           8 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1382             : 
    1383             :         /* TYPE_NAME */
    1384           8 :         tds_get_string(tds, tds_get_byte(tds), NULL, 0);
    1385             : 
    1386             :         /* UDT_METADATA */
    1387           8 :         tds_get_string(tds, tds_get_usmallint(tds), NULL, 0);
    1388             : 
    1389           8 :         col->column_size = 0x7ffffffflu;
    1390           8 :         col->column_varint_size = 8;
    1391             : 
    1392           8 :         return TDS_SUCCESS;
    1393             : }
    1394             : 
    1395             : TDS_INT
    1396          16 : tds_clrudt_row_len(TDSCOLUMN *col)
    1397             : {
    1398          16 :         col->column_varint_size = 8;
    1399             :         /* TODO save other fields */
    1400          16 :         return sizeof(TDSBLOB);
    1401             : }
    1402             : 
    1403             : TDSRET
    1404           0 : tds_clrudt_put_info(TDSSOCKET * tds, TDSCOLUMN * col TDS_UNUSED)
    1405             : {
    1406             :         /* FIXME support properly */
    1407           0 :         tds_put_byte(tds, 0);   /* db_name */
    1408           0 :         tds_put_byte(tds, 0);   /* schema_name */
    1409           0 :         tds_put_byte(tds, 0);   /* type_name */
    1410             : 
    1411           0 :         return TDS_SUCCESS;
    1412             : }
    1413             : 
    1414             : TDSRET
    1415           0 : tds_sybbigtime_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
    1416             : {
    1417           0 :         col->column_scale = col->column_prec = 6;
    1418           0 :         tds_get_byte(tds); /* 8, size */
    1419           0 :         tds_get_byte(tds); /* 6, precision ?? */
    1420           0 :         col->on_server.column_size = col->column_size = sizeof(TDS_UINT8);
    1421           0 :         return TDS_SUCCESS;
    1422             : }
    1423             : 
    1424             : TDS_INT
    1425          60 : tds_sybbigtime_row_len(TDSCOLUMN *col TDS_UNUSED)
    1426             : {
    1427          60 :         return sizeof(TDS_UINT8);
    1428             : }
    1429             : 
    1430             : TDSRET
    1431           0 : tds_sybbigtime_get(TDSSOCKET * tds, TDSCOLUMN * col)
    1432             : {
    1433           0 :         TDS_UINT8 *dt = (TDS_UINT8 *) col->column_data;
    1434           0 :         int size = tds_get_byte(tds);
    1435             : 
    1436           0 :         if (size == 0) {
    1437           0 :                 col->column_cur_size = -1;
    1438           0 :                 return TDS_SUCCESS;
    1439             :         }
    1440             : 
    1441           0 :         col->column_cur_size = sizeof(TDS_UINT8);
    1442           0 :         *dt = tds_get_int8(tds);
    1443             : 
    1444           0 :         return TDS_SUCCESS;
    1445             : }
    1446             : 
    1447             : TDSRET
    1448           0 : tds_sybbigtime_put_info(TDSSOCKET * tds, TDSCOLUMN * col TDS_UNUSED)
    1449             : {
    1450           0 :         tds_put_byte(tds, 8);
    1451           0 :         tds_put_byte(tds, 6);
    1452           0 :         return TDS_SUCCESS;
    1453             : }
    1454             : 
    1455             : TDSRET
    1456           0 : tds_sybbigtime_put(TDSSOCKET *tds, TDSCOLUMN *col, bool bcp7 TDS_UNUSED)
    1457             : {
    1458           0 :         const TDS_UINT8 *dt = (const TDS_UINT8 *) col->column_data;
    1459             : 
    1460           0 :         if (col->column_cur_size < 0) {
    1461           0 :                 tds_put_byte(tds, 0);
    1462           0 :                 return TDS_SUCCESS;
    1463             :         }
    1464             : 
    1465           0 :         tds_put_byte(tds, 8);
    1466           0 :         tds_put_int8(tds, *dt);
    1467             : 
    1468           0 :         return TDS_SUCCESS;
    1469             : }
    1470             : 
    1471             : TDSRET
    1472           0 : tds_mstabletype_get_info(TDSSOCKET *tds TDS_UNUSED, TDSCOLUMN *col TDS_UNUSED)
    1473             : {
    1474             :         /* Table type is strictly only an input variable */
    1475           0 :         return TDS_FAIL;
    1476             : }
    1477             : 
    1478             : TDS_INT
    1479           8 : tds_mstabletype_row_len(TDSCOLUMN *col TDS_UNUSED)
    1480             : {
    1481           8 :         return sizeof(TDS_TVP);
    1482             : }
    1483             : 
    1484             : TDSRET
    1485           0 : tds_mstabletype_get(TDSSOCKET *tds TDS_UNUSED, TDSCOLUMN *col TDS_UNUSED)
    1486             : {
    1487             :         /* Table type is strictly only an input variable */
    1488           0 :         return TDS_FAIL;
    1489             : }
    1490             : 
    1491             : TDSRET
    1492           8 : tds_mstabletype_put_info(TDSSOCKET *tds, TDSCOLUMN *col)
    1493             : {
    1494           8 :         TDS_TVP *table = (TDS_TVP *) col->column_data;
    1495             :         TDSFREEZE current_freeze[1];
    1496             :         unsigned int written;
    1497             : 
    1498             :         /* TVP_TYPENAME */
    1499           8 :         tds_put_byte(tds, 0); /* Empty DB name */
    1500             : 
    1501           8 :         tds_freeze(tds, current_freeze, 1);
    1502           8 :         tds_put_string(tds, table->schema, -1);
    1503           8 :         written = tds_freeze_written(current_freeze) / 2;
    1504           8 :         tds_freeze_close_len(current_freeze, written);
    1505             : 
    1506           8 :         tds_freeze(tds, current_freeze, 1);
    1507           8 :         tds_put_string(tds, table->name, -1);
    1508           8 :         written = tds_freeze_written(current_freeze) / 2;
    1509           8 :         tds_freeze_close_len(current_freeze, written);
    1510             : 
    1511           8 :         return TDS_SUCCESS;
    1512             : }
    1513             : 
    1514             : TDSRET
    1515           8 : tds_mstabletype_put(TDSSOCKET *tds, TDSCOLUMN *col, bool bcp7 TDS_UNUSED)
    1516             : {
    1517           8 :         TDS_TVP *table = (TDS_TVP *) col->column_data;
    1518             :         TDSPARAMINFO *params;
    1519             :         TDSCOLUMN *tds_col;
    1520             :         TDS_TVP_ROW *row;
    1521             :         int i;
    1522           8 :         TDS_USMALLINT num_cols = table->metadata ? table->metadata->num_cols : 0;
    1523             : 
    1524             :         /* COL_METADATA */
    1525           8 :         if (num_cols == 0)
    1526           0 :                 tds_put_smallint(tds, 0xffff); /* TVP_NULL_TOKEN */
    1527             :         else {
    1528           8 :                 tds_put_smallint(tds, num_cols);
    1529             : 
    1530           8 :                 params = table->metadata;
    1531          24 :                 for (i = 0; i < num_cols; i++) {
    1532          16 :                         tds_col = params->columns[i];
    1533             : 
    1534             :                         /* UserType*/
    1535          16 :                         tds_put_int(tds, tds_col->column_usertype);
    1536             :                         /* Flags */
    1537          16 :                         tds_put_smallint(tds, tds_col->column_flags);
    1538             :                         /* TYPE_INFO */
    1539          16 :                         tds_put_byte(tds, tds_col->on_server.column_type);
    1540          16 :                         TDS_PROPAGATE(tds_col->funcs->put_info(tds, tds_col));
    1541             : 
    1542             :                         /* ColName - Empty string */
    1543          16 :                         tds_put_byte(tds, 0x00);
    1544             :                 }
    1545             :         }
    1546             : 
    1547             :         /* TVP_END_TOKEN */
    1548           8 :         tds_put_byte(tds, 0x00);
    1549             : 
    1550          44 :         for (row = table->row; row != NULL; row = row->next) {
    1551             :                 /* TVP_ROW_TOKEN */
    1552          36 :                 tds_put_byte(tds, 0x01);
    1553             : 
    1554          36 :                 params = row->params;
    1555         108 :                 for (i = 0; i < num_cols; i++) {
    1556          72 :                         tds_col = params->columns[i];
    1557          72 :                         TDS_PROPAGATE(tds_col->funcs->put_data(tds, tds_col, false));
    1558             :                 }
    1559             :         }
    1560             : 
    1561             :         /* TVP_END_TOKEN */
    1562           8 :         tds_put_byte(tds, 0x00);
    1563             : 
    1564           8 :         return TDS_SUCCESS;
    1565             : }
    1566             : 
    1567             : TDSRET
    1568           0 : tds_invalid_get_info(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * col TDS_UNUSED)
    1569             : {
    1570           0 :         return TDS_FAIL;
    1571             : }
    1572             : 
    1573             : TDS_INT
    1574           0 : tds_invalid_row_len(TDSCOLUMN *col TDS_UNUSED)
    1575             : {
    1576           0 :         return 0;
    1577             : }
    1578             : 
    1579             : TDSRET
    1580           0 : tds_invalid_get(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * col TDS_UNUSED)
    1581             : {
    1582           0 :         return TDS_FAIL;
    1583             : }
    1584             : 
    1585             : TDSRET
    1586           0 : tds_invalid_put_info(TDSSOCKET * tds TDS_UNUSED, TDSCOLUMN * col TDS_UNUSED)
    1587             : {
    1588           0 :         return TDS_FAIL;
    1589             : }
    1590             : 
    1591             : TDSRET
    1592           0 : tds_invalid_put(TDSSOCKET *tds TDS_UNUSED, TDSCOLUMN *col TDS_UNUSED, bool bcp7 TDS_UNUSED)
    1593             : {
    1594           0 :         return TDS_FAIL;
    1595             : }
    1596             : 
    1597             : #if ENABLE_EXTRA_CHECKS
    1598             : int
    1599    13273828 : tds_generic_check(const TDSCOLUMN *col TDS_UNUSED)
    1600             : {
    1601    13273828 :         return 0;
    1602             : }
    1603             : 
    1604             : int
    1605         140 : tds_sybbigtime_check(const TDSCOLUMN *col)
    1606             : {
    1607         140 :         assert(col->column_type == col->on_server.column_type);
    1608         140 :         assert(col->on_server.column_size == col->column_size);
    1609         140 :         assert(!is_numeric_type(col->column_type));
    1610         140 :         assert(!is_fixed_type(col->column_type));
    1611         140 :         assert(!type_has_textptr(col->column_type));
    1612         140 :         assert(!is_variable_type(col->column_type));
    1613         140 :         assert(is_nullable_type(col->column_type));
    1614         140 :         assert(col->column_varint_size == 1);
    1615         140 :         assert(col->column_prec == 6);
    1616         140 :         assert(col->column_scale == col->column_prec);
    1617             : 
    1618         140 :         return 1;
    1619             : }
    1620             : 
    1621             : int
    1622          24 : tds_mstabletype_check(const TDSCOLUMN *col TDS_UNUSED)
    1623             : {
    1624          24 :         return 0;
    1625             : }
    1626             : 
    1627             : int
    1628         168 : tds_clrudt_check(const TDSCOLUMN *col TDS_UNUSED)
    1629             : {
    1630         168 :         return 0;
    1631             : }
    1632             : 
    1633             : int
    1634        7484 : tds_msdatetime_check(const TDSCOLUMN *col)
    1635             : {
    1636        7484 :         assert(col->column_type == col->on_server.column_type);
    1637        7484 :         assert(col->on_server.column_size == col->column_size);
    1638        7484 :         assert(!is_numeric_type(col->column_type));
    1639        7484 :         if (col->column_type == SYBMSDATE) {
    1640        1894 :                 assert(is_fixed_type(col->column_type));
    1641             :         } else {
    1642        5590 :                 assert(!is_fixed_type(col->column_type));
    1643             :         }
    1644        7484 :         assert(!type_has_textptr(col->column_type));
    1645        7484 :         assert(!is_variable_type(col->column_type));
    1646        7484 :         assert(is_nullable_type(col->column_type));
    1647        7484 :         assert(col->column_varint_size == 1);
    1648        7484 :         assert(col->column_prec >= 0 && col->column_prec <= 7);
    1649        7484 :         assert(col->column_scale == col->column_prec);
    1650             : 
    1651        7484 :         return 1;
    1652             : }
    1653             : 
    1654             : int
    1655        9342 : tds_variant_check(const TDSCOLUMN *col TDS_UNUSED)
    1656             : {
    1657        9342 :         return 0;
    1658             : }
    1659             : 
    1660             : int
    1661       83188 : tds_numeric_check(const TDSCOLUMN *col)
    1662             : {
    1663       83188 :         assert(col->column_type == col->on_server.column_type);
    1664       83188 :         assert(col->on_server.column_size == col->column_size);
    1665       83188 :         assert(is_numeric_type(col->column_type));
    1666       83188 :         assert(!is_fixed_type(col->column_type));
    1667       83188 :         assert(!type_has_textptr(col->column_type));
    1668       83188 :         assert(!is_variable_type(col->column_type));
    1669       83188 :         assert(col->column_varint_size == 1);
    1670       83188 :         assert(col->column_prec >= 1 && col->column_prec <= MAXPRECISION);
    1671       83188 :         assert(col->column_scale <= col->column_prec);
    1672             : 
    1673       83188 :         return 1;
    1674             : }
    1675             : 
    1676             : int
    1677           0 : tds_invalid_check(const TDSCOLUMN *col TDS_UNUSED)
    1678             : {
    1679           0 :         return 1;
    1680             : }
    1681             : #endif
    1682             : 
    1683             : 
    1684             : #define TDS_DECLARE_FUNCS(name) \
    1685             :      extern const TDSCOLUMNFUNCS tds_ ## name ## _funcs
    1686             : 
    1687             : #include <freetds/pushvis.h>
    1688             : TDS_DECLARE_FUNCS(generic);
    1689             : TDS_DECLARE_FUNCS(numeric);
    1690             : TDS_DECLARE_FUNCS(variant);
    1691             : TDS_DECLARE_FUNCS(msdatetime);
    1692             : TDS_DECLARE_FUNCS(clrudt);
    1693             : TDS_DECLARE_FUNCS(sybbigtime);
    1694             : TDS_DECLARE_FUNCS(invalid);
    1695             : TDS_DECLARE_FUNCS(mstabletype);
    1696             : #include <freetds/popvis.h>
    1697             : 
    1698             : static const TDSCOLUMNFUNCS *
    1699       95745 : tds_get_column_funcs(TDSCONNECTION *conn, int type)
    1700             : {
    1701       95745 :         switch (type) {
    1702             :         case SYBNUMERIC:
    1703             :         case SYBDECIMAL:
    1704             :                 return &tds_numeric_funcs;
    1705           8 :         case SYBMSUDT:
    1706           8 :                 return &tds_clrudt_funcs;
    1707        1602 :         case SYBVARIANT:
    1708        1602 :                 if (IS_TDS7_PLUS(conn))
    1709             :                         return &tds_variant_funcs;
    1710             :                 break;
    1711        2476 :         case SYBMSDATE:
    1712             :         case SYBMSTIME:
    1713             :         case SYBMSDATETIME2:
    1714             :         case SYBMSDATETIMEOFFSET:
    1715        2476 :                 return &tds_msdatetime_funcs;
    1716          60 :         case SYB5BIGTIME:
    1717             :         case SYB5BIGDATETIME:
    1718          60 :                 return &tds_sybbigtime_funcs;
    1719           8 :         case SYBMSTABLE:
    1720           8 :                 return &tds_mstabletype_funcs;
    1721             :         }
    1722       88779 :         return &tds_generic_funcs;
    1723             : }
    1724             : #include "tds_types.h"
    1725             : 
    1726             : #ifdef WORDS_BIGENDIAN
    1727             : void
    1728             : tds_swap_datatype(int coltype, void *b)
    1729             : {
    1730             :         unsigned char *buf = (unsigned char *) b;
    1731             : 
    1732             :         switch (coltype) {
    1733             :         case SYBDATETIME4:
    1734             :                 tds_swap_bytes(&buf[2], 2);
    1735             :         case SYBINT2:
    1736             :         case SYBUINT2:
    1737             :                 tds_swap_bytes(buf, 2);
    1738             :                 break;
    1739             :         case SYBMONEY:
    1740             :         case SYBDATETIME:
    1741             :                 tds_swap_bytes(&buf[4], 4);
    1742             :         case SYBINT4:
    1743             :         case SYBUINT4:
    1744             :         case SYBMONEY4:
    1745             :         case SYBREAL:
    1746             :         case SYBDATE:
    1747             :         case SYBTIME:
    1748             :                 tds_swap_bytes(buf, 4);
    1749             :                 break;
    1750             :         case SYBINT8:
    1751             :         case SYBUINT8:
    1752             :         case SYBFLT8:
    1753             :         case SYB5BIGTIME:
    1754             :         case SYB5BIGDATETIME:
    1755             :                 tds_swap_bytes(buf, 8);
    1756             :                 break;
    1757             :         case SYBUNIQUE:
    1758             :                 tds_swap_bytes(buf, 4);
    1759             :                 tds_swap_bytes(&buf[4], 2);
    1760             :                 tds_swap_bytes(&buf[6], 2);
    1761             :                 break;
    1762             :         }
    1763             : }
    1764             : #endif
    1765             : 
    1766             : /**
    1767             :  * Converts numeric from Microsoft representation to internal one (Sybase).
    1768             :  * \param num numeric data to convert
    1769             :  */
    1770             : static void
    1771             : tds_swap_numeric(TDS_NUMERIC *num)
    1772             : {
    1773             :         /* swap the sign */
    1774        2300 :         num->array[0] = (num->array[0] == 0) ? 1 : 0;
    1775             :         /* swap the data */
    1776        2300 :         tds_swap_bytes(&(num->array[1]), tds_numeric_bytes_per_prec[num->precision] - 1);
    1777             : }
    1778             : 

Generated by: LCOV version 1.13