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