File freetds-0.82-odbc-csa2.patch of Package freetds

diff -dPNur freetds-0.82/include/tds.h freetds-0.82-new/include/tds.h
--- freetds-0.82/include/tds.h	2007-12-27 14:45:22.000000000 +0100
+++ freetds-0.82-new/include/tds.h	2008-07-02 22:32:56.000000000 +0200
@@ -1005,6 +1005,7 @@
 	TDS_INT *column_lenbind;
 	TDS_INT column_textpos;
 	TDS_INT column_text_sqlgetdatapos;
+	TDS_CHAR column_text_sqlputdatainfo;
 
 	BCPCOLDATA *bcp_column_data;
 	/**
diff -dPNur freetds-0.82/src/odbc/odbc.c freetds-0.82-new/src/odbc/odbc.c
--- freetds-0.82/src/odbc/odbc.c	2008-05-06 04:57:26.000000000 +0200
+++ freetds-0.82-new/src/odbc/odbc.c	2008-07-02 22:32:56.000000000 +0200
@@ -4564,6 +4564,9 @@
 	SQLLEN dummy_cb;
 	int nSybType;
 
+	TDS_INT converted_column_cur_size;
+	int extra_bytes = 0;
+
 	INIT_HSTMT;
 
 	tdsdump_log(TDS_DBG_FUNC, "SQLGetData(%p, %u, %d, %p, %d, %p)\n", 
@@ -4597,46 +4600,120 @@
 		ODBC_RETURN(stmt, SQL_ERROR);
 	}
 	colinfo = resinfo->columns[icol - 1];
+	converted_column_cur_size = colinfo->column_cur_size;
 
 	if (colinfo->column_cur_size < 0) {
 		*pcbValue = SQL_NULL_DATA;
 	} else {
+		nSybType = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
+		if (fCType == SQL_C_DEFAULT)
+			fCType = odbc_sql_to_c_type_default(stmt->ird->records[icol - 1].sql_desc_concise_type);
+		if (fCType == SQL_ARD_TYPE) {
+			if (icol > stmt->ard->header.sql_desc_count) {
+				odbc_errs_add(&stmt->errs, "07009", NULL);
+				ODBC_RETURN(stmt, SQL_ERROR);
+			}
+			fCType = stmt->ard->records[icol - 1].sql_desc_concise_type;
+		}
+		assert(fCType);
+
 		src = (TDS_CHAR *) colinfo->column_data;
 		if (is_variable_type(colinfo->column_type)) {
-			if (colinfo->column_text_sqlgetdatapos > 0
-			    && colinfo->column_text_sqlgetdatapos >= colinfo->column_cur_size)
-				ODBC_RETURN(stmt, SQL_NO_DATA);
-
+			int nread = 0;
+			
 			/* 2003-8-29 check for an old bug -- freddy77 */
 			assert(colinfo->column_text_sqlgetdatapos >= 0);
 			if (is_blob_type(colinfo->column_type))
 				src = ((TDSBLOB *) src)->textvalue;
