Index: driver/results.c =================================================================== --- driver/results.c (revision 1081) +++ driver/results.c (working copy) @@ -44,6 +44,34 @@ } +bool isNumericCType( SQLSMALLINT cType) +{ + return (cType != SQL_C_CHAR + && cType != SQL_C_BINARY + && cType != SQL_C_WCHAR + /* well, SQL_C_BIT is rather binary and 1 byte anyway. */ + && cType != SQL_C_BIT); +} + +void binary2numeric(UINT64 *dst, char *src, uint srcLen) +{ + *dst=0; + + while (srcLen) + { + *dst+= (0xff & *src++) << (--srcLen)*8; + } +} + +BOOL anyNonZeroByte(char *value, uint length) +{ + for(;length;--length,++value) + if (*value != '\0') + return TRUE; + + return FALSE; +} + /** Retrieve the data from a field as a specified ODBC C type. @@ -66,6 +94,8 @@ char *value, uint length, DESCREC *arrec) { SQLLEN tmp; + UINT64 numericValue; + my_bool convert= 1; /* get the exact type if we don't already have it */ if (fCType == SQL_C_DEFAULT) @@ -111,6 +141,22 @@ if (!pcbValue) pcbValue= &tmp; /* Easier code */ + if (field->type == MYSQL_TYPE_BIT) + { + if (isNumericCType(fCType)) + { + binary2numeric(&numericValue, value, length); + convert= 0; + } + else + { + memset(rgbValue,0, cbValueMax > length ? length + 1 : cbValueMax ); + + return copy_binary_result(stmt, (SQLCHAR *)rgbValue, cbValueMax + , pcbValue, field , value , length); + } + } + switch (fCType) { case SQL_C_CHAR: /* Handle BLOB -> CHAR conversion */ @@ -190,10 +236,11 @@ cbValueMax / sizeof(SQLWCHAR), pcbValue, field, value, length); + /* This remains only for non-bit fields*/ case SQL_C_BIT: if (rgbValue) { - if (value[0] == 1 || !atoi(value)) + if (value[0] == 1 || anyNonZeroByte(value, length)) *((char *)rgbValue)= 1; else *((char *)rgbValue)= 0; @@ -204,26 +251,34 @@ case SQL_C_TINYINT: case SQL_C_STINYINT: if (rgbValue) - *((SQLSCHAR *)rgbValue)= (SQLSCHAR)atoi(value); + *((SQLSCHAR *)rgbValue)= (SQLSCHAR)(convert + ? atoi(value) + : (numericValue & (SQLSCHAR)(-1))); *pcbValue= 1; break; case SQL_C_UTINYINT: if (rgbValue) - *((SQLCHAR *)rgbValue)= (SQLCHAR)(unsigned int)atoi(value); + *((SQLCHAR *)rgbValue)= (SQLCHAR)(unsigned int)(convert + ? atoi(value) + : (numericValue & (SQLCHAR)(-1))); *pcbValue= 1; break; case SQL_C_SHORT: case SQL_C_SSHORT: if (rgbValue) - *((SQLSMALLINT *)rgbValue)= (SQLSMALLINT)atoi(value); + *((SQLSMALLINT *)rgbValue)= (SQLSMALLINT)(convert + ? atoi(value) + : (numericValue & (SQLUSMALLINT)(-1))); *pcbValue= sizeof(SQLSMALLINT); break; case SQL_C_USHORT: if (rgbValue) - *((SQLUSMALLINT *)rgbValue)= (SQLUSMALLINT)(uint)atol(value); + *((SQLUSMALLINT *)rgbValue)= (SQLUSMALLINT)(uint)(convert + ? atol(value) + : (numericValue & (SQLUSMALLINT)(-1))); *pcbValue= sizeof(SQLUSMALLINT); break; @@ -232,34 +287,41 @@ if (rgbValue) { /* Check if it could be a date...... :) */ - if (length >= 10 && value[4] == '-' && value[7] == '-' && - (!value[10] || value[10] == ' ')) - { - *((SQLINTEGER *)rgbValue)= ((SQLINTEGER) atol(value) * 10000L + - (SQLINTEGER) atol(value + 5) * 100L + - (SQLINTEGER) atol(value + 8)); - } + if (convert) + if (length >= 10 && value[4] == '-' && value[7] == '-' && + (!value[10] || value[10] == ' ')) + { + *((SQLINTEGER *)rgbValue)= ((SQLINTEGER) atol(value) * 10000L + + (SQLINTEGER) atol(value + 5) * 100L + + (SQLINTEGER) atol(value + 8)); + } + else + *((SQLINTEGER *)rgbValue)= (SQLINTEGER) atol(value); else - *((SQLINTEGER *)rgbValue)= (SQLINTEGER) atol(value); + *((SQLINTEGER *)rgbValue)= (SQLINTEGER)(numericValue + & (SQLUINTEGER)(-1)); } *pcbValue= sizeof(SQLINTEGER); break; case SQL_C_ULONG: if (rgbValue) - *((SQLUINTEGER *)rgbValue)= (SQLUINTEGER)strtoul(value, NULL, 10); + *((SQLUINTEGER *)rgbValue)= (SQLUINTEGER)(convert + ? strtoul(value, NULL, 10) + : numericValue & (SQLUINTEGER)(-1)); *pcbValue= sizeof(SQLUINTEGER); break; case SQL_C_FLOAT: if (rgbValue) - *((float *)rgbValue)= (float)atof(value); + *((float *)rgbValue)= (float)(convert ? atof(value) + : numericValue & (int)(-1)); *pcbValue= sizeof(float); break; case SQL_C_DOUBLE: if (rgbValue) - *((double *)rgbValue)= (double)strtod(value, NULL); + *((double *)rgbValue)= (double)(convert ? strtod(value, NULL) : numericValue); *pcbValue= sizeof(double); break; @@ -369,14 +431,17 @@ case SQL_C_SBIGINT: /** @todo This is not right. SQLBIGINT is not always longlong. */ if (rgbValue) - *((longlong *)rgbValue)= (longlong)strtoll(value, NULL, 10); + *((longlong *)rgbValue)= (longlong)(convert ? strtoll(value, NULL, 10) + : numericValue); *pcbValue= sizeof(longlong); break; case SQL_C_UBIGINT: /** @todo This is not right. SQLUBIGINT is not always ulonglong. */ if (rgbValue) - *((ulonglong *)rgbValue)= (ulonglong)strtoull(value, NULL, 10); + *((ulonglong *)rgbValue)= (ulonglong)(convert + ? strtoull(value, NULL, 10) + : numericValue); *pcbValue= sizeof(ulonglong); break; Index: test/my_result.c =================================================================== --- test/my_result.c (revision 1081) +++ test/my_result.c (working copy) @@ -2332,7 +2332,6 @@ { SQLCHAR buff[10]; SQLLEN len= 0; - SQLSMALLINT namelen, type, digits, nullable; ok_stmt(hstmt, SQLPrepare(hstmt, (SQLCHAR *) "SELECT ?", SQL_NTS)); strcpy((char *)buff, "2.0"); @@ -2365,8 +2364,72 @@ return OK; } +/* + Bug#32821(it might be duplicate though): Wrong value if bit field is bound to + other than SQL_C_BIT variable +*/ +DECLARE_TEST(t_bug32821) +{ + SQLRETURN rc; + SQLUINTEGER b; + SQLUSMALLINT c; + SQLINTEGER a_ind, b_ind, c_ind, i; + unsigned char a; + SQLUINTEGER par= sizeof(SQLUSMALLINT)*8+1; + SQLUINTEGER beoyndShortBit= 1<<(par-1); + SQLINTEGER sPar= sizeof(SQLUINTEGER); + + /* 131071 = 0x1ffff - all 1 for field c*/ + const SQLCHAR * insStmt= "insert into t_bug32821 values (0,0,0),(1,1,1)\ + ,(1,255,131071),(1,258,?)"; + const unsigned char expected_a[]= {'\0', '\1', '\1', '\1'}; + const SQLINTEGER expected_b[]= {0L, 1L, 255L, 258L}; + const SQLUSMALLINT expected_c[]= {0, 1, 65535, 0}; + + ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug32821"); + + ok_stmt(hstmt, SQLPrepare(hstmt, "CREATE TABLE t_bug32821 (a BIT(1), b BIT(16)\ + , c BIT(?))", SQL_NTS)); + ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG + , SQL_INTEGER, 0, 0, &par, 0, &sPar )); + ok_stmt(hstmt, SQLExecute(hstmt)); + + ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); + + ok_stmt(hstmt, SQLPrepare(hstmt, insStmt, SQL_NTS)); + ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG + , SQL_INTEGER, 0, 0, &beoyndShortBit, 0 + , &sPar )); + ok_stmt(hstmt, SQLExecute(hstmt)); + + ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); + + ok_sql(hstmt, "SELECT a,b,c FROM t_bug32821"); + + ok_stmt( hstmt, SQLBindCol( hstmt, 1, SQL_C_BIT, &a, 0, &a_ind ) ); + ok_stmt( hstmt, SQLBindCol( hstmt, 2, SQL_C_ULONG, &b, 0, &b_ind ) ); + ok_stmt( hstmt, SQLBindCol( hstmt, 3, SQL_C_USHORT, &c, 0, &c_ind ) ); + + i= 0; + while( (rc= SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0)) != SQL_NO_DATA_FOUND) + { + /*printMessage("testing row #%d", i+1);*/ + is_num(a, expected_a[i]); + is_num(b, expected_b[i]); + is_num(c, expected_c[i]); + ++i; + } + + ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); + + ok_sql(hstmt, "DROP TABLE IF EXISTS t_bug32821"); + return OK; +} + + BEGIN_TESTS + ADD_TEST(my_resultset) ADD_TEST(t_convert_type) ADD_TEST(t_desc_col) @@ -2401,6 +2464,7 @@ ADD_TEST(t_bug34429) ADD_TEST(t_bug32420) ADD_TEST(t_bug34575) + ADD_TEST(t_bug32821) END_TESTS