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