-			src += colinfo->column_text_sqlgetdatapos;
-			srclen = colinfo->column_cur_size - colinfo->column_text_sqlgetdatapos;
+
+			if (fCType == SQL_C_CHAR) {
+				TDS_CHAR buf[3];
+				SQLLEN len;
+
+				switch (nSybType) {
+				case SYBLONGBINARY:
+				case SYBBINARY:
+				case SYBVARBINARY:
+				case SYBIMAGE:
+				case XSYBBINARY:
+				case XSYBVARBINARY:
+				case TDS_CONVERT_BINARY:
+					if (colinfo->column_text_sqlgetdatapos % 2) {
+						nread = (colinfo->column_text_sqlgetdatapos - 1) / 2;
+						if (nread >= colinfo->column_cur_size)
+							ODBC_RETURN(stmt, SQL_NO_DATA);
+						
+						if (cbValueMax > 2) {
+							len = convert_tds2sql(context, nSybType, src + nread, 1, fCType, buf, sizeof(buf), NULL);
+							if (len < 2) {
+								if (len < 0) 
+									odbc_convert_err_set(&stmt->errs, len);
+								ODBC_RETURN(stmt, SQL_ERROR);
+							}
+							*(TDS_CHAR *) rgbValue = buf[1];
+							*((TDS_CHAR *) rgbValue + 1) = 0;
+						
+							rgbValue++;
+							cbValueMax--;
+						
+							extra_bytes = 1;
+							nread++;
+
+							if (nread >= colinfo->column_cur_size)
+								ODBC_RETURN_(stmt);
+						} else {
+							if (cbValueMax) 
+								*(TDS_CHAR *) rgbValue = 0;
+							odbc_errs_add(&stmt->errs, "01004", "String data, right truncated");
+							ODBC_RETURN(stmt, SQL_SUCCESS_WITH_INFO);
+						}
+					} else {
+						nread = colinfo->column_text_sqlgetdatapos / 2;
+						
+						if (colinfo->column_text_sqlgetdatapos > 0
+						    && nread >= colinfo->column_cur_size)
+							ODBC_RETURN(stmt, SQL_NO_DATA);
+					}
+					
+					src += nread;
+					srclen = colinfo->column_cur_size - nread;
+					converted_column_cur_size *= 2;
+					break;
+				default:
+					if (colinfo->column_text_sqlgetdatapos > 0
+					&&  colinfo->column_text_sqlgetdatapos >= colinfo->column_cur_size)
+						ODBC_RETURN(stmt, SQL_NO_DATA);
+						
+					src += colinfo->column_text_sqlgetdatapos;
+					srclen = colinfo->column_cur_size - colinfo->column_text_sqlgetdatapos;
+				}
+			} else {
+				if (colinfo->column_text_sqlgetdatapos > 0
+				&&  colinfo->column_text_sqlgetdatapos >= colinfo->column_cur_size)
+					ODBC_RETURN(stmt, SQL_NO_DATA);
+
+				src += colinfo->column_text_sqlgetdatapos;
+				srclen = colinfo->column_cur_size - colinfo->column_text_sqlgetdatapos;
+			}
 		} else {
 			if (colinfo->column_text_sqlgetdatapos > 0
-			    && colinfo->column_text_sqlgetdatapos >= colinfo->column_cur_size)
+			&&  colinfo->column_text_sqlgetdatapos >= colinfo->column_cur_size)
 				ODBC_RETURN(stmt, SQL_NO_DATA);
 
 			srclen = colinfo->column_cur_size;
 		}
-		nSybType = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
-		if (fCType == SQL_C_DEFAULT)
-			fCType = odbc_sql_to_c_type_default(stmt->ird->records[icol - 1].sql_desc_concise_type);
-		if (fCType == SQL_ARD_TYPE) {
-			if (icol > stmt->ard->header.sql_desc_count) {
-				odbc_errs_add(&stmt->errs, "07009", NULL);
-				ODBC_RETURN(stmt, SQL_ERROR);
-			}
-			fCType = stmt->ard->records[icol - 1].sql_desc_concise_type;
-		}
-		assert(fCType);
+
 		*pcbValue = convert_tds2sql(context, nSybType, src, srclen, fCType, (TDS_CHAR *) rgbValue, cbValueMax, NULL);
 		if (*pcbValue < 0) {
 			odbc_convert_err_set(&stmt->errs, *pcbValue);
 			ODBC_RETURN(stmt, SQL_ERROR);
 		}
