Skip to content

Commit b5454bd

Browse files
committed
Fix: string and floats conversion fixes (#117)
* fix wide to C string conversion truncation indic. In case of SQLWCHAR to SQLCHAR conversion the truncation indication was wrongly signaled (the conversion was performed correctly, though). This happened because of the wrong number of bytes compared against available buffer: the SQLWCHAR string space instead of the (correct) converted equivalent. * added unit test for the fix * fix: consider val 0 on dbl to float range check - double zero will be less than FLT_MIN and still a valid float; - double zero to integers conversions consider this case already and are not affected. (cherry picked from commit f6669dd)
1 parent 963ccce commit b5454bd

File tree

4 files changed

+98
-28
lines changed

4 files changed

+98
-28
lines changed

driver/convert.c

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,9 +1332,11 @@ SQLRETURN sql2c_double(esodbc_rec_st *arec, esodbc_rec_st *irec,
13321332

13331333
case SQL_C_FLOAT:
13341334
REJECT_IF_NULL_DEST_BUFF(stmt, data_ptr);
1335-
udbl = dbl < 0 ? -dbl : dbl;
1336-
if (udbl < FLT_MIN || FLT_MAX < udbl) {
1337-
REJECT_AS_OOR(stmt, dbl, /* is fixed */FALSE, SQLREAL);
1335+
if (dbl) {
1336+
udbl = dbl < 0 ? -dbl : dbl;
1337+
if (udbl < FLT_MIN || FLT_MAX < udbl) {
1338+
REJECT_AS_OOR(stmt, dbl, /* is fixed */FALSE, SQLREAL);
1339+
}
13381340
}
13391341
*(SQLREAL *)data_ptr = (SQLREAL)dbl;
13401342
write_out_octets(octet_len_ptr, sizeof(SQLREAL), irec);
@@ -1382,15 +1384,38 @@ static SQLRETURN wstr_to_cstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
13821384

13831385
gd_offset_apply(stmt, &xstr);
13841386

1387+
assert(xstr.w.str[xstr.w.cnt] == L'\0');
1388+
/* how much space would the converted string take? */
1389+
in_bytes = WCS2U8(xstr.w.str, (int)xstr.w.cnt + 1, NULL, 0);
1390+
if (in_bytes <= 0) {
1391+
ERRNH(stmt, "failed to convert wchar* to char* for string `"
1392+
LWPDL "`.", LWSTR(&xstr.w));
1393+
RET_HDIAGS(stmt, SQL_STATE_22018);
1394+
}
1395+
/* out length needs to be provided with no (potential) truncation. */
1396+
if (octet_len_ptr) {
1397+
/* chars_0 accounts for 0-terminator, so WCS2U8 will count that in
1398+
* the output as well => trim it, since we must not count it when
1399+
* indicating the length to the application */
1400+
out_bytes = in_bytes - 1;
1401+
write_out_octets(octet_len_ptr, out_bytes, irec);
1402+
} else {
1403+
DBGH(stmt, "REC@0x%p, NULL octet_len_ptr.", arec);
1404+
}
1405+
13851406
if (data_ptr) {
13861407
charp = (char *)data_ptr;
13871408

1388-
in_bytes = (int)buff_octet_size((xstr.w.cnt + 1) * sizeof(SQLWCHAR),
1389-
sizeof(SQLCHAR), arec, irec, &state);
1409+
/* calculate how much of original data could possibly be copied in
1410+
* provided buffer; this will be given as a limitation to W-to-C
1411+
* conversion function. */
1412+
in_bytes = (int)buff_octet_size(in_bytes, sizeof(SQLCHAR), arec, irec,
1413+
&state);
13901414
/* trim the original string until it fits in output buffer, with given
13911415
* length limitation */
13921416
for (c = (int)xstr.w.cnt + 1; 0 < c; c --) {
13931417
out_bytes = WCS2U8(xstr.w.str, c, charp, in_bytes);
1418+
/* if user gives 0 as buffer size, out_bytes will also be 0 */
13941419
if (out_bytes <= 0) {
13951420
if (WCS2U8_BUFF_INSUFFICIENT) {
13961421
continue;
@@ -1404,10 +1429,7 @@ static SQLRETURN wstr_to_cstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
14041429
}
14051430
}
14061431

1407-
/* if 0's present => 0 < out_bytes */
1408-
assert(xstr.w.str[xstr.w.cnt] == L'\0');
14091432
assert(0 < out_bytes);
1410-
/* if user gives 0 as buffer size, out_bytes will also be 0 */
14111433
if (charp[out_bytes - 1]) {
14121434
/* ran out of buffer => not 0-terminated and truncated already */
14131435
charp[out_bytes - 1] = 0;
@@ -1418,30 +1440,12 @@ static SQLRETURN wstr_to_cstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
14181440
/* only update offset if data is copied out */
14191441
gd_offset_update(stmt, xstr.w.cnt, c);
14201442

1421-
DBGH(stmt, "REC@0x%p, data_ptr@0x%p, copied %zd bytes: `" LWPD "`.",
1443+
DBGH(stmt, "REC@0x%p, data_ptr@0x%p, copied %d bytes: `" LCPD "`.",
14221444
arec, data_ptr, out_bytes, charp);
14231445
} else {
14241446
DBGH(stmt, "REC@0x%p, NULL data_ptr.", arec);
14251447
}
14261448

1427-
/* if length needs to be given, calculate it (not truncated) & converted */
1428-
if (octet_len_ptr) {
1429-
out_bytes = (size_t)WCS2U8(xstr.w.str, (int)xstr.w.cnt + 1, NULL, 0);
1430-
if (out_bytes <= 0) {
1431-
ERRNH(stmt, "failed to convert wchar* to char* for string `"
1432-
LWPDL "`.", LWSTR(&xstr.w));
1433-
RET_HDIAGS(stmt, SQL_STATE_22018);
1434-
} else {
1435-
/* chars_0 accounts for 0-terminator, so WCS2U8 will count that in
1436-
* the output as well => trim it, since we must not count it when
1437-
* indicating the length to the application */
1438-
out_bytes --;
1439-
}
1440-
write_out_octets(octet_len_ptr, out_bytes, irec);
1441-
} else {
1442-
DBGH(stmt, "REC@0x%p, NULL octet_len_ptr.", arec);
1443-
}
1444-
14451449
if (state != SQL_STATE_00000) {
14461450
RET_HDIAGS(stmt, state);
14471451
}

driver/queries.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ SQLRETURN copy_one_row(esodbc_stmt_st *stmt, SQLULEN pos)
773773
case UJT_Long:
774774
case UJT_LongLong:
775775
ll = UJNumericLongLong(obj);
776-
DBGH(stmt, "value [%zd, %d] is numeric: %lld.", rowno, i + 1,
776+
DBGH(stmt, "value [%zd, %d] is integer: %lld.", rowno, i + 1,
777777
ll);
778778
ret = sql2c_longlong(arec, irec, pos, ll);
779779
break;

test/test_conversion_sql2c_floats.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,39 @@ TEST_F(ConvertSQL2C_Floats, Float2Long) {
333333
}
334334

335335

336+
TEST_F(ConvertSQL2C_Floats, Double_zero2Float) {
337+
338+
#undef SQL_RAW
339+
#undef SQL_VAL
340+
#undef SQL
341+
#define SQL_RAW 0.0
342+
#define SQL_VAL STR(SQL_RAW)
343+
#define SQL "CAST(" SQL_VAL " AS DOUBLE)"
344+
345+
const char json_answer[] = "\
346+
{\
347+
\"columns\": [\
348+
{\"name\": \"" SQL "\", \"type\": \"double\"}\
349+
],\
350+
\"rows\": [\
351+
[" SQL_VAL "]\
352+
]\
353+
}\
354+
";
355+
prepareStatement(json_answer);
356+
357+
SQLREAL val;
358+
ret = SQLBindCol(stmt, /*col#*/1, SQL_C_FLOAT, &val, sizeof(val), &ind_len);
359+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
360+
361+
ret = SQLFetch(stmt);
362+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
363+
364+
EXPECT_EQ(ind_len, sizeof(val));
365+
EXPECT_LE(SQL_RAW, val);
366+
}
367+
368+
336369
TEST_F(ConvertSQL2C_Floats, Double2Float) {
337370

338371
#undef SQL_RAW

test/test_conversion_sql2c_string.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ TEST_F(ConvertSQL2C_String, String2Char) {
4545

4646
ret = SQLFetch(stmt);
4747
ASSERT_TRUE(SQL_SUCCEEDED(ret));
48+
assertState(L"00000");
4849

4950
EXPECT_EQ(ind_len, sizeof(SQL_VAL) - /*\0*/1);
5051
EXPECT_STREQ((char/*4gtest*/*)buff, SQL_VAL);
@@ -114,6 +115,38 @@ TEST_F(ConvertSQL2C_String, String2Char_zero_copy) {
114115
}
115116

116117

118+
TEST_F(ConvertSQL2C_String, String2Char_truncate) {
119+
120+
#undef SQL_VAL
121+
#undef SQL
122+
#define SQL_VAL "abcdef"
123+
#define SQL "CAST(" SQL_VAL " AS TEXT)"
124+
125+
const char json_answer[] = "\
126+
{\
127+
\"columns\": [\
128+
{\"name\": \"" SQL "\", \"type\": \"text\"}\
129+
],\
130+
\"rows\": [\
131+
[\"" SQL_VAL "\"]\
132+
]\
133+
}\
134+
";
135+
prepareStatement(json_answer);
136+
137+
SQLCHAR buff[(sizeof(SQL_VAL) - 1)/2 + 1];
138+
ret = SQLBindCol(stmt, /*col#*/1, SQL_C_CHAR, &buff, sizeof(buff), &ind_len);
139+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
140+
141+
ret = SQLFetch(stmt);
142+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
143+
assertState(L"01004");
144+
145+
EXPECT_EQ(ind_len, sizeof(SQL_VAL) - /*\0*/1);
146+
EXPECT_STREQ((char/*4gtest*/*)buff, "abc");
147+
}
148+
149+
117150
TEST_F(ConvertSQL2C_String, String2SLong) {
118151

119152
#undef SQL_RAW

0 commit comments

Comments
 (0)