diff -dPNur freetds/include/tds.h freetds-ds/include/tds.h --- freetds/include/tds.h 2008-06-18 11:06:26.000000000 +0200 +++ freetds-ds/include/tds.h 2008-07-02 22:10:03.000000000 +0200 @@ -1009,6 +1009,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/src/odbc/odbc.c freetds-ds/src/odbc/odbc.c --- freetds/src/odbc/odbc.c 2008-06-18 11:06:26.000000000 +0200 +++ freetds-ds/src/odbc/odbc.c 2008-07-02 22:10:03.000000000 +0200 @@ -4652,6 +4652,7 @@ SQLLEN dummy_cb; int nSybType; + TDS_INT converted_column_cur_size; int extra_bytes = 0; INIT_HSTMT; @@ -4690,6 +4691,7 @@ ODBC_RETURN(stmt, SQL_ERROR); } colinfo = tds->current_results->columns[icol - 1]; + converted_column_cur_size = colinfo->column_cur_size; if (colinfo->column_cur_size < 0) { *pcbValue = SQL_NULL_DATA; @@ -4715,7 +4717,7 @@ if (is_blob_type(colinfo->column_type)) src = ((TDSBLOB *) src)->textvalue; - if (fCType == SQL_C_CHAR && colinfo->column_text_sqlgetdatapos) { + if (fCType == SQL_C_CHAR) { TDS_CHAR buf[3]; SQLLEN len; @@ -4758,40 +4760,15 @@ } } else { nread = colinfo->column_text_sqlgetdatapos / 2; - if (nread >= colinfo->column_cur_size) + + 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; - break; - default: - if (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 (fCType == SQL_C_BINARY) { - switch (nSybType) { - case SYBCHAR: - case SYBVARCHAR: - case SYBTEXT: - case XSYBCHAR: - case XSYBVARCHAR: - nread = (src[0] == '0' && toupper(src[1]) == 'X')? 2 : 0; - - while ((nread < colinfo->column_cur_size) && (src[nread] == ' ' || src[nread] == '\0')) - nread++; - - nread += colinfo->column_text_sqlgetdatapos * 2; - - if (nread && 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 @@ -4842,7 +4819,7 @@ if (colinfo->column_text_sqlgetdatapos == 0 && cbValueMax > 0) ++colinfo->column_text_sqlgetdatapos; - if (colinfo->column_text_sqlgetdatapos < colinfo->column_cur_size) { /* not all read ?? */ + if (colinfo->column_text_sqlgetdatapos < converted_column_cur_size) { /* not all read ?? */ odbc_errs_add(&stmt->errs, "01004", "String data, right truncated"); ODBC_RETURN(stmt, SQL_SUCCESS_WITH_INFO); } diff -dPNur freetds/src/odbc/prepare_query.c freetds-ds/src/odbc/prepare_query.c --- freetds/src/odbc/prepare_query.c 2007-04-18 16:29:24.000000000 +0200 +++ freetds-ds/src/odbc/prepare_query.c 2008-07-02 22:10:03.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/src/odbc/unittests/blob1.c freetds-ds/src/odbc/unittests/blob1.c --- freetds/src/odbc/unittests/blob1.c 2008-01-12 01:14:11.000000000 +0100 +++ freetds-ds/src/odbc/unittests/blob1.c 2008-07-02 22:09:28.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");