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