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");