1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 * Copyright (C) 1998-1999 Brian Bruns
3 * Copyright (C) 2003-2010 Frediano Ziglio
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #include <config.h>
22
23 #include <assert.h>
24
25 #if HAVE_STDLIB_H
26 #include <stdlib.h>
27 #endif /* HAVE_STDLIB_H */
28
29 #if HAVE_STRING_H
30 #include <string.h>
31 #endif /* HAVE_STRING_H */
32
33 #include <ctype.h>
34
35 #include <freetds/utils.h>
36 #include <freetds/odbc.h>
37 #include <freetds/convert.h>
38 #include <freetds/iconv.h>
39 #include <freetds/utils/string.h>
40 #include <freetds/encodings.h>
41 #include <odbcss.h>
42
43 #define TDS_ISSPACE(c) isspace((unsigned char) (c))
44
45 /**
46 * Copy beginning of column_iconv_buf
47 */
48 static void
eat_iconv_left(TDSCOLUMN * curcol,char ** pbuf,size_t * plen)49 eat_iconv_left(TDSCOLUMN * curcol, char **pbuf, size_t *plen)
50 {
51 unsigned cp = ODBC_MIN(*plen, curcol->column_iconv_left);
52 memcpy(*pbuf, curcol->column_iconv_buf, cp);
53 if (cp < curcol->column_iconv_left)
54 memmove(curcol->column_iconv_buf, curcol->column_iconv_buf + cp, curcol->column_iconv_left - cp);
55 curcol->column_iconv_left -= cp;
56 *pbuf += cp;
57 *plen -= cp;
58 }
59
60 /**
61 * Handle conversions from TDS (N)CHAR to ODBC (W)CHAR
62 */
63 static SQLLEN
odbc_convert_char(TDS_STMT * stmt,TDSCOLUMN * curcol,TDS_CHAR * src,TDS_UINT srclen,int desttype,TDS_CHAR * dest,SQLULEN destlen)64 odbc_convert_char(TDS_STMT * stmt, TDSCOLUMN * curcol, TDS_CHAR * src, TDS_UINT srclen, int desttype, TDS_CHAR * dest, SQLULEN destlen)
65 {
66 const char *ib;
67 char *ob;
68 size_t il, ol, char_size;
69
70 /* FIXME MARS not correct cause is the global tds but stmt->tds can be NULL on SQLGetData */
71 TDSSOCKET *tds = stmt->dbc->tds_socket;
72
73 TDSICONV *conv = curcol->char_conv;
74 if (!conv)
75 conv = tds->conn->char_convs[client2server_chardata];
76 if (desttype == SQL_C_WCHAR) {
77 int charset = odbc_get_wide_canonic(tds->conn);
78 /* SQL_C_WCHAR, convert to wide encode */
79 conv = tds_iconv_get_info(tds->conn, charset, conv->to.charset.canonic);
80 if (!conv)
81 conv = tds_iconv_get_info(tds->conn, charset, TDS_CHARSET_ISO_8859_1);
82 #ifdef ENABLE_ODBC_WIDE
83 } else {
84 conv = tds_iconv_get_info(tds->conn, stmt->dbc->original_charset_num, conv->to.charset.canonic);
85 if (!conv)
86 conv = tds_iconv_get_info(tds->conn, stmt->dbc->original_charset_num, TDS_CHARSET_ISO_8859_1);
87 if (!conv)
88 conv = tds_iconv_get_info(tds->conn, TDS_CHARSET_ISO_8859_1, TDS_CHARSET_ISO_8859_1);
89 #endif
90 }
91
92 ib = src;
93 il = srclen;
94 ob = dest;
95 ol = 0;
96 char_size = desttype == SQL_C_CHAR ? 1 : SIZEOF_SQLWCHAR;
97 if (destlen >= char_size) {
98 ol = destlen - char_size;
99 /* copy left and continue only if possible */
100 eat_iconv_left(curcol, &ob, &ol);
101 if (ol) {
102 memset(&conv->suppress, 0, sizeof(conv->suppress));
103 conv->suppress.eilseq = 1;
104 conv->suppress.e2big = 1;
105 /* TODO check return value */
106 tds_iconv(tds, conv, to_client, &ib, &il, &ob, &ol);
107 }
108 /* if input left try to decode on future left */
109 if (il && ol < sizeof(curcol->column_iconv_buf) && curcol->column_iconv_left == 0) {
110 char *left_ob = curcol->column_iconv_buf;
111 size_t left_ol = sizeof(curcol->column_iconv_buf);
112 conv->suppress.eilseq = 1;
113 conv->suppress.einval = 1;
114 conv->suppress.e2big = 1;
115 tds_iconv(tds, conv, to_client, &ib, &il, &left_ob, &left_ol);
116 curcol->column_iconv_left = sizeof(curcol->column_iconv_buf) - left_ol;
117 /* copy part to fill buffer */
118 eat_iconv_left(curcol, &ob, &ol);
119 }
120 ol = ob - dest; /* bytes written */
121 curcol->column_text_sqlgetdatapos += ib - src;
122 /* terminate string */
123 memset(ob, 0, char_size);
124 }
125
126 /* returned size have to take into account buffer left unconverted */
127 if (il == 0 || (conv->from.charset.min_bytes_per_char == conv->from.charset.max_bytes_per_char
128 && conv->to.charset.min_bytes_per_char == conv->to.charset.max_bytes_per_char)) {
129 ol += il * conv->from.charset.min_bytes_per_char / conv->to.charset.min_bytes_per_char + curcol->column_iconv_left;
130 } else if ((conv->flags & TDS_ENCODING_MEMCPY) != 0) {
131 ol += il + curcol->column_iconv_left;
132 } else {
133 /* TODO convert and discard ?? or return proper SQL_NO_TOTAL values ?? */
134 return SQL_NO_TOTAL;
135 }
136 return ol;
137 }
138
139 /**
140 * Handle conversions from TDS NCHAR to ISO8859-1 striping spaces (for fixed types)
141 */
142 static int
odbc_tds_convert_wide_iso(TDSCOLUMN * curcol,TDS_CHAR * src,TDS_UINT srclen,TDS_CHAR * buf,TDS_UINT buf_len)143 odbc_tds_convert_wide_iso(TDSCOLUMN *curcol, TDS_CHAR *src, TDS_UINT srclen, TDS_CHAR *buf, TDS_UINT buf_len)
144 {
145 TDS_CHAR *p;
146 /*
147 * TODO check for endian
148 * This affect for instance Sybase under little endian system
149 */
150
151 /* skip white spaces */
152 while (srclen > 1 && src[1] == 0 && TDS_ISSPACE(src[0])) {
153 srclen -= 2;
154 src += 2;
155 }
156
157 /* convert */
158 p = buf;
159 while (buf_len > 1 && srclen > 1 && src[1] == 0) {
160 *p++ = src[0];
161 --buf_len;
162 srclen -= 2;
163 src += 2;
164 }
165
166 /* skip white spaces */
167 while (srclen > 1 && src[1] == 0 && TDS_ISSPACE(src[0])) {
168 srclen -= 2;
169 src += 2;
170 }
171
172 /* still characters, wrong format */
173 if (srclen)
174 return -1;
175
176 *p = 0;
177 return p - buf;
178 }
179
180 /* The following structure is going to write in these structure not using them
181 * but just knowing the ABI. Check these ABI. Mainly make sure the alignment
182 * is still correct.
183 */
184 TDS_COMPILE_CHECK(ss_time2, sizeof(SQL_SS_TIME2_STRUCT) == 12
185 && TDS_OFFSET(SQL_SS_TIME2_STRUCT, fraction) == 8);
186 TDS_COMPILE_CHECK(ss_timestampoffset, sizeof(SQL_SS_TIMESTAMPOFFSET_STRUCT) == 20
187 && TDS_OFFSET(SQL_SS_TIMESTAMPOFFSET_STRUCT, fraction) == 12);
188 TDS_COMPILE_CHECK(date_struct, sizeof(DATE_STRUCT) == 6
189 && TDS_OFFSET(DATE_STRUCT, year) == 0
190 && TDS_OFFSET(DATE_STRUCT, month) == 2
191 && TDS_OFFSET(DATE_STRUCT, day) == 4);
192 TDS_COMPILE_CHECK(timestamp_struct, sizeof(TIMESTAMP_STRUCT) == 16
193 && TDS_OFFSET(TIMESTAMP_STRUCT, year) == 0
194 && TDS_OFFSET(TIMESTAMP_STRUCT, month) == 2
195 && TDS_OFFSET(TIMESTAMP_STRUCT, day) == 4
196 && TDS_OFFSET(TIMESTAMP_STRUCT, hour) == 6
197 && TDS_OFFSET(TIMESTAMP_STRUCT, minute) == 8
198 && TDS_OFFSET(TIMESTAMP_STRUCT, second) == 10
199 && TDS_OFFSET(TIMESTAMP_STRUCT, fraction) == 12);
200
201 /**
202 * Handle conversions from MSSQL 2008 DATE/TIME types to binary.
203 * These types have a different binary representation in libTDS.
204 */
205 static SQLLEN
odbc_convert_datetime_to_binary(TDS_STMT * stmt,TDSCOLUMN * curcol,int srctype,TDS_DATETIMEALL * dta,TDS_CHAR * dest,SQLULEN destlen)206 odbc_convert_datetime_to_binary(TDS_STMT * stmt, TDSCOLUMN *curcol, int srctype, TDS_DATETIMEALL * dta, TDS_CHAR * dest, SQLULEN destlen)
207 {
208 size_t len, cplen;
209 TDS_USMALLINT buf[10];
210 TDSDATEREC when;
211
212 tds_datecrack(srctype, dta, &when);
213
214 len = 0;
215 if (srctype != SYBMSTIME && srctype != SYBTIME && srctype != SYB5BIGTIME) {
216 buf[0] = when.year;
217 buf[1] = when.month + 1;
218 buf[2] = when.day;
219 len = 3;
220 }
221 if (srctype != SYBMSDATE && srctype != SYBDATE) {
222 buf[len++] = when.hour;
223 buf[len++] = when.minute;
224 buf[len++] = when.second;
225 if ((len % 2) != 0)
226 buf[len++] = 0;
227 *((TDS_UINT*) (buf+len)) = when.decimicrosecond * 100u;
228 len += 2;
229 }
230 if (srctype == SYBMSDATETIMEOFFSET) {
231 /* TODO check for negative hour/minutes */
232 buf[8] = dta->offset / 60;
233 buf[9] = dta->offset % 60;
234 len = 10;
235 }
236 len *= 2;
237
238 /* just return length */
239 if (destlen == 0)
240 return len;
241
242 cplen = ODBC_MIN(destlen, len);
243 memcpy(dest, buf, cplen);
244 if (curcol)
245 curcol->column_text_sqlgetdatapos += cplen;
246 return len;
247 }
248
249 static SQLLEN
odbc_convert_to_binary(TDS_STMT * stmt,TDSCOLUMN * curcol,int srctype,TDS_CHAR * src,TDS_UINT srclen,TDS_CHAR * dest,SQLULEN destlen)250 odbc_convert_to_binary(TDS_STMT * stmt, TDSCOLUMN *curcol, int srctype, TDS_CHAR * src, TDS_UINT srclen, TDS_CHAR * dest, SQLULEN destlen)
251 {
252 SQLLEN ret = srclen;
253
254 /* special case for date/time */
255 switch (srctype) {
256 case SYBMSTIME:
257 case SYBMSDATE:
258 case SYBMSDATETIME2:
259 case SYBMSDATETIMEOFFSET:
260 case SYBDATE:
261 case SYBTIME:
262 case SYB5BIGTIME:
263 case SYB5BIGDATETIME:
264 return odbc_convert_datetime_to_binary(stmt, curcol, srctype, (TDS_DATETIMEALL *) src, dest, destlen);
265 }
266
267 /* if destlen == 0 we return only length */
268 if (destlen > 0) {
269 size_t cplen = ODBC_MIN(destlen, srclen);
270 /* do not NUL terminate binary buffer */
271 memcpy(dest, src, cplen);
272 if (curcol)
273 curcol->column_text_sqlgetdatapos += cplen;
274 }
275 return ret;
276 }
277
278 static SQLLEN
odbc_tds2sql(TDS_STMT * stmt,TDSCOLUMN * curcol,int srctype,TDS_CHAR * src,TDS_UINT srclen,int desttype,TDS_CHAR * dest,SQLULEN destlen,const struct _drecord * drec_ixd)279 odbc_tds2sql(TDS_STMT * stmt, TDSCOLUMN *curcol, int srctype, TDS_CHAR * src, TDS_UINT srclen, int desttype, TDS_CHAR * dest, SQLULEN destlen,
280 const struct _drecord *drec_ixd)
281 {
282 TDS_INT nDestSybType;
283 TDS_INT nRetVal = TDS_CONVERT_FAIL;
284 TDSCONTEXT *context = stmt->dbc->env->tds_ctx;
285
286 CONV_RESULT ores;
287
288 SQLLEN ret = SQL_NULL_DATA;
289 int i, cplen;
290 int binary_conversion = 0;
291 TDS_CHAR conv_buf[256];
292
293 tdsdump_log(TDS_DBG_FUNC, "odbc_tds2sql: src is %d dest = %d\n", srctype, desttype);
294
295 assert(desttype != SQL_C_DEFAULT);
296
297 nDestSybType = odbc_c_to_server_type(desttype);
298 if (!nDestSybType) {
299 odbc_errs_add(&stmt->errs, "HY003", NULL);
300 return SQL_NULL_DATA;
301 }
302
303 /* special case for binary type */
304 if (desttype == SQL_C_BINARY) {
305 tdsdump_log(TDS_DBG_FUNC, "odbc_tds2sql: outputting binary data destlen = %lu \n", (unsigned long) destlen);
306
307 if (is_numeric_type(srctype)) {
308 desttype = SQL_C_NUMERIC;
309 nDestSybType = SYBNUMERIC;
310 /* prevent buffer overflow */
311 if (destlen < sizeof(SQL_NUMERIC_STRUCT)) {
312 odbc_errs_add(&stmt->errs, "07006", NULL);
313 return SQL_NULL_DATA;
314 }
315 ores.n.precision = ((TDS_NUMERIC *) src)->precision;
316 ores.n.scale = ((TDS_NUMERIC *) src)->scale;
317 } else {
318 return odbc_convert_to_binary(stmt, curcol, srctype, src, srclen, dest, destlen);
319 }
320 } else if (is_numeric_type(nDestSybType)) {
321 /* TODO use descriptor information (APD) ?? However APD can contain SQL_C_DEFAULT... */
322 if (drec_ixd)
323 ores.n.precision = drec_ixd->sql_desc_precision;
324 else
325 ores.n.precision = 38;
326 ores.n.scale = 0;
327 }
328
329 if (is_char_type(srctype)) {
330 if (desttype == SQL_C_CHAR || desttype == SQL_C_WCHAR)
331 return odbc_convert_char(stmt, curcol, src, srclen, desttype, dest, destlen);
332 if (is_unicode_type(srctype)) {
333 /*
334 * convert to single and then process normally.
335 * Here we processed SQL_C_BINARY and SQL_C_*CHAR so only fixed types are left
336 */
337 i = odbc_tds_convert_wide_iso(curcol, src, srclen, conv_buf, sizeof(conv_buf));
338 if (i < 0)
339 return SQL_NULL_DATA;
340 src = conv_buf;
341 srclen = i;
342 srctype = SYBVARCHAR;
343 }
344 }
345
346 if (desttype == SQL_C_WCHAR)
347 destlen /= sizeof(SQLWCHAR);
348 if (desttype == SQL_C_CHAR || desttype == SQL_C_WCHAR) {
349 if (is_binary_type(srctype)) {
350 binary_conversion = 1;
351 if (destlen && !(destlen % 2))
352 --destlen;
353 }
354
355 nDestSybType = TDS_CONVERT_CHAR;
356 ores.cc.len = destlen;
357 ores.cc.c = dest;
358 }
359
360 if (desttype == SQL_C_CHAR || desttype == SQL_C_WCHAR) {
361 char buf[48];
362 TDSDATEREC when;
363 int prec;
364 const char *fmt = NULL;
365 const TDS_DATETIMEALL *dta = (const TDS_DATETIMEALL *) src;
366
367 switch (srctype) {
368 case SYBMSDATETIMEOFFSET:
369 case SYBMSDATETIME2:
370 prec = dta->time_prec;
371 goto datetime;
372 case SYB5BIGDATETIME:
373 prec = 6;
374 goto datetime;
375 case SYBDATETIME:
376 prec = 3;
377 goto datetime;
378 case SYBDATETIME4:
379 prec = 0;
380 datetime:
381 fmt = "%Y-%m-%d %H:%M:%S.%z";
382 break;
383 case SYBMSTIME:
384 prec = dta->time_prec;
385 goto time;
386 case SYB5BIGTIME:
387 prec = 6;
388 goto time;
389 case SYBTIME:
390 prec = 3;
391 time:
392 fmt = "%H:%M:%S.%z";
393 break;
394 case SYBMSDATE:
395 case SYBDATE:
396 prec = 0;
397 fmt = "%Y-%m-%d";
398 break;
399 }
400 if (!fmt) goto normal_conversion;
401
402 tds_datecrack(srctype, src, &when);
403 tds_strftime(buf, sizeof(buf), fmt, &when, prec);
404
405 if (srctype == SYBMSDATETIMEOFFSET) {
406 char sign = '+';
407 int off = dta->offset;
408 if (off < 0) {
409 sign = '-';
410 off = -off;
411 }
412 sprintf(buf + strlen(buf), " %c%02d:%02d", sign, off / 60, off % 60);
413 }
414
415 nRetVal = strlen(buf);
416 memcpy(dest, buf, ODBC_MIN(destlen, nRetVal));
417 } else {
418 normal_conversion:
419 nRetVal = tds_convert(context, srctype, src, srclen, nDestSybType, &ores);
420 }
421 if (nRetVal < 0) {
422 odbc_convert_err_set(&stmt->errs, nRetVal);
423 return SQL_NULL_DATA;
424 }
425
426 switch (desttype) {
427
428 case SQL_C_CHAR:
429 tdsdump_log(TDS_DBG_FUNC, "odbc_tds2sql: outputting character data destlen = %lu \n", (unsigned long) destlen);
430
431 ret = nRetVal;
432 /* TODO handle not terminated configuration */
433 if (destlen > 0) {
434 cplen = ODBC_MIN(destlen - 1, nRetVal);
435 assert(cplen >= 0);
436 /*
437 * odbc always terminate but do not overwrite
438 * destination buffer more than needed
439 */
440 /* update datapos only for binary source (char already handled) */
441 if (curcol && binary_conversion)
442 curcol->column_text_sqlgetdatapos += cplen / 2;
443 dest[cplen] = 0;
444 } else {
445 /* if destlen == 0 we return only length */
446 }
447 break;
448
449 case SQL_C_WCHAR:
450 tdsdump_log(TDS_DBG_FUNC, "odbc_tds2sql: outputting character data destlen = %lu \n", (unsigned long) destlen);
451
452 ret = nRetVal * sizeof(SQLWCHAR);
453 /* TODO handle not terminated configuration */
454 if (destlen > 0) {
455 SQLWCHAR *wp = (SQLWCHAR *) dest;
456 SQLCHAR *p = (SQLCHAR *) dest;
457
458 cplen = ODBC_MIN(destlen - 1, nRetVal);
459 assert(cplen >= 0);
460 /*
461 * odbc always terminate but do not overwrite
462 * destination buffer more than needed
463 */
464 /* update datapos only for binary source (char already handled) */
465 if (curcol && binary_conversion)
466 curcol->column_text_sqlgetdatapos += cplen / 2;
467 /* convert in place and terminate */
468 wp[cplen] = 0;
469 while (cplen > 0) {
470 --cplen;
471 wp[cplen] = p[cplen];
472 }
473 } else {
474 /* if destlen == 0 we return only length */
475 }
476 break;
477
478 case SQL_C_TYPE_DATE:
479 case SQL_C_DATE:
480 {
481 TDSDATEREC dr;
482 DATE_STRUCT *dsp = (DATE_STRUCT *) dest;
483
484 /*
485 * we've already converted the returned value to a SYBMSDATETIME2
486 * now decompose date into constituent parts...
487 */
488 tds_datecrack(SYBMSDATETIME2, &(ores.dt), &dr);
489
490 dsp->year = dr.year;
491 dsp->month = dr.month + 1;
492 dsp->day = dr.day;
493
494 ret = sizeof(DATE_STRUCT);
495 }
496 break;
497
498 case SQL_C_TYPE_TIME:
499 case SQL_C_TIME:
500 {
501 TDSDATEREC dr;
502 TIME_STRUCT *tsp = (TIME_STRUCT *) dest;
503
504 /*
505 * we've already converted the returned value to a SYBMSDATETIME2
506 * now decompose date into constituent parts...
507 */
508 tds_datecrack(SYBMSDATETIME2, &(ores.dt), &dr);
509
510 tsp->hour = dr.hour;
511 tsp->minute = dr.minute;
512 tsp->second = dr.second;
513
514 ret = sizeof(TIME_STRUCT);
515 }
516 break;
517
518 case SQL_C_TYPE_TIMESTAMP:
519 case SQL_C_TIMESTAMP:
520 {
521 TDSDATEREC dr;
522 TIMESTAMP_STRUCT *tssp = (TIMESTAMP_STRUCT *) dest;
523
524 /*
525 * we've already converted the returned value to a SYBMSDATETIME2
526 * now decompose date into constituent parts...
527 */
528 tds_datecrack(SYBMSDATETIME2, &(ores.dt), &dr);
529
530 tssp->year = dr.year;
531 tssp->month = dr.month + 1;
532 tssp->day = dr.day;
533 tssp->hour = dr.hour;
534 tssp->minute = dr.minute;
535 tssp->second = dr.second;
536 tssp->fraction = dr.decimicrosecond * 100u;
537
538 ret = sizeof(TIMESTAMP_STRUCT);
539 }
540 break;
541
542 #ifdef SQL_C_SBIGINT
543 case SQL_C_SBIGINT:
544 case SQL_C_UBIGINT:
545 *((TDS_INT8 *) dest) = ores.bi;
546 ret = sizeof(TDS_INT8);
547 break;
548 #endif
549
550 case SQL_C_LONG:
551 case SQL_C_SLONG:
552 case SQL_C_ULONG:
553 *((TDS_INT *) dest) = ores.i;
554 ret = sizeof(TDS_INT);
555 break;
556
557 case SQL_C_SHORT:
558 case SQL_C_SSHORT:
559 case SQL_C_USHORT:
560 *((TDS_SMALLINT *) dest) = ores.si;
561 ret = sizeof(TDS_SMALLINT);
562 break;
563
564 case SQL_C_TINYINT:
565 case SQL_C_STINYINT:
566 case SQL_C_UTINYINT:
567 case SQL_C_BIT:
568 *((TDS_TINYINT *) dest) = ores.ti;
569 ret = sizeof(TDS_TINYINT);
570 break;
571
572 case SQL_C_DOUBLE:
573 *((TDS_FLOAT *) dest) = ores.f;
574 ret = sizeof(TDS_FLOAT);
575 break;
576
577 case SQL_C_FLOAT:
578 *((TDS_REAL *) dest) = ores.r;
579 ret = sizeof(TDS_REAL);
580 break;
581
582 case SQL_C_NUMERIC:
583 {
584 /* ODBC numeric is quite different from TDS one ... */
585 SQL_NUMERIC_STRUCT *num = (SQL_NUMERIC_STRUCT *) dest;
586 num->precision = ores.n.precision;
587 num->scale = ores.n.scale;
588 num->sign = ores.n.array[0] ^ 1;
589 /*
590 * TODO can be greater than SQL_MAX_NUMERIC_LEN ??
591 * seeing Sybase manual wire support bigger numeric but currently
592 * DBs so not support such precision
593 */
594 i = ODBC_MIN(tds_numeric_bytes_per_prec[ores.n.precision] - 1, SQL_MAX_NUMERIC_LEN);
595 memcpy(num->val, ores.n.array + 1, i);
596 tds_swap_bytes(num->val, i);
597 if (i < SQL_MAX_NUMERIC_LEN)
598 memset(num->val + i, 0, SQL_MAX_NUMERIC_LEN - i);
599 ret = sizeof(SQL_NUMERIC_STRUCT);
600 }
601 break;
602
603 #ifdef SQL_C_GUID
604 case SQL_C_GUID:
605 memcpy(dest, &(ores.u), sizeof(TDS_UNIQUE));
606 ret = sizeof(TDS_UNIQUE);
607 break;
608 #endif
609
610 default:
611 break;
612 }
613
614 return ret;
615 }
616
odbc_tds2sql_col(TDS_STMT * stmt,TDSCOLUMN * curcol,int desttype,TDS_CHAR * dest,SQLULEN destlen,const struct _drecord * drec_ixd)617 SQLLEN odbc_tds2sql_col(TDS_STMT * stmt, TDSCOLUMN *curcol, int desttype, TDS_CHAR * dest, SQLULEN destlen,
618 const struct _drecord *drec_ixd)
619 {
620 int srctype = tds_get_conversion_type(curcol->on_server.column_type, curcol->on_server.column_size);
621 TDS_CHAR *src = (TDS_CHAR *) curcol->column_data;
622 TDS_UINT srclen = curcol->column_cur_size;
623
624 if (is_blob_col(curcol)) {
625 if (srctype == SYBLONGBINARY && (
626 curcol->column_usertype == USER_UNICHAR_TYPE ||
627 curcol->column_usertype == USER_UNIVARCHAR_TYPE))
628 srctype = SYBNTEXT;
629 if (curcol->column_type == SYBVARIANT)
630 srctype = ((TDSVARIANT *) src)->type;
631 src = ((TDSBLOB *) src)->textvalue;
632 }
633 if (is_variable_type(srctype)) {
634 src += curcol->column_text_sqlgetdatapos;
635 srclen -= curcol->column_text_sqlgetdatapos;
636 }
637 return odbc_tds2sql(stmt, curcol, srctype, src, srclen, desttype, dest, destlen, drec_ixd);
638 }
639
odbc_tds2sql_int4(TDS_STMT * stmt,TDS_INT * src,int desttype,TDS_CHAR * dest,SQLULEN destlen)640 SQLLEN odbc_tds2sql_int4(TDS_STMT * stmt, TDS_INT *src, int desttype, TDS_CHAR * dest, SQLULEN destlen)
641 {
642 return odbc_tds2sql(stmt, NULL, SYBINT4, (TDS_CHAR *) src, sizeof(*src),
643 desttype, dest, destlen, NULL);
644 }
645