Skip to content

Commit 23e1031

Browse files
committed
fix length read from app ptr when param binding (#173)
This commit fixes the length handling of received wide-char strings as parameters: the indicator-length pointer of the API call provides the octet count, so the lenght needs to be divided by wide-char size. Besides a couple of tests for the fix, the commit adds a new test on null generation (both of non-NULL and NULL types). (cherry picked from commit 29376ec)
1 parent c5cb9cb commit 23e1031

File tree

3 files changed

+109
-4
lines changed

3 files changed

+109
-4
lines changed

driver/convert.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3666,7 +3666,6 @@ static BOOL xstr_to_number(esodbc_stmt_st *stmt, void *data_ptr,
36663666
SQLRETURN c2sql_null(esodbc_rec_st *arec,
36673667
esodbc_rec_st *irec, char *dest, size_t *len)
36683668
{
3669-
assert(irec->concise_type == ESODBC_SQL_NULL);
36703669
if (dest) {
36713670
memcpy(dest, JSON_VAL_NULL, sizeof(JSON_VAL_NULL) - /*\0*/1);
36723671
}
@@ -4617,6 +4616,37 @@ SQLRETURN c2sql_interval(esodbc_rec_st *arec, esodbc_rec_st *irec,
46174616
# undef ASSIGN_SIGNED
46184617
}
46194618

4619+
static inline SQLLEN get_octet_len(SQLLEN *octet_len_ptr, void *data_ptr,
4620+
BOOL wide)
4621+
{
4622+
SQLLEN cnt;
4623+
4624+
assert(data_ptr);
4625+
4626+
if (! octet_len_ptr) {
4627+
/* "If [...] is a null pointer, the driver assumes that all input
4628+
* parameter values are non-NULL and that character and binary data is
4629+
* null-terminated." */
4630+
cnt = wide ? wcslen((wchar_t *)data_ptr) : strlen((char *)data_ptr);
4631+
} else {
4632+
cnt = *octet_len_ptr;
4633+
switch (cnt) {
4634+
case SQL_NTSL:
4635+
cnt = wide ? wcslen((wchar_t *)data_ptr) :
4636+
strlen((char *)data_ptr);
4637+
break;
4638+
case SQL_NULL_DATA:
4639+
BUG("converting SQL_NULL_DATA");
4640+
cnt = -1; /* UTF16/8 will fail */
4641+
break;
4642+
default: /* get characters count from octets count */
4643+
cnt /= wide ? sizeof(SQLWCHAR) : sizeof(SQLCHAR);
4644+
}
4645+
}
4646+
4647+
return cnt;
4648+
}
4649+
46204650
static SQLRETURN c2sql_cstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
46214651
SQLULEN pos, char *dest, size_t *len)
46224652
{
@@ -4629,7 +4659,7 @@ static SQLRETURN c2sql_cstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
46294659
/* pointer to app's buffer */
46304660
data_ptr = deferred_address(SQL_DESC_DATA_PTR, pos, arec);
46314661

4632-
cnt = octet_len_ptr ? *octet_len_ptr : strlen((char *)data_ptr);
4662+
cnt = get_octet_len(octet_len_ptr, data_ptr, /*wide*/FALSE);
46334663

46344664
if (dest) {
46354665
*dest = '"';
@@ -4663,7 +4693,7 @@ static SQLRETURN c2sql_wstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
46634693
/* pointer to app's buffer */
46644694
data_ptr = deferred_address(SQL_DESC_DATA_PTR, pos, arec);
46654695

4666-
cnt = octet_len_ptr ? *octet_len_ptr : wcslen((wchar_t *)data_ptr);
4696+
cnt = get_octet_len(octet_len_ptr, data_ptr, /*wide*/TRUE);
46674697

46684698
if (dest) {
46694699
*dest = '"';
@@ -4682,7 +4712,7 @@ static SQLRETURN c2sql_wstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
46824712
octets = U16WC_TO_MBU8((wchar_t *)data_ptr, cnt, dest + !!dest,
46834713
dest ? INT_MAX : 0);
46844714
if ((err = WAPI_ERRNO()) != ERROR_SUCCESS) {
4685-
ERRH(stmt, "converting to multibyte string failed: %d", err);
4715+
ERRH(stmt, "converting to multibyte string failed: 0x%x", err);
46864716
RET_HDIAGS(stmt, SQL_STATE_HY000);
46874717
}
46884718
assert(0 < octets); /* shouldn't not fail and return negative */

test/test_conversion_c2sql_null.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
#include <gtest/gtest.h>
8+
#include "connected_dbc.h"
9+
10+
namespace test {
11+
12+
class ConvertC2SQL_Null : public ::testing::Test, public ConnectedDBC {
13+
};
14+
15+
16+
TEST_F(ConvertC2SQL_Null, CStr2Boolean_null)
17+
{
18+
prepareStatement();
19+
20+
SQLCHAR val[] = "1";
21+
SQLLEN osize = SQL_NULL_DATA;
22+
ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR,
23+
ESODBC_SQL_BOOLEAN, /*size*/0, /*decdigits*/0, val,
24+
sizeof(val) - /*\0*/1, &osize);
25+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
26+
27+
assertRequest("[{\"type\": \"BOOLEAN\", \"value\": null}]");
28+
}
29+
30+
TEST_F(ConvertC2SQL_Null, WStr2Null)
31+
{
32+
prepareStatement();
33+
34+
SQLWCHAR val[] = L"0X";
35+
ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR,
36+
ESODBC_SQL_NULL, /*size*/0, /*decdigits*/0, val, 0, NULL);
37+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
38+
39+
assertRequest("[{\"type\": \"NULL\", \"value\": null}]");
40+
}
41+
42+
43+
} // test namespace
44+
45+
/* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */

test/test_conversion_c2sql_varchar.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,21 @@ TEST_F(ConvertC2SQL_Varchar, WStr2Varchar_ansi_jsonescape)
8585
"\"value\": \"START_{xxx}=\\\"yyy\\\"\\r__END\"}]");
8686
}
8787

88+
TEST_F(ConvertC2SQL_Varchar, CStr2Varchar_jsonescape_oct_len_ptr)
89+
{
90+
prepareStatement();
91+
92+
SQLCHAR val[] = "START_{xxx}=\"yyy\"\r__END";
93+
SQLLEN octet_len = strlen((char *)val);
94+
ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR,
95+
SQL_VARCHAR, /*size*/35, /*decdigits*/0, val, sizeof(val),
96+
&octet_len);
97+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
98+
99+
assertRequest("[{\"type\": \"KEYWORD\", "
100+
"\"value\": \"START_{xxx}=\\\"yyy\\\"\\r__END\"}]");
101+
}
102+
88103
/* note: test name used in test */
89104
TEST_F(ConvertC2SQL_Varchar, CStr2Varchar_jsonescape)
90105
{
@@ -115,6 +130,21 @@ TEST_F(ConvertC2SQL_Varchar, WStr2Varchar_u8_jsonescape)
115130
"\"value\": \"START_\\\"A\u00C4o\u00F6U\u00FC\\\"__END\"}]");
116131
}
117132

133+
TEST_F(ConvertC2SQL_Varchar, WStr2Varchar_u8_fullescape_oct_len_ptr)
134+
{
135+
prepareStatement();
136+
137+
SQLWCHAR val[] = L"äöüÄÖÜ";
138+
SQLLEN octet_len = SQL_NTSL;
139+
ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_WCHAR,
140+
SQL_VARCHAR, /*size*/35, /*decdigits*/0, val, sizeof(val),
141+
&octet_len);
142+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
143+
144+
assertRequest("[{\"type\": \"KEYWORD\", "
145+
"\"value\": \"\u00E4\u00F6\u00FC\u00C4\u00D6\u00DC\"}]");
146+
}
147+
118148
/* note: test name used in test */
119149
TEST_F(ConvertC2SQL_Varchar, WStr2Varchar_u8_fullescape)
120150
{

0 commit comments

Comments
 (0)