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 : #undef MIN
213 : #define MIN(a,b) (((a) < (b)) ? (a) : (b))
214 : #undef MAX
215 : #define MAX(a,b) (((a) > (b)) ? (a) : (b))
216 :
217 : /**
218 : * Set type of column initializing all dependency.
219 : * column_usertype should already be set.
220 : * @param curcol column to set
221 : * @param type type to set
222 : */
223 : void
224 73316 : tds_set_column_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
225 : {
226 : /* set type */
227 73316 : curcol->on_server.column_type = type;
228 73316 : curcol->funcs = tds_get_column_funcs(conn, type);
229 73316 : curcol->column_type = tds_get_cardinal_type(type, curcol->column_usertype);
230 :
231 : /* set size */
232 73316 : curcol->column_cur_size = -1;
233 73316 : curcol->column_varint_size = tds_get_varint_size(conn, type);
234 73316 : if (curcol->column_varint_size == 0)
235 20406 : curcol->column_cur_size = curcol->on_server.column_size = curcol->column_size = tds_get_size_by_type(type);
236 :
237 73316 : }
238 :
239 : /**
240 : * Set type of column initializing all dependency
241 : * \param tds state information for the socket and the TDS protocol
242 : * \param curcol column to set
243 : * \param type type to set
244 : */
245 : void
246 16470 : tds_set_param_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE type)
247 : {
248 16470 : if (IS_TDS7_PLUS(conn)) {
249 13958 : switch (type) {
250 1740 : case SYBVARCHAR:
251 1740 : type = XSYBVARCHAR;
252 1740 : break;
253 858 : case SYBCHAR:
254 858 : type = XSYBCHAR;
255 858 : break;
256 156 : case SYBVARBINARY:
257 156 : type = XSYBVARBINARY;
258 156 : break;
259 826 : case SYBBINARY:
260 826 : type = XSYBBINARY;
261 826 : break;
262 594 : case SYBBIT:
263 594 : type = SYBBITN;
264 594 : break;
265 : /* avoid warning on other types */
266 : default:
267 : break;
268 : }
269 2512 : } else if (IS_TDS50(conn)) {
270 2512 : switch (type) {
271 14 : case SYBINT8:
272 14 : type = SYB5INT8;
273 14 : break;
274 : /* avoid warning on other types */
275 : default:
276 : break;
277 : }
278 0 : }
279 16470 : tds_set_column_type(conn, curcol, type);
280 :
281 16470 : if (is_collate_type(type) || is_char_type(type)) {
282 5712 : curcol->char_conv = conn->char_convs[is_unicode_type(type) ? client2ucs2 : client2server_chardata];
283 5712 : memcpy(curcol->column_collation, conn->collation, sizeof(conn->collation));
284 : }
285 :
286 : /* special case, GUID, varint != 0 but only a size */
287 : /* TODO VARIANT, when supported */
288 16470 : switch (type) {
289 34 : case SYBUNIQUE:
290 34 : curcol->on_server.column_size = curcol->column_size = sizeof(TDS_UNIQUE);
291 34 : break;
292 596 : case SYBBITN:
293 596 : curcol->on_server.column_size = curcol->column_size = sizeof(TDS_TINYINT);
294 596 : break;
295 : /* mssql 2005 don't like SYBINT4 as parameter closing connection */
296 3502 : case SYBINT1:
297 : case SYBINT2:
298 : case SYBINT4:
299 : case SYBINT8:
300 3502 : curcol->on_server.column_type = SYBINTN;
301 3502 : curcol->column_varint_size = 1;
302 3502 : curcol->column_cur_size = -1;
303 3502 : break;
304 48 : case SYBMONEY4:
305 : case SYBMONEY:
306 48 : curcol->on_server.column_type = SYBMONEYN;
307 48 : curcol->column_varint_size = 1;
308 48 : curcol->column_cur_size = -1;
309 48 : break;
310 726 : case SYBDATETIME:
311 : case SYBDATETIME4:
312 726 : curcol->on_server.column_type = SYBDATETIMN;
313 726 : curcol->column_varint_size = 1;
314 726 : curcol->column_cur_size = -1;
315 726 : break;
316 1846 : case SYBFLT8:
317 : case SYBREAL:
318 1846 : curcol->on_server.column_type = SYBFLTN;
319 1846 : curcol->column_varint_size = 1;
320 1846 : curcol->column_cur_size = -1;
321 1846 : break;
322 348 : case SYBNTEXT:
323 348 : if (IS_TDS72_PLUS(conn)) {
324 116 : curcol->column_varint_size = 8;
325 116 : curcol->on_server.column_type = XSYBNVARCHAR;
326 : }
327 : break;
328 520 : case SYBTEXT:
329 520 : if (IS_TDS72_PLUS(conn)) {
330 152 : curcol->column_varint_size = 8;
331 152 : curcol->on_server.column_type = XSYBVARCHAR;
332 : }
333 : break;
334 142 : case SYBIMAGE:
335 142 : if (IS_TDS72_PLUS(conn)) {
336 44 : curcol->column_varint_size = 8;
337 44 : curcol->on_server.column_type = XSYBVARBINARY;
338 : }
339 : break;
340 0 : case SYB5BIGTIME:
341 : case SYB5BIGDATETIME:
342 0 : curcol->column_prec = 6;
343 0 : curcol->column_scale = 6;
344 0 : break;
345 : default:
346 : break;
347 : }
348 16470 : }
349 :
350 : TDS_SERVER_TYPE
351 9819357 : tds_get_cardinal_type(TDS_SERVER_TYPE datatype, int usertype)
352 : {
353 9819357 : switch (datatype) {
354 : case XSYBVARBINARY:
355 : return SYBVARBINARY;
356 5492 : case XSYBBINARY:
357 5492 : return SYBBINARY;
358 22412 : case SYBNTEXT:
359 22412 : return SYBTEXT;
360 1957964 : case XSYBNVARCHAR:
361 : case XSYBVARCHAR:
362 1957964 : return SYBVARCHAR;
363 1075925 : case XSYBNCHAR:
364 : case XSYBCHAR:
365 1075925 : return SYBCHAR;
366 942 : case SYB5INT8:
367 942 : return SYBINT8;
368 18936 : case SYBLONGBINARY:
369 18936 : switch (usertype) {
370 : case USER_UNICHAR_TYPE:
371 : case USER_UNIVARCHAR_TYPE:
372 : return SYBTEXT;
373 : }
374 : break;
375 208 : case SYBMSXML:
376 208 : return SYBLONGCHAR;
377 : default:
378 : break;
379 : }
380 6728182 : return datatype;
381 : }
382 :
383 : TDSRET
384 52574 : tds_generic_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
385 : {
386 52574 : switch (col->column_varint_size) {
387 8 : case 8:
388 8 : col->column_size = 0x7ffffffflu;
389 8 : break;
390 2802 : case 5:
391 : case 4:
392 2802 : col->column_size = tds_get_int(tds);
393 2802 : if (col->column_size < 0)
394 : return TDS_FAIL;
395 : break;
396 15436 : case 2:
397 : /* assure > 0 */
398 15436 : col->column_size = tds_get_smallint(tds);
399 : /* under TDS7.2 this means ?var???(MAX) */
400 15436 : if (col->column_size < 0 && IS_TDS72_PLUS(tds->conn)) {
401 92 : if (is_char_type(col->column_type))
402 72 : col->column_size = 0x3ffffffflu;
403 : else
404 20 : col->column_size = 0x7ffffffflu;
405 :
406 92 : col->column_varint_size = 8;
407 : }
408 15436 : if (col->column_size < 0)
409 : return TDS_FAIL;
410 : break;
411 21080 : case 1:
412 21080 : col->column_size = tds_get_byte(tds);
413 21080 : break;
414 13248 : case 0:
415 13248 : col->column_size = tds_get_size_by_type(col->column_type);
416 13248 : break;
417 : }
418 :
419 52574 : if (IS_TDS71_PLUS(tds->conn) && is_collate_type(col->on_server.column_type)) {
420 : /* based on true type as sent by server */
421 : /*
422 : * first 2 bytes are windows code (such as 0x409 for english)
423 : * other 2 bytes ???
424 : * last bytes is id in syscharsets
425 : */
426 16204 : tds_get_n(tds, col->column_collation, 5);
427 16204 : col->char_conv =
428 16204 : tds_iconv_from_collate(tds->conn, col->column_collation);
429 : }
430 :
431 : /* Only read table_name for blob columns (eg. not for SYBLONGBINARY) */
432 52574 : if (is_blob_type(col->on_server.column_type)) {
433 : /* discard this additional byte */
434 1912 : if (IS_TDS72_PLUS(tds->conn)) {
435 486 : unsigned char num_parts = tds_get_byte(tds);
436 : /* TODO do not discard first ones */
437 972 : for (; num_parts; --num_parts) {
438 486 : tds_dstr_get(tds, &col->table_name, tds_get_usmallint(tds));
439 : }
440 : } else {
441 1426 : tds_dstr_get(tds, &col->table_name, tds_get_usmallint(tds));
442 : }
443 50662 : } else if (IS_TDS72_PLUS(tds->conn) && col->on_server.column_type == SYBMSXML) {
444 8 : unsigned char has_schema = tds_get_byte(tds);
445 8 : if (has_schema) {
446 : /* discard schema informations */
447 2 : tds_get_string(tds, tds_get_byte(tds), NULL, 0); /* dbname */
448 2 : tds_get_string(tds, tds_get_byte(tds), NULL, 0); /* schema owner */
449 2 : tds_get_string(tds, tds_get_usmallint(tds), NULL, 0); /* schema collection */
450 : }
451 : }
452 : return TDS_SUCCESS;
453 : }
454 :
455 : /* tds_generic_row_len support also variant and return size to hold blob */
456 : TDS_COMPILE_CHECK(variant_size, sizeof(TDSBLOB) >= sizeof(TDSVARIANT));
457 :
458 : TDS_INT
459 114000 : tds_generic_row_len(TDSCOLUMN *col)
460 : {
461 114000 : CHECK_COLUMN_EXTRA(col);
462 :
463 114000 : if (is_blob_col(col))
464 : return sizeof(TDSBLOB);
465 106470 : return col->column_size;
466 : }
467 :
468 : static TDSRET
469 2756 : tds_get_char_dynamic(TDSSOCKET *tds, TDSCOLUMN *curcol, void **pp, size_t allocated, TDSINSTREAM *r_stream)
470 : {
471 : TDSRET res;
472 : TDSDYNAMICSTREAM w;
473 :
474 : /*
475 : * Blobs don't use a column's fixed buffer because the official maximum size is 2 GB.
476 : * Instead, they're reallocated as necessary, based on the data's size.
477 : */
478 2756 : res = tds_dynamic_stream_init(&w, pp, allocated);
479 2756 : if (TDS_FAILED(res))
480 : return res;
481 :
482 2756 : if (USE_ICONV && curcol->char_conv)
483 820 : res = tds_convert_stream(tds, curcol->char_conv, to_client, r_stream, &w.stream);
484 : else
485 1936 : res = tds_copy_stream(r_stream, &w.stream);
486 2756 : curcol->column_cur_size = w.size;
487 : return res;
488 : }
489 :
490 : typedef struct tds_varmax_stream {
491 : TDSINSTREAM stream;
492 : TDSSOCKET *tds;
493 : TDS_INT chunk_left;
494 : } TDSVARMAXSTREAM;
495 :
496 : static int
497 162 : tds_varmax_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
498 : {
499 162 : TDSVARMAXSTREAM *s = (TDSVARMAXSTREAM *) stream;
500 :
501 : /* read chunk len if needed */
502 162 : if (s->chunk_left == 0) {
503 162 : TDS_INT l = tds_get_int(s->tds);
504 162 : if (l <= 0) l = -1;
505 162 : s->chunk_left = l;
506 : }
507 :
508 : /* no more data ?? */
509 162 : if (s->chunk_left < 0)
510 : return 0;
511 :
512 : /* read part of data */
513 74 : if (len > s->chunk_left)
514 74 : len = s->chunk_left;
515 74 : s->chunk_left -= len;
516 74 : if (tds_get_n(s->tds, ptr, len))
517 74 : return len;
518 : return -1;
519 : }
520 :
521 : static TDSRET
522 88 : tds72_get_varmax(TDSSOCKET * tds, TDSCOLUMN * curcol)
523 : {
524 : TDS_INT8 len;
525 : TDSVARMAXSTREAM r;
526 88 : size_t allocated = 0;
527 88 : void **pp = (void**) &(((TDSBLOB*) curcol->column_data)->textvalue);
528 :
529 88 : len = tds_get_int8(tds);
530 :
531 : /* NULL */
532 88 : if (len == -1) {
533 0 : curcol->column_cur_size = -1;
534 0 : return TDS_SUCCESS;
535 : }
536 :
537 : /* try to allocate an initial buffer */
538 : if (len > (TDS_INT8) (~((size_t) 0) >> 1))
539 : return TDS_FAIL;
540 88 : if (len > 0) {
541 66 : TDS_ZERO_FREE(*pp);
542 66 : allocated = (size_t) len;
543 66 : if (is_unicode_type(curcol->on_server.column_type))
544 16 : allocated /= 2;
545 : }
546 :
547 88 : r.stream.read = tds_varmax_stream_read;
548 88 : r.tds = tds;
549 88 : r.chunk_left = 0;
550 :
551 88 : return tds_get_char_dynamic(tds, curcol, pp, allocated, &r.stream);
552 : }
553 :
554 : TDS_COMPILE_CHECK(tds_variant_size, sizeof(((TDSVARIANT*)0)->data) == sizeof(((TDSBLOB*)0)->textvalue));
555 : TDS_COMPILE_CHECK(tds_variant_offset,TDS_OFFSET(TDSVARIANT, data) == TDS_OFFSET(TDSBLOB, textvalue));
556 :
557 : /*
558 : * This strange type has following structure
559 : * 0 len (int32) -- NULL
560 : * len (int32), type (int8), data -- ints, date, etc
561 : * len (int32), type (int8), 7 (int8), collation, column size (int16) -- [n]char, [n]varchar, binary, varbinary
562 : * BLOBS (text/image) not supported
563 : */
564 : TDSRET
565 104 : tds_variant_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
566 : {
567 104 : unsigned int colsize = tds_get_uint(tds);
568 : int varint;
569 : TDS_SERVER_TYPE type;
570 : TDS_UCHAR info_len;
571 : TDSVARIANT *v;
572 : TDSRET rc;
573 :
574 : /* NULL */
575 104 : curcol->column_cur_size = -1;
576 104 : if (colsize < 2) {
577 4 : tds_get_n(tds, NULL, colsize);
578 4 : return TDS_SUCCESS;
579 : }
580 :
581 100 : type = (TDS_SERVER_TYPE) tds_get_byte(tds);
582 100 : info_len = tds_get_byte(tds);
583 100 : if (!is_variant_inner_type(type))
584 : goto error_type;
585 100 : v = (TDSVARIANT*) curcol->column_data;
586 100 : v->type = type;
587 100 : colsize -= 2;
588 100 : if (info_len > colsize)
589 : goto error_type;
590 100 : if (is_collate_type(type)) {
591 42 : if (sizeof(v->collation) > info_len)
592 : goto error_type;
593 42 : tds_get_n(tds, v->collation, sizeof(v->collation));
594 42 : colsize -= sizeof(v->collation);
595 42 : info_len -= sizeof(v->collation);
596 42 : curcol->char_conv = is_unicode_type(type) ?
597 42 : tds->conn->char_convs[client2ucs2] : tds_iconv_from_collate(tds->conn, v->collation);
598 : }
599 :
600 : /* special case for numeric */
601 100 : if (is_numeric_type(type)) {
602 : TDS_NUMERIC *num;
603 12 : if (info_len != 2)
604 : goto error_type;
605 12 : if (v->data)
606 6 : TDS_ZERO_FREE(v->data);
607 12 : v->data_len = sizeof(TDS_NUMERIC);
608 12 : num = tds_new0(TDS_NUMERIC, 1);
609 12 : if (!num)
610 : goto error_memory;
611 12 : v->data = (TDS_CHAR *) num;
612 12 : num->precision = tds_get_byte(tds);
613 12 : num->scale = tds_get_byte(tds);
614 12 : colsize -= 2;
615 : /* check prec/scale, don't let server crash us */
616 12 : if (num->precision < 1 || num->precision > MAXPRECISION
617 12 : || num->scale > num->precision)
618 : goto error_type;
619 12 : if (colsize > sizeof(num->array))
620 : goto error_type;
621 12 : curcol->column_cur_size = colsize;
622 12 : tds_get_n(tds, num->array, colsize);
623 12 : if (IS_TDS7_PLUS(tds->conn))
624 : tds_swap_numeric(num);
625 : return TDS_SUCCESS;
626 : }
627 :
628 : /* special case for MS date/time */
629 88 : switch (type) {
630 8 : case SYBMSTIME:
631 : case SYBMSDATETIME2:
632 : case SYBMSDATETIMEOFFSET:
633 8 : if (info_len != 1)
634 : goto error_type;
635 8 : curcol->column_scale = curcol->column_prec = tds_get_byte(tds);
636 8 : if (curcol->column_prec > 7)
637 : goto error_type;
638 8 : colsize -= info_len;
639 8 : info_len = 0;
640 : /* fall through */
641 2 : case SYBMSDATE:
642 2 : if (info_len != 0)
643 : goto error_type;
644 : /* dirty trick */
645 10 : tds->in_buf[--tds->in_pos] = colsize;
646 10 : if (v->data)
647 0 : TDS_ZERO_FREE(v->data);
648 10 : v->data_len = sizeof(TDS_DATETIMEALL);
649 10 : v->data = tds_new0(TDS_CHAR, sizeof(TDS_DATETIMEALL));
650 10 : curcol->column_type = type;
651 10 : curcol->column_data = (unsigned char *) v->data;
652 : /* trick, call get function */
653 10 : rc = tds_msdatetime_get(tds, curcol);
654 10 : curcol->column_type = SYBVARIANT;
655 10 : curcol->column_data = (unsigned char *) v;
656 10 : return rc;
657 : default:
658 : break;
659 : }
660 78 : varint = (type == SYBUNIQUE) ? 0 : tds_get_varint_size(tds->conn, type);
661 156 : if (varint != info_len || varint > 2)
662 : goto error_type;
663 78 : switch (varint) {
664 30 : case 0:
665 30 : v->size = tds_get_size_by_type(type);
666 30 : break;
667 0 : case 1:
668 0 : v->size = tds_get_byte(tds);
669 0 : break;
670 48 : case 2:
671 48 : v->size = tds_get_smallint(tds);
672 48 : break;
673 : default:
674 : goto error_type;
675 : }
676 78 : colsize -= info_len;
677 78 : curcol->column_cur_size = colsize;
678 78 : if (v->data)
679 6 : TDS_ZERO_FREE(v->data);
680 78 : if (colsize) {
681 : TDSRET res;
682 : TDSDATAINSTREAM r;
683 :
684 78 : if (USE_ICONV && curcol->char_conv)
685 12 : v->type = tds_get_cardinal_type(type, 0);
686 :
687 78 : tds_datain_stream_init(&r, tds, colsize);
688 78 : res = tds_get_char_dynamic(tds, curcol, (void **) &v->data, colsize, &r.stream);
689 78 : if (TDS_FAILED(res))
690 0 : return res;
691 78 : colsize = curcol->column_cur_size;
692 : #ifdef WORDS_BIGENDIAN
693 : tds_swap_datatype(tds_get_conversion_type(type, colsize), v->data);
694 : #endif
695 : }
696 78 : v->data_len = colsize;
697 78 : CHECK_COLUMN_EXTRA(curcol);
698 78 : return TDS_SUCCESS;
699 :
700 0 : error_type:
701 0 : error_memory:
702 0 : tds_get_n(tds, NULL, colsize);
703 0 : return TDS_FAIL;
704 : }
705 :
706 : /**
707 : * Read a data from wire
708 : * \param tds state information for the socket and the TDS protocol
709 : * \param curcol column where store column information
710 : * \return TDS_FAIL on error or TDS_SUCCESS
711 : */
712 : TDSRET
713 850662 : tds_generic_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
714 : {
715 : unsigned char *dest;
716 : int len, colsize;
717 : int fillchar;
718 850662 : TDSBLOB *blob = NULL;
719 :
720 850662 : CHECK_TDS_EXTRA(tds);
721 850662 : CHECK_COLUMN_EXTRA(curcol);
722 :
723 850662 : tdsdump_log(TDS_DBG_INFO1, "tds_get_data: type %d, varint size %d\n", curcol->column_type, curcol->column_varint_size);
724 850662 : switch (curcol->column_varint_size) {
725 2126 : case 4:
726 : /* It's a BLOB... */
727 2126 : len = tds_get_byte(tds);
728 2126 : blob = (TDSBLOB *) curcol->column_data;
729 2126 : if (len == 16) { /* Jeff's hack */
730 1938 : tds_get_n(tds, blob->textptr, 16);
731 1938 : tds_get_n(tds, blob->timestamp, 8);
732 1938 : blob->valid_ptr = true;
733 2436 : if (IS_TDS72_PLUS(tds->conn) &&
734 498 : memcmp(blob->textptr, "dummy textptr\0\0",16) == 0)
735 498 : blob->valid_ptr = false;
736 1938 : colsize = tds_get_int(tds);
737 : } else {
738 : colsize = -1;
739 : }
740 : break;
741 778 : case 5:
742 778 : colsize = tds_get_int(tds);
743 778 : if (colsize == 0)
744 0 : colsize = -1;
745 : break;
746 88 : case 8:
747 88 : return tds72_get_varmax(tds, curcol);
748 279673 : case 2:
749 279673 : colsize = tds_get_smallint(tds);
750 279673 : break;
751 297322 : case 1:
752 297322 : colsize = tds_get_byte(tds);
753 297322 : if (colsize == 0)
754 7620 : colsize = -1;
755 : break;
756 270675 : case 0:
757 : /* TODO this should be column_size */
758 270675 : colsize = tds_get_size_by_type(curcol->column_type);
759 : break;
760 : default:
761 : colsize = -1;
762 : break;
763 : }
764 850574 : if (IS_TDSDEAD(tds))
765 : return TDS_FAIL;
766 :
767 850574 : tdsdump_log(TDS_DBG_INFO1, "tds_get_data(): wire column size is %d \n", colsize);
768 : /* set NULL flag in the row buffer */
769 850574 : if (colsize < 0) {
770 10690 : curcol->column_cur_size = -1;
771 10690 : return TDS_SUCCESS;
772 : }
773 :
774 : /*
775 : * We're now set to read the data from the wire. For varying types (e.g. char/varchar)
776 : * make sure that curcol->column_cur_size reflects the size of the read data,
777 : * after any charset conversion. tds_get_char_data() does that for you,
778 : * but of course tds_get_n() doesn't.
779 : *
780 : * colsize == wire_size, bytes to read
781 : * curcol->column_cur_size == sizeof destination buffer, room to write
782 : */
783 839884 : dest = curcol->column_data;
784 839884 : if (is_blob_col(curcol)) {
785 : TDSDATAINSTREAM r;
786 : size_t allocated;
787 : TDSRET ret;
788 :
789 2716 : blob = (TDSBLOB *) dest; /* cf. column_varint_size case 4, above */
790 :
791 : /* empty string */
792 2716 : if (colsize == 0) {
793 126 : curcol->column_cur_size = 0;
794 126 : if (blob->textvalue)
795 0 : TDS_ZERO_FREE(blob->textvalue);
796 : return TDS_SUCCESS;
797 : }
798 :
799 2590 : allocated = MAX(curcol->column_cur_size, 0);
800 2590 : if (colsize > allocated) {
801 2322 : TDS_ZERO_FREE(blob->textvalue);
802 2322 : allocated = colsize;
803 2322 : if (is_unicode_type(curcol->on_server.column_type))
804 528 : allocated /= 2;
805 : }
806 :
807 2590 : tds_datain_stream_init(&r, tds, colsize);
808 2590 : ret = tds_get_char_dynamic(tds, curcol, (void **) &blob->textvalue, allocated, &r.stream);
809 2590 : if (TDS_FAILED(ret) && TDS_UNLIKELY(r.wire_size > 0)) {
810 0 : tds_get_n(tds, NULL, r.wire_size);
811 0 : return ret;
812 : }
813 : return TDS_SUCCESS;
814 : }
815 :
816 : /* non-numeric and non-blob */
817 :
818 837168 : if (USE_ICONV && curcol->char_conv) {
819 382283 : if (TDS_FAILED(tds_get_char_data(tds, (char *) dest, colsize, curcol)))
820 : return TDS_FAIL;
821 : } else {
822 : /*
823 : * special case, some servers seem to return more data in some conditions
824 : * (ASA 7 returning 4 byte nullable integer)
825 : */
826 454885 : int discard_len = 0;
827 454885 : if (colsize > curcol->column_size) {
828 0 : discard_len = colsize - curcol->column_size;
829 0 : colsize = curcol->column_size;
830 : }
831 454885 : if (!tds_get_n(tds, dest, colsize))
832 : return TDS_FAIL;
833 454885 : if (discard_len > 0)
834 0 : tds_get_n(tds, NULL, discard_len);
835 454885 : curcol->column_cur_size = colsize;
836 : }
837 :
838 : /* pad (UNI)CHAR and BINARY types */
839 837168 : fillchar = 0;
840 837168 : switch (curcol->column_type) {
841 : /* extra handling for SYBLONGBINARY */
842 0 : case SYBLONGBINARY:
843 0 : if (curcol->column_usertype != USER_UNICHAR_TYPE)
844 : break;
845 : case SYBCHAR:
846 : case XSYBCHAR:
847 166144 : if (curcol->column_size != curcol->on_server.column_size)
848 : break;
849 : /* FIXME use client charset */
850 : fillchar = ' ';
851 165787 : case SYBBINARY:
852 : case XSYBBINARY:
853 165787 : if (colsize < curcol->column_size)
854 70 : memset(dest + colsize, fillchar, curcol->column_size - colsize);
855 165787 : colsize = curcol->column_size;
856 165787 : break;
857 : default:
858 : break;
859 : }
860 :
861 : #ifdef WORDS_BIGENDIAN
862 : tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n", tds_get_conversion_type(curcol->column_type, colsize));
863 : tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), dest);
864 : #endif
865 453 : return TDS_SUCCESS;
866 : }
867 :
868 : /**
869 : * Put data information to wire
870 : * \param tds state information for the socket and the TDS protocol
871 : * \param col column where to store information
872 : * \return TDS_SUCCESS or TDS_FAIL
873 : */
874 : TDSRET
875 8138 : tds_generic_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
876 : {
877 : size_t size;
878 :
879 8138 : CHECK_TDS_EXTRA(tds);
880 8138 : CHECK_COLUMN_EXTRA(col);
881 :
882 8138 : size = tds_fix_column_size(tds, col);
883 8138 : switch (col->column_varint_size) {
884 : case 0:
885 : break;
886 3532 : case 1:
887 3532 : tds_put_byte(tds, size);
888 3532 : break;
889 3056 : case 2:
890 3056 : tds_put_smallint(tds, size);
891 3056 : break;
892 628 : case 5:
893 : case 4:
894 628 : tds_put_int(tds, size);
895 628 : break;
896 312 : case 8:
897 312 : tds_put_smallint(tds, 0xffff);
898 312 : break;
899 : }
900 :
901 : /* TDS5 wants a table name for LOBs */
902 8138 : if (IS_TDS50(tds->conn) && is_blob_type(col->on_server.column_type))
903 0 : tds_put_smallint(tds, 0);
904 :
905 : /* TDS7.1 output collate information */
906 8138 : if (IS_TDS71_PLUS(tds->conn) && is_collate_type(col->on_server.column_type))
907 3674 : tds_put_n(tds, tds->conn->collation, 5);
908 :
909 8138 : return TDS_SUCCESS;
910 : }
911 :
912 : /**
913 : * Write data to wire
914 : * \param tds state information for the socket and the TDS protocol
915 : * \param curcol column where store column information
916 : * \return TDS_FAIL on error or TDS_SUCCESS
917 : */
918 : TDSRET
919 14036 : tds_generic_put(TDSSOCKET * tds, TDSCOLUMN * curcol, int bcp7)
920 : {
921 : unsigned char *src;
922 14036 : TDSBLOB *blob = NULL;
923 : size_t colsize, size;
924 :
925 : const char *s;
926 14036 : int converted = 0;
927 :
928 14036 : CHECK_TDS_EXTRA(tds);
929 14036 : CHECK_COLUMN_EXTRA(curcol);
930 :
931 14036 : tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: colsize = %d\n", (int) curcol->column_cur_size);
932 :
933 : /* output NULL data */
934 14036 : if (curcol->column_cur_size < 0) {
935 3872 : tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: null param\n");
936 3872 : switch (curcol->column_varint_size) {
937 0 : case 5:
938 0 : tds_put_int(tds, 0);
939 0 : break;
940 84 : case 4:
941 84 : if ((bcp7 || !IS_TDS7_PLUS(tds->conn)) && is_blob_type(curcol->on_server.column_type))
942 12 : tds_put_byte(tds, 0);
943 : else
944 72 : tds_put_int(tds, -1);
945 : break;
946 1050 : case 2:
947 1050 : tds_put_smallint(tds, -1);
948 1050 : break;
949 38 : case 8:
950 38 : tds_put_int8(tds, -1);
951 38 : break;
952 2700 : default:
953 2700 : assert(curcol->column_varint_size);
954 : /* FIXME not good for SYBLONGBINARY/SYBLONGCHAR (still not supported) */
955 2700 : tds_put_byte(tds, 0);
956 2700 : break;
957 : }
958 : return TDS_SUCCESS;
959 : }
960 10164 : colsize = curcol->column_cur_size;
961 :
962 10164 : size = tds_fix_column_size(tds, curcol);
963 :
964 10164 : src = curcol->column_data;
965 10164 : if (is_blob_col(curcol)) {
966 842 : blob = (TDSBLOB *) src;
967 842 : src = (unsigned char *) blob->textvalue;
968 : }
969 :
970 10164 : s = (char *) src;
971 :
972 : /* convert string if needed */
973 10164 : if (!bcp7 && curcol->char_conv && curcol->char_conv->flags != TDS_ENCODING_MEMCPY && colsize) {
974 : size_t output_size;
975 : #if 0
976 : /* TODO this case should be optimized */
977 : /* we know converted bytes */
978 : if (curcol->char_conv->client_charset.min_bytes_per_char == curcol->char_conv->client_charset.max_bytes_per_char
979 : && curcol->char_conv->server_charset.min_bytes_per_char == curcol->char_conv->server_charset.max_bytes_per_char) {
980 : converted_size = colsize * curcol->char_conv->server_charset.min_bytes_per_char / curcol->char_conv->client_charset.min_bytes_per_char;
981 :
982 : } else {
983 : #endif
984 : /* we need to convert data before */
985 : /* TODO this can be a waste of memory... */
986 2258 : converted = 1;
987 2258 : s = tds_convert_string(tds, curcol->char_conv, s, colsize, &output_size);
988 2258 : colsize = (TDS_INT)output_size;
989 2258 : if (!s) {
990 : /* on conversion error put a empty string */
991 : /* TODO on memory failure we should compute converted size and use chunks */
992 0 : colsize = 0;
993 0 : converted = -1;
994 : }
995 : }
996 :
997 : /*
998 : * TODO here we limit data sent with MIN, should mark somewhere
999 : * and inform client ??
1000 : * Test proprietary behavior
1001 : */
1002 10164 : if (IS_TDS7_PLUS(tds->conn)) {
1003 9530 : tdsdump_log(TDS_DBG_INFO1, "tds_generic_put: not null param varint_size = %d\n",
1004 0 : curcol->column_varint_size);
1005 :
1006 9530 : switch (curcol->column_varint_size) {
1007 274 : case 8:
1008 : /* this difference for BCP operation is due to
1009 : * a bug in different server version that does
1010 : * not accept a length here */
1011 274 : tds_put_int8(tds, bcp7 ? (TDS_INT8) -2 : (TDS_INT8) colsize);
1012 274 : tds_put_int(tds, colsize);
1013 274 : break;
1014 564 : case 4: /* It's a BLOB... */
1015 564 : colsize = MIN(colsize, size);
1016 : /* mssql require only size */
1017 564 : if (bcp7 && is_blob_type(curcol->on_server.column_type)) {
1018 : static const unsigned char textptr[] = {
1019 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1020 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
1021 : };
1022 42 : tds_put_byte(tds, 16);
1023 42 : tds_put_n(tds, textptr, 16);
1024 42 : tds_put_n(tds, textptr, 8);
1025 : }
1026 564 : tds_put_int(tds, colsize);
1027 564 : break;
1028 3348 : case 2:
1029 3348 : colsize = MIN(colsize, size);
1030 3348 : tds_put_smallint(tds, colsize);
1031 3348 : break;
1032 2300 : case 1:
1033 2300 : colsize = MIN(colsize, size);
1034 2300 : tds_put_byte(tds, colsize);
1035 2300 : break;
1036 3044 : case 0:
1037 : /* TODO should be column_size */
1038 3044 : colsize = tds_get_size_by_type(curcol->on_server.column_type);
1039 3044 : break;
1040 : }
1041 :
1042 : /* conversion error, exit with an error */
1043 9530 : if (converted < 0)
1044 : return TDS_FAIL;
1045 :
1046 : /* put real data */
1047 9530 : if (blob) {
1048 838 : tds_put_n(tds, s, colsize);
1049 : } else {
1050 : #ifdef WORDS_BIGENDIAN
1051 : unsigned char buf[64];
1052 :
1053 : if (!converted && colsize < 64) {
1054 : tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n",
1055 : tds_get_conversion_type(curcol->column_type, colsize));
1056 : memcpy(buf, s, colsize);
1057 : tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf);
1058 : s = (char *) buf;
1059 : }
1060 : #endif
1061 8692 : tds_put_n(tds, s, colsize);
1062 : }
1063 : /* finish chunk for varchar/varbinary(max) */
1064 9530 : if (curcol->column_varint_size == 8 && colsize)
1065 266 : tds_put_int(tds, 0);
1066 : } else {
1067 : /* TODO ICONV handle charset conversions for data */
1068 : /* put size of data */
1069 634 : switch (curcol->column_varint_size) {
1070 4 : case 5: /* It's a LONGBINARY */
1071 4 : colsize = MIN(colsize, 0x7fffffff);
1072 4 : tds_put_int(tds, colsize);
1073 4 : break;
1074 0 : case 4: /* It's a BLOB... */
1075 0 : tds_put_byte(tds, 16);
1076 0 : tds_put_n(tds, blob->textptr, 16);
1077 0 : tds_put_n(tds, blob->timestamp, 8);
1078 0 : colsize = MIN(colsize, 0x7fffffff);
1079 0 : tds_put_int(tds, colsize);
1080 0 : break;
1081 0 : case 2:
1082 0 : colsize = MIN(colsize, 8000);
1083 0 : tds_put_smallint(tds, colsize);
1084 0 : break;
1085 618 : case 1:
1086 618 : if (!colsize) {
1087 10 : tds_put_byte(tds, 1);
1088 10 : if (is_char_type(curcol->column_type))
1089 10 : tds_put_byte(tds, ' ');
1090 : else
1091 0 : tds_put_byte(tds, 0);
1092 10 : if (converted > 0)
1093 0 : tds_convert_string_free((char*)src, s);
1094 : return TDS_SUCCESS;
1095 : }
1096 608 : colsize = MIN(colsize, 255);
1097 608 : tds_put_byte(tds, colsize);
1098 608 : break;
1099 12 : case 0:
1100 : /* TODO should be column_size */
1101 12 : colsize = tds_get_size_by_type(curcol->column_type);
1102 12 : break;
1103 : }
1104 :
1105 : /* conversion error, exit with an error */
1106 624 : if (converted < 0)
1107 : return TDS_FAIL;
1108 :
1109 : /* put real data */
1110 624 : if (blob) {
1111 4 : tds_put_n(tds, s, colsize);
1112 : } else {
1113 : #ifdef WORDS_BIGENDIAN
1114 : unsigned char buf[64];
1115 :
1116 : if (!converted && colsize < 64) {
1117 : tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n",
1118 : tds_get_conversion_type(curcol->column_type, colsize));
1119 : memcpy(buf, s, colsize);
1120 : tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf);
1121 : s = (char *) buf;
1122 : }
1123 : #endif
1124 620 : tds_put_n(tds, s, colsize);
1125 : }
1126 : }
1127 10154 : if (converted > 0)
1128 2258 : tds_convert_string_free((char*)src, s);
1129 : return TDS_SUCCESS;
1130 : }
1131 :
1132 : TDSRET
1133 1176 : tds_numeric_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
1134 : {
1135 1176 : col->column_size = tds_get_byte(tds);
1136 1176 : col->column_prec = tds_get_byte(tds); /* precision */
1137 1176 : col->column_scale = tds_get_byte(tds); /* scale */
1138 :
1139 : /* check prec/scale, don't let server crash us */
1140 1176 : if (col->column_prec < 1 || col->column_prec > MAXPRECISION
1141 1176 : || col->column_scale > col->column_prec)
1142 : return TDS_FAIL;
1143 :
1144 1176 : return TDS_SUCCESS;
1145 : }
1146 :
1147 : TDS_INT
1148 2760 : tds_numeric_row_len(TDSCOLUMN *col)
1149 : {
1150 2760 : return sizeof(TDS_NUMERIC);
1151 : }
1152 :
1153 : TDSRET
1154 1476 : tds_numeric_get(TDSSOCKET * tds, TDSCOLUMN * curcol)
1155 : {
1156 : int colsize;
1157 : TDS_NUMERIC *num;
1158 :
1159 1476 : CHECK_TDS_EXTRA(tds);
1160 1476 : CHECK_COLUMN_EXTRA(curcol);
1161 :
1162 1476 : colsize = tds_get_byte(tds);
1163 :
1164 : /* set NULL flag in the row buffer */
1165 1476 : if (colsize <= 0) {
1166 334 : curcol->column_cur_size = -1;
1167 334 : return TDS_SUCCESS;
1168 : }
1169 :
1170 : /*
1171 : * Since these can be passed around independent
1172 : * of the original column they came from, we embed the TDS_NUMERIC datatype in the row buffer
1173 : * instead of using the wire representation, even though it uses a few more bytes.
1174 : */
1175 1142 : num = (TDS_NUMERIC *) curcol->column_data;
1176 1142 : memset(num, '\0', sizeof(TDS_NUMERIC));
1177 : /* TODO perhaps it would be fine to change format ?? */
1178 1142 : num->precision = curcol->column_prec;
1179 1142 : num->scale = curcol->column_scale;
1180 :
1181 : /* server is going to crash freetds ?? */
1182 : /* TODO close connection it server try to do so ?? */
1183 1142 : if (colsize > sizeof(num->array))
1184 : return TDS_FAIL;
1185 1142 : tds_get_n(tds, num->array, colsize);
1186 :
1187 1142 : if (IS_TDS7_PLUS(tds->conn))
1188 : tds_swap_numeric(num);
1189 :
1190 : /* corrected colsize for column_cur_size */
1191 1142 : curcol->column_cur_size = sizeof(TDS_NUMERIC);
1192 :
1193 1142 : return TDS_SUCCESS;
1194 : }
1195 :
1196 : TDSRET
1197 538 : tds_numeric_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
1198 : {
1199 538 : CHECK_TDS_EXTRA(tds);
1200 538 : CHECK_COLUMN_EXTRA(col);
1201 :
1202 : #if 1
1203 538 : tds_put_byte(tds, tds_numeric_bytes_per_prec[col->column_prec]);
1204 538 : tds_put_byte(tds, col->column_prec);
1205 538 : tds_put_byte(tds, col->column_scale);
1206 : #else
1207 : TDS_NUMERIC *num = (TDS_NUMERIC *) col->column_data;
1208 : tds_put_byte(tds, tds_numeric_bytes_per_prec[num->precision]);
1209 : tds_put_byte(tds, num->precision);
1210 : tds_put_byte(tds, num->scale);
1211 : #endif
1212 :
1213 538 : return TDS_SUCCESS;
1214 : }
1215 :
1216 : TDSRET
1217 1462 : tds_numeric_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
1218 : {
1219 1462 : TDS_NUMERIC *num = (TDS_NUMERIC *) col->column_data, buf;
1220 : unsigned char colsize;
1221 :
1222 1462 : if (col->column_cur_size < 0) {
1223 606 : tds_put_byte(tds, 0);
1224 606 : return TDS_SUCCESS;
1225 : }
1226 856 : colsize = tds_numeric_bytes_per_prec[num->precision];
1227 856 : tds_put_byte(tds, colsize);
1228 :
1229 856 : buf = *num;
1230 856 : if (IS_TDS7_PLUS(tds->conn))
1231 : tds_swap_numeric(&buf);
1232 856 : tds_put_n(tds, buf.array, colsize);
1233 856 : return TDS_SUCCESS;
1234 : }
1235 :
1236 : TDSRET
1237 0 : tds_variant_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
1238 : {
1239 : /* TODO */
1240 0 : return TDS_FAIL;
1241 : }
1242 :
1243 : TDSRET
1244 0 : tds_variant_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
1245 : {
1246 : /* TODO */
1247 0 : return TDS_FAIL;
1248 : }
1249 :
1250 : TDSRET
1251 130 : tds_msdatetime_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
1252 : {
1253 130 : col->column_scale = col->column_prec = 0;
1254 130 : if (col->column_type != SYBMSDATE) {
1255 94 : col->column_scale = col->column_prec = tds_get_byte(tds);
1256 94 : if (col->column_prec > 7)
1257 : return TDS_FAIL;
1258 : }
1259 130 : col->on_server.column_size = col->column_size = sizeof(TDS_DATETIMEALL);
1260 130 : return TDS_SUCCESS;
1261 : }
1262 :
1263 : TDS_INT
1264 896 : tds_msdatetime_row_len(TDSCOLUMN *col)
1265 : {
1266 896 : return sizeof(TDS_DATETIMEALL);
1267 : }
1268 :
1269 : TDSRET
1270 130 : tds_msdatetime_get(TDSSOCKET * tds, TDSCOLUMN * col)
1271 : {
1272 130 : TDS_DATETIMEALL *dt = (TDS_DATETIMEALL*) col->column_data;
1273 130 : int size = tds_get_byte(tds);
1274 :
1275 130 : if (size == 0) {
1276 0 : col->column_cur_size = -1;
1277 0 : return TDS_SUCCESS;
1278 : }
1279 :
1280 130 : memset(dt, 0, sizeof(*dt));
1281 :
1282 130 : if (col->column_type == SYBMSDATETIMEOFFSET)
1283 10 : size -= 2;
1284 130 : if (col->column_type != SYBMSTIME)
1285 92 : size -= 3;
1286 130 : if (size < 0)
1287 : return TDS_FAIL;
1288 :
1289 130 : dt->time_prec = col->column_prec;
1290 :
1291 : /* get time part */
1292 130 : if (col->column_type != SYBMSDATE) {
1293 : TDS_UINT8 u8;
1294 : int i;
1295 :
1296 94 : if (size < 3 || size > 5)
1297 0 : return TDS_FAIL;
1298 94 : u8 = 0;
1299 94 : tds_get_n(tds, &u8, size);
1300 : #ifdef WORDS_BIGENDIAN
1301 : tds_swap_bytes(&u8, 8);
1302 : #endif
1303 216 : for (i = col->column_prec; i < 7; ++i)
1304 122 : u8 *= 10;
1305 94 : dt->time = u8;
1306 94 : dt->has_time = 1;
1307 36 : } else if (size != 0)
1308 : return TDS_FAIL;
1309 :
1310 : /* get date part */
1311 130 : if (col->column_type != SYBMSTIME) {
1312 : TDS_UINT ui;
1313 :
1314 92 : ui = 0;
1315 92 : tds_get_n(tds, &ui, 3);
1316 : #ifdef WORDS_BIGENDIAN
1317 : tds_swap_bytes(&ui, 4);
1318 : #endif
1319 92 : dt->has_date = 1;
1320 92 : dt->date = ui - 693595;
1321 : }
1322 :
1323 : /* get time offset */
1324 130 : if (col->column_type == SYBMSDATETIMEOFFSET) {
1325 10 : dt->offset = tds_get_smallint(tds);
1326 10 : if (dt->offset > 840 || dt->offset < -840)
1327 : return TDS_FAIL;
1328 10 : dt->has_offset = 1;
1329 : }
1330 130 : col->column_cur_size = sizeof(TDS_DATETIMEALL);
1331 130 : return TDS_SUCCESS;
1332 : }
1333 :
1334 : TDSRET
1335 174 : tds_msdatetime_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
1336 : {
1337 : /* TODO precision */
1338 174 : if (col->on_server.column_type != SYBMSDATE)
1339 162 : tds_put_byte(tds, 7);
1340 174 : return TDS_SUCCESS;
1341 : }
1342 :
1343 : TDSRET
1344 174 : tds_msdatetime_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
1345 : {
1346 174 : const TDS_DATETIMEALL *dta = (const TDS_DATETIMEALL *) col->column_data;
1347 : unsigned char buf[12], *p;
1348 :
1349 174 : if (col->column_cur_size < 0) {
1350 36 : tds_put_byte(tds, 0);
1351 36 : return TDS_SUCCESS;
1352 : }
1353 :
1354 : /* TODO precision */
1355 138 : p = buf + 1;
1356 138 : if (col->on_server.column_type != SYBMSDATE) {
1357 126 : TDS_PUT_UA4LE(p, (TDS_UINT) dta->time);
1358 126 : p[4] = (unsigned char) (dta->time >> 32);
1359 126 : p += 5;
1360 : }
1361 138 : if (col->on_server.column_type != SYBMSTIME) {
1362 126 : TDS_UINT ui = dta->date + 693595;
1363 126 : TDS_PUT_UA4LE(p, ui);
1364 126 : p += 3;
1365 : }
1366 138 : if (col->on_server.column_type == SYBMSDATETIMEOFFSET) {
1367 0 : TDS_PUT_UA2LE(p, dta->offset);
1368 0 : p += 2;
1369 : }
1370 138 : buf[0] = p - buf - 1;
1371 138 : tds_put_n(tds, buf, p - buf);
1372 :
1373 138 : return TDS_SUCCESS;
1374 : }
1375 :
1376 : TDSRET
1377 4 : tds_clrudt_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
1378 : {
1379 : /* TODO save fields */
1380 : /* FIXME support RPC */
1381 :
1382 : /* MAX_BYTE_SIZE */
1383 4 : tds_get_usmallint(tds);
1384 :
1385 : /* DB_NAME */
1386 4 : tds_get_string(tds, tds_get_byte(tds), NULL, 0);
1387 :
1388 : /* SCHEMA_NAME */
1389 4 : tds_get_string(tds, tds_get_byte(tds), NULL, 0);
1390 :
1391 : /* TYPE_NAME */
1392 4 : tds_get_string(tds, tds_get_byte(tds), NULL, 0);
1393 :
1394 : /* UDT_METADATA */
1395 4 : tds_get_string(tds, tds_get_usmallint(tds), NULL, 0);
1396 :
1397 4 : col->column_size = 0x7ffffffflu;
1398 4 : col->column_varint_size = 8;
1399 :
1400 4 : return TDS_SUCCESS;
1401 : }
1402 :
1403 : TDS_INT
1404 8 : tds_clrudt_row_len(TDSCOLUMN *col)
1405 : {
1406 8 : col->column_varint_size = 8;
1407 : /* TODO save other fields */
1408 8 : return sizeof(TDSBLOB);
1409 : }
1410 :
1411 : TDSRET
1412 0 : tds_clrudt_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
1413 : {
1414 : /* FIXME support properly*/
1415 0 : tds_put_byte(tds, 0); /* db_name */
1416 0 : tds_put_byte(tds, 0); /* schema_name */
1417 0 : tds_put_byte(tds, 0); /* type_name */
1418 :
1419 0 : return TDS_SUCCESS;
1420 : }
1421 :
1422 : TDSRET
1423 0 : tds_sybbigtime_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
1424 : {
1425 0 : col->column_scale = col->column_prec = 6;
1426 0 : tds_get_byte(tds); /* 8, size */
1427 0 : tds_get_byte(tds); /* 6, precision ?? */
1428 0 : col->on_server.column_size = col->column_size = sizeof(TDS_UINT8);
1429 0 : return TDS_SUCCESS;
1430 : }
1431 :
1432 : TDS_INT
1433 48 : tds_sybbigtime_row_len(TDSCOLUMN *col)
1434 : {
1435 48 : return sizeof(TDS_UINT8);
1436 : }
1437 :
1438 : TDSRET
1439 0 : tds_sybbigtime_get(TDSSOCKET * tds, TDSCOLUMN * col)
1440 : {
1441 0 : TDS_UINT8 *dt = (TDS_UINT8 *) col->column_data;
1442 0 : int size = tds_get_byte(tds);
1443 :
1444 0 : if (size == 0) {
1445 0 : col->column_cur_size = -1;
1446 0 : return TDS_SUCCESS;
1447 : }
1448 :
1449 0 : col->column_cur_size = sizeof(TDS_UINT8);
1450 0 : *dt = tds_get_int8(tds);
1451 :
1452 0 : return TDS_SUCCESS;
1453 : }
1454 :
1455 : TDSRET
1456 0 : tds_sybbigtime_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
1457 : {
1458 0 : tds_put_byte(tds, 8);
1459 0 : tds_put_byte(tds, 6);
1460 0 : return TDS_SUCCESS;
1461 : }
1462 :
1463 : TDSRET
1464 0 : tds_sybbigtime_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
1465 : {
1466 0 : const TDS_UINT8 *dt = (const TDS_UINT8 *) col->column_data;
1467 :
1468 0 : if (col->column_cur_size < 0) {
1469 0 : tds_put_byte(tds, 0);
1470 0 : return TDS_SUCCESS;
1471 : }
1472 :
1473 0 : tds_put_byte(tds, 8);
1474 0 : tds_put_int8(tds, *dt);
1475 :
1476 0 : return TDS_SUCCESS;
1477 : }
1478 :
1479 : TDSRET
1480 0 : tds_mstabletype_get_info(TDSSOCKET *tds, TDSCOLUMN *col)
1481 : {
1482 : /* Table type is strictly only an input variable */
1483 0 : return TDS_FAIL;
1484 : }
1485 :
1486 : TDS_INT
1487 4 : tds_mstabletype_row_len(TDSCOLUMN *col)
1488 : {
1489 4 : return sizeof(TDS_TVP);
1490 : }
1491 :
1492 : TDSRET
1493 0 : tds_mstabletype_get(TDSSOCKET *tds, TDSCOLUMN *col)
1494 : {
1495 : /* Table type is strictly only an input variable */
1496 0 : return TDS_FAIL;
1497 : }
1498 :
1499 : TDSRET
1500 4 : tds_mstabletype_put_info(TDSSOCKET *tds, TDSCOLUMN *col)
1501 : {
1502 4 : TDS_TVP *table = (TDS_TVP *) col->column_data;
1503 : TDSFREEZE current_freeze[1];
1504 : size_t written;
1505 :
1506 : /* TVP_TYPENAME */
1507 4 : tds_put_byte(tds, 0); /* Empty DB name */
1508 :
1509 4 : tds_freeze(tds, current_freeze, 1);
1510 4 : tds_put_string(tds, table->schema, -1);
1511 4 : written = tds_freeze_written(current_freeze) / 2;
1512 4 : tds_freeze_close_len(current_freeze, written);
1513 :
1514 4 : tds_freeze(tds, current_freeze, 1);
1515 4 : tds_put_string(tds, table->name, -1);
1516 4 : written = tds_freeze_written(current_freeze) / 2;
1517 4 : tds_freeze_close_len(current_freeze, written);
1518 :
1519 4 : return TDS_SUCCESS;
1520 : }
1521 :
1522 : TDSRET
1523 4 : tds_mstabletype_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
1524 : {
1525 4 : TDS_TVP *table = (TDS_TVP *) col->column_data;
1526 : TDSPARAMINFO *params;
1527 : TDSCOLUMN *tds_col;
1528 : TDS_TVP_ROW *row;
1529 : TDSRET ret;
1530 : int i;
1531 4 : TDS_USMALLINT num_cols = table->metadata ? table->metadata->num_cols : 0;
1532 :
1533 : /* COL_METADATA */
1534 4 : if (num_cols == 0)
1535 0 : tds_put_smallint(tds, 0xffff); /* TVP_NULL_TOKEN */
1536 : else {
1537 4 : tds_put_smallint(tds, num_cols);
1538 :
1539 4 : params = table->metadata;
1540 12 : for (i = 0; i < num_cols; i++) {
1541 8 : tds_col = params->columns[i];
1542 :
1543 : /* UserType*/
1544 8 : tds_put_int(tds, tds_col->column_usertype);
1545 : /* Flags */
1546 8 : tds_put_smallint(tds, tds_col->column_flags);
1547 : /* TYPE_INFO */
1548 8 : tds_put_byte(tds, tds_col->on_server.column_type);
1549 8 : ret = tds_col->funcs->put_info(tds, tds_col);
1550 8 : if (TDS_FAILED(ret))
1551 : return ret;
1552 :
1553 : /* ColName - Empty string */
1554 8 : tds_put_byte(tds, 0x00);
1555 : }
1556 : }
1557 :
1558 : /* TVP_END_TOKEN */
1559 4 : tds_put_byte(tds, 0x00);
1560 :
1561 22 : for (row = table->row; row != NULL; row = row->next) {
1562 : /* TVP_ROW_TOKEN */
1563 18 : tds_put_byte(tds, 0x01);
1564 :
1565 18 : params = row->params;
1566 54 : for (i = 0; i < num_cols; i++) {
1567 36 : tds_col = params->columns[i];
1568 36 : ret = tds_col->funcs->put_data(tds, tds_col, 0);
1569 36 : if (TDS_FAILED(ret))
1570 : return ret;
1571 : }
1572 : }
1573 :
1574 : /* TVP_END_TOKEN */
1575 4 : tds_put_byte(tds, 0x00);
1576 :
1577 4 : return TDS_SUCCESS;
1578 : }
1579 :
1580 : TDSRET
1581 0 : tds_invalid_get_info(TDSSOCKET * tds, TDSCOLUMN * col)
1582 : {
1583 0 : return TDS_FAIL;
1584 : }
1585 :
1586 : TDS_INT
1587 0 : tds_invalid_row_len(TDSCOLUMN *col)
1588 : {
1589 0 : return 0;
1590 : }
1591 :
1592 : TDSRET
1593 0 : tds_invalid_get(TDSSOCKET * tds, TDSCOLUMN * col)
1594 : {
1595 0 : return TDS_FAIL;
1596 : }
1597 :
1598 : TDSRET
1599 0 : tds_invalid_put_info(TDSSOCKET * tds, TDSCOLUMN * col)
1600 : {
1601 0 : return TDS_FAIL;
1602 : }
1603 :
1604 : TDSRET
1605 0 : tds_invalid_put(TDSSOCKET *tds, TDSCOLUMN *col, int bcp7)
1606 : {
1607 0 : return TDS_FAIL;
1608 : }
1609 :
1610 : #if ENABLE_EXTRA_CHECKS
1611 : int
1612 9740733 : tds_generic_check(const TDSCOLUMN *col)
1613 : {
1614 9740733 : return 0;
1615 : }
1616 :
1617 : int
1618 112 : tds_sybbigtime_check(const TDSCOLUMN *col)
1619 : {
1620 112 : assert(col->column_type == col->on_server.column_type);
1621 112 : assert(col->on_server.column_size == col->column_size);
1622 112 : assert(!is_numeric_type(col->column_type));
1623 112 : assert(!is_fixed_type(col->column_type));
1624 112 : assert(!is_blob_type(col->column_type));
1625 112 : assert(!is_variable_type(col->column_type));
1626 112 : assert(is_nullable_type(col->column_type));
1627 112 : assert(col->column_varint_size == 1);
1628 112 : assert(col->column_prec == 6);
1629 112 : assert(col->column_scale == col->column_prec);
1630 :
1631 112 : return 1;
1632 : }
1633 :
1634 : int
1635 12 : tds_mstabletype_check(const TDSCOLUMN *col)
1636 : {
1637 12 : return 0;
1638 : }
1639 :
1640 : int
1641 84 : tds_clrudt_check(const TDSCOLUMN *col)
1642 : {
1643 84 : return 0;
1644 : }
1645 :
1646 : int
1647 3726 : tds_msdatetime_check(const TDSCOLUMN *col)
1648 : {
1649 3726 : assert(col->column_type == col->on_server.column_type);
1650 3726 : assert(col->on_server.column_size == col->column_size);
1651 3726 : assert(!is_numeric_type(col->column_type));
1652 3726 : if (col->column_type == SYBMSDATE) {
1653 1076 : assert(is_fixed_type(col->column_type));
1654 : } else {
1655 2650 : assert(!is_fixed_type(col->column_type));
1656 : }
1657 3726 : assert(!is_blob_type(col->column_type));
1658 3726 : assert(!is_variable_type(col->column_type));
1659 3726 : assert(is_nullable_type(col->column_type));
1660 3726 : assert(col->column_varint_size == 1);
1661 3726 : assert(col->column_prec >= 0 && col->column_prec <= 7);
1662 3726 : assert(col->column_scale == col->column_prec);
1663 :
1664 3726 : return 1;
1665 : }
1666 :
1667 : int
1668 7226 : tds_variant_check(const TDSCOLUMN *col)
1669 : {
1670 7226 : return 0;
1671 : }
1672 :
1673 : int
1674 64193 : tds_numeric_check(const TDSCOLUMN *col)
1675 : {
1676 64193 : assert(col->column_type == col->on_server.column_type);
1677 64193 : assert(col->on_server.column_size == col->column_size);
1678 64193 : assert(is_numeric_type(col->column_type));
1679 64193 : assert(!is_fixed_type(col->column_type));
1680 64193 : assert(!is_blob_type(col->column_type));
1681 64193 : assert(!is_variable_type(col->column_type));
1682 64193 : assert(col->column_varint_size == 1);
1683 64193 : assert(col->column_prec >= 1 && col->column_prec <= MAXPRECISION);
1684 64193 : assert(col->column_scale <= col->column_prec);
1685 :
1686 64193 : return 1;
1687 : }
1688 :
1689 : int
1690 0 : tds_invalid_check(const TDSCOLUMN *col)
1691 : {
1692 0 : return 1;
1693 : }
1694 : #endif
1695 :
1696 :
1697 : #define TDS_DECLARE_FUNCS(name) \
1698 : extern const TDSCOLUMNFUNCS tds_ ## name ## _funcs
1699 :
1700 : #include <freetds/pushvis.h>
1701 : TDS_DECLARE_FUNCS(generic);
1702 : TDS_DECLARE_FUNCS(numeric);
1703 : TDS_DECLARE_FUNCS(variant);
1704 : TDS_DECLARE_FUNCS(msdatetime);
1705 : TDS_DECLARE_FUNCS(clrudt);
1706 : TDS_DECLARE_FUNCS(sybbigtime);
1707 : TDS_DECLARE_FUNCS(invalid);
1708 : TDS_DECLARE_FUNCS(mstabletype);
1709 : #include <freetds/popvis.h>
1710 :
1711 : static const TDSCOLUMNFUNCS *
1712 73316 : tds_get_column_funcs(TDSCONNECTION *conn, int type)
1713 : {
1714 73316 : switch (type) {
1715 : case SYBNUMERIC:
1716 : case SYBDECIMAL:
1717 : return &tds_numeric_funcs;
1718 4 : case SYBMSUDT:
1719 4 : return &tds_clrudt_funcs;
1720 1270 : case SYBVARIANT:
1721 1270 : if (IS_TDS7_PLUS(conn))
1722 : return &tds_variant_funcs;
1723 : break;
1724 1786 : case SYBMSDATE:
1725 : case SYBMSTIME:
1726 : case SYBMSDATETIME2:
1727 : case SYBMSDATETIMEOFFSET:
1728 1786 : return &tds_msdatetime_funcs;
1729 48 : case SYB5BIGTIME:
1730 : case SYB5BIGDATETIME:
1731 48 : return &tds_sybbigtime_funcs;
1732 4 : case SYBMSTABLE:
1733 4 : return &tds_mstabletype_funcs;
1734 : }
1735 68024 : return &tds_generic_funcs;
1736 : }
1737 : #include "tds_types.h"
1738 :
1739 : #ifdef WORDS_BIGENDIAN
1740 : void
1741 : tds_swap_datatype(int coltype, void *b)
1742 : {
1743 : unsigned char *buf = (unsigned char *) b;
1744 :
1745 : switch (coltype) {
1746 : case SYBDATETIME4:
1747 : tds_swap_bytes(&buf[2], 2);
1748 : case SYBINT2:
1749 : case SYBUINT2:
1750 : tds_swap_bytes(buf, 2);
1751 : break;
1752 : case SYBMONEY:
1753 : case SYBDATETIME:
1754 : tds_swap_bytes(&buf[4], 4);
1755 : case SYBINT4:
1756 : case SYBUINT4:
1757 : case SYBMONEY4:
1758 : case SYBREAL:
1759 : case SYBDATE:
1760 : case SYBTIME:
1761 : tds_swap_bytes(buf, 4);
1762 : break;
1763 : case SYBINT8:
1764 : case SYBUINT8:
1765 : case SYBFLT8:
1766 : case SYB5BIGTIME:
1767 : case SYB5BIGDATETIME:
1768 : tds_swap_bytes(buf, 8);
1769 : break;
1770 : case SYBUNIQUE:
1771 : tds_swap_bytes(buf, 4);
1772 : tds_swap_bytes(&buf[4], 2);
1773 : tds_swap_bytes(&buf[6], 2);
1774 : break;
1775 : }
1776 : }
1777 : #endif
1778 :
1779 : /**
1780 : * Converts numeric from Microsoft representation to internal one (Sybase).
1781 : * \param num numeric data to convert
1782 : */
1783 : static void
1784 : tds_swap_numeric(TDS_NUMERIC *num)
1785 : {
1786 : /* swap the sign */
1787 1706 : num->array[0] = (num->array[0] == 0) ? 1 : 0;
1788 : /* swap the data */
1789 1706 : tds_swap_bytes(&(num->array[1]), tds_numeric_bytes_per_prec[num->precision] - 1);
1790 : }
1791 :
|