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