-
+		
+		if (extra_bytes) {
+			colinfo->column_text_sqlgetdatapos += extra_bytes;
+			*pcbValue += extra_bytes;
+		}
+		
 		if (is_variable_type(colinfo->column_type) && (fCType == SQL_C_CHAR || fCType == SQL_C_BINARY)) {
 			/* calc how many bytes was readed */
 			int readed = cbValueMax;
@@ -4651,7 +4728,7 @@
 			if (colinfo->column_text_sqlgetdatapos == 0 && cbValueMax > 0)
 				++colinfo->column_text_sqlgetdatapos;
 			/* not all readed ?? */
-			if (colinfo->column_text_sqlgetdatapos < colinfo->column_cur_size) {
+			if (colinfo->column_text_sqlgetdatapos < converted_column_cur_size) {
 				odbc_errs_add(&stmt->errs, "01004", "String data, right truncated");
 				ODBC_RETURN(stmt, SQL_SUCCESS_WITH_INFO);
 			}
diff -dPNur freetds-0.82/src/odbc/prepare_query.c freetds-0.82-new/src/odbc/prepare_query.c
--- freetds-0.82/src/odbc/prepare_query.c	2007-04-18 16:29:24.000000000 +0200
+++ freetds-0.82-new/src/odbc/prepare_query.c	2008-07-02 22:32:56.000000000 +0200
@@ -275,21 +275,110 @@
 	/* copy to destination */
 	if (blob) {
 		TDS_CHAR *p;
+		int dest_type, src_type, sql_src_type, res;
+		CONV_RESULT ores;
+		TDS_DBC * dbc = stmt->dbc;
+		void *free_ptr = NULL;
+		int start = 0;
+		SQLPOINTER extradata = NULL;
+		SQLLEN extralen = 0;
+		
+
+		dest_type = odbc_sql_to_server_type(dbc->tds_socket, drec_ipd->sql_desc_concise_type);
+		if (dest_type == TDS_FAIL)
+			return SQL_ERROR;
+			
+		/* get C type */
+		sql_src_type = drec_apd->sql_desc_concise_type;
+		if (sql_src_type == SQL_C_DEFAULT)
+			sql_src_type = odbc_sql_to_c_type_default(drec_ipd->sql_desc_concise_type);
+
+		/* test source type */
+		/* TODO test intervals */
+		src_type = odbc_c_to_server_type(sql_src_type);
+		if (src_type == TDS_FAIL)
+			return SQL_ERROR;
+		
+		if (sql_src_type == SQL_C_CHAR) {
+			switch (tds_get_conversion_type(curcol->column_type, curcol->column_size)) {
+			case SYBBINARY:
+			case SYBVARBINARY:
+			case XSYBBINARY:
+			case XSYBVARBINARY:
+			case SYBLONGBINARY:
+			case SYBIMAGE:
+				if (!*((char*)DataPtr+len-1))
+					--len;
+					
+				if (!len)
+					return SQL_SUCCESS;
+					
+				if (curcol->column_cur_size > 0
+				&&  curcol->column_text_sqlputdatainfo) {
+					TDS_CHAR data[2];
+					data[0] = curcol->column_text_sqlputdatainfo;
+					data[1] = *(char*)DataPtr;
+				    
+					res = tds_convert(dbc->env->tds_ctx, src_type, data, 2, dest_type, &ores);
+					if (res < 0)
+						return SQL_ERROR;
+				    
+					extradata = ores.ib;
+					extralen = res;
+					
+					start = 1;
+					--len;
+				}
+				
+			        if (len&1) {
+					--len;
+					curcol->column_text_sqlputdatainfo = *((char*)DataPtr+len);
+				}
+
+				res = tds_convert(dbc->env->tds_ctx, src_type, DataPtr+start, len, dest_type, &ores);
+				if (res < 0) {
+					if (extradata)
+						free(extradata);
+						
+					return SQL_ERROR;
+				}
+			    
+				DataPtr = free_ptr = ores.ib;
+				len = res;
+				break;
+			}
+		}
 
 		if (blob->textvalue)
-			p = (TDS_CHAR *) realloc(blob->textvalue, len + curcol->column_cur_size);
+			p = (TDS_CHAR *) realloc(blob->textvalue, len + extralen + curcol->column_cur_size);
 		else {
 			assert(curcol->column_cur_size == 0);
-			p = (TDS_CHAR *) malloc(len);
+			p = (TDS_CHAR *) malloc(len + extralen);
 		}
-		if (!p)
+		if (!p) {
+			if (free_ptr)
+				free(free_ptr);
+			if (extradata)
+				free(extradata);
 			return SQL_ERROR;
+		}
 		blob->textvalue = p;
+		if (extralen) {
+			memcpy(blob->textvalue + curcol->column_cur_size, extradata, extralen);
+			curcol->column_cur_size += extralen;
+		}
 		memcpy(blob->textvalue + curcol->column_cur_size, DataPtr, len);
+		
+		if (extradata)
+			free(extradata);
+		if (free_ptr)
+			free(free_ptr);
 	} else {
 		memcpy(curcol->column_data + curcol->column_cur_size, DataPtr, len);
 	}
+	
 	curcol->column_cur_size += len;
+	
 	if (blob && curcol->column_cur_size > curcol->column_size)
 		curcol->column_size = curcol->column_cur_size;
 
diff -dPNur freetds-0.82/src/odbc/unittests/blob1.c freetds-0.82-new/src/odbc/unittests/blob1.c
--- freetds-0.82/src/odbc/unittests/blob1.c	2008-01-12 01:21:39.000000000 +0100
+++ freetds-0.82-new/src/odbc/unittests/blob1.c	2008-07-02 22:32:56.000000000 +0200
@@ -47,6 +47,16 @@
 		buf[n] = 'a' + ((start+n) * step % ('z' - 'a' + 1));
 }
 
+static void
+fill_hex(char *buf, size_t len, unsigned int start, unsigned int step)
+{
+	size_t n;
+
+	for (n = 0; n < len; ++n)
+		sprintf(buf + 2*n, "%2x", (unsigned int)('a' + ((start+n) * step % ('z' - 'a' + 1))));
+}
+
+
 static int
 check_chars(const char *buf, size_t len, unsigned int start, unsigned int step)
 {
@@ -60,6 +70,21 @@
 }
 
 static int
+check_hex(const char *buf, size_t len, unsigned int start, unsigned int step)
+{
+	size_t n;
+	char symbol[3];
+
+	for (n = 0; n < len; ++n) {
+		sprintf(symbol, "%2x", (unsigned int)('a' + ((start+n) / 2 * step % ('z' - 'a' + 1))));
+		if (buf[n] != symbol[(start+n) % 2])
+			return 0;
+	}
+
+	return 1;
+}
+
+static int
 readBlob(SQLHSTMT * stmth, SQLUSMALLINT pos)
 {
 	SQLRETURN rcode;
@@ -93,6 +118,43 @@
 	return rcode;
 }
 
+static int
+readBlobAsChar(SQLHSTMT * stmth, SQLUSMALLINT pos, int step)
+{
+	SQLRETURN rcode = SQL_SUCCESS_WITH_INFO;
+	char buf[8192];
+	SQLLEN len, total = 0;
+	int i = 0;
+	int check;
+	int bufsize;
+	
+	if (step%2) bufsize = sizeof(buf) - 1;
+	else bufsize = sizeof(buf);
+
+	printf(">> readBlobAsChar field %d\n", pos);
+	while (rcode == SQL_SUCCESS_WITH_INFO) {
+		i++;
+		rcode = SQLGetData(stmth, pos, SQL_C_CHAR, (SQLPOINTER) buf, (SQLINTEGER) bufsize, &len);
+		if (!SQL_SUCCEEDED(rcode) || len <= 0)
+			break;
+		if (len > (SQLLEN) bufsize)
+			len = (SQLLEN) bufsize - 1;
+		printf(">>     step %d: %d bytes readed\n", i, (int) len);
+		
+		check =	check_hex(buf, len, 2*987 + total, 25);
+		if (!check) {
+			fprintf(stderr, "Wrong buffer content\n");
+			failed = 1;
+		}
+		total += len;
+	}
+	printf(">>   total bytes read = %d \n", (int) total);
+	if (total != 20000)
+		failed = 1;
+	return rcode;
+}
+
+
 int
 main(int argc, char **argv)
 {
@@ -106,12 +168,14 @@
 	SQLLEN vind1;
 	char buf2[NBYTES];
 	SQLLEN vind2;
+	char buf3[NBYTES*2 + 1];
+	SQLLEN vind3;
 	int cnt = 2;
 
 	use_odbc_version3 = 1;
 	Connect();
 
-	Command(Statement, "CREATE TABLE #tt ( k INT, t TEXT, b IMAGE, v INT )");
+	Command(Statement, "CREATE TABLE #tt ( k INT, t TEXT, b1 IMAGE, b2 IMAGE, v INT )");
 
 	/* Insert rows ... */
 
@@ -121,7 +185,7 @@
 		rcode = SQLAllocHandle(SQL_HANDLE_STMT, Connection, &m_hstmt);
 		CHECK_RCODE(SQL_HANDLE_DBC, Connection, "SQLAllocHandle StmtH");
 
-		rcode = SQLPrepare(m_hstmt, (SQLCHAR *) "INSERT INTO #tt VALUES ( ?, ?, ?, ? )", SQL_NTS);
+		rcode = SQLPrepare(m_hstmt, (SQLCHAR *) "INSERT INTO #tt VALUES ( ?, ?, ?, ?, ? )", SQL_NTS);
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLPrepare");
 
 		SQLBindParameter(m_hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &key, 0, &vind0);
@@ -133,9 +197,12 @@
 		SQLBindParameter(m_hstmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0x10000000, 0, buf2, 0, &vind2);
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLBindParameter 3");
 
-		SQLBindParameter(m_hstmt, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &key, 0, &vind0);
+		SQLBindParameter(m_hstmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARBINARY, 0x10000000, 0, buf3, 0, &vind3);
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLBindParameter 4");
 
+		SQLBindParameter(m_hstmt, 5, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &key, 0, &vind0);
+		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLBindParameter 5");
+
 		key = i;
 		vind0 = 0;
 
@@ -144,6 +211,10 @@
 
 		fill_chars(buf2, NBYTES, 987, 25);
 		vind2 = SQL_LEN_DATA_AT_EXEC(NBYTES);
+		
+		memset(buf3, 0, sizeof(buf3));
+		vind3 = SQL_LEN_DATA_AT_EXEC(2*NBYTES+1);
+		
 
 		printf(">> insert... %d\n", i);
 		rcode = SQLExecute(m_hstmt);
@@ -155,10 +226,25 @@
 			CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLParamData StmtH");
 			printf(">> SQLParamData: ptr = %p  rcode = %d\n", (void *) p, rcode);
 			if (rcode == SQL_NEED_DATA) {
-				SQLRETURN rcode = SQLPutData(m_hstmt, p, NBYTES);
+				SQLRETURN rcode;
+				if (p == buf3) {
+					fill_hex(buf3, NBYTES, 987, 25);
+					
+					rcode = SQLPutData(m_hstmt, p, NBYTES - (i&1));
 
-				CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLPutData StmtH");
-				printf(">> param %p: total bytes written = %d\n", (void *) p, NBYTES);
+					CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLPutData StmtH");
+					printf(">> param %p: total bytes written = %d\n", (void *) p, NBYTES - (i&1));
+					
+					rcode = SQLPutData(m_hstmt, p + NBYTES - (i&1), NBYTES + (i&1));
+
+					CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLPutData StmtH");
+					printf(">> param %p: total bytes written = %d\n", (void *) p, NBYTES + (i&1));
+				} else {
+					rcode = SQLPutData(m_hstmt, p, NBYTES);
+
+					CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLPutData StmtH");
+					printf(">> param %p: total bytes written = %d\n", (void *) p, NBYTES);
+				}
 			}
 		}
 
@@ -182,7 +268,7 @@
 			CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLSetStmtAttr SQL_ATTR_CURSOR_SENSITIVITY");
 		}
 
