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