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