-		rcode = SQLPrepare(m_hstmt, (SQLCHAR *) "SELECT t, b, v FROM #tt WHERE k = ?", SQL_NTS);
+		rcode = SQLPrepare(m_hstmt, (SQLCHAR *) "SELECT t, b1, b2, v FROM #tt WHERE k = ?", SQL_NTS);
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLPrepare");
 
 		SQLBindParameter(m_hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &i, 0, &vind0);
@@ -192,7 +278,9 @@
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLBindCol 2");
 		SQLBindCol(m_hstmt, 2, SQL_C_BINARY, NULL, 0, &vind2);
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLBindCol 3");
-		SQLBindCol(m_hstmt, 3, SQL_C_LONG, &key, 0, &vind0);
+		SQLBindCol(m_hstmt, 3, SQL_C_BINARY, NULL, 0, &vind3);
+		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLBindCol 4");
+		SQLBindCol(m_hstmt, 4, SQL_C_LONG, &key, 0, &vind0);
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLBindCol 1");
 
 		vind0 = 0;
@@ -210,6 +298,8 @@
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "readBlob 1");
 		rcode = readBlob(m_hstmt, 2);
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "readBlob 2");
+		rcode = readBlobAsChar(m_hstmt, 3, i);
+		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "readBlob 3 as SQL_C_CHAR");
 
 		rcode = SQLCloseCursor(m_hstmt);
 		CHECK_RCODE(SQL_HANDLE_STMT, m_hstmt, "SQLCloseCursor StmtH");
openSUSE Build Service is sponsored by