Skip to content

Commit 26fe33c

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) Resolved conflict: s/WAPI_ERRNO/WCS2U8_ERRNO/ .
1 parent 8dbd16a commit 26fe33c

File tree

3 files changed

+110
-5
lines changed

3 files changed

+110
-5
lines changed

driver/convert.c

+35-5
Original file line numberDiff line numberDiff line change
@@ -3664,7 +3664,6 @@ static BOOL xstr_to_number(esodbc_stmt_st *stmt, void *data_ptr,
36643664
SQLRETURN c2sql_null(esodbc_rec_st *arec,
36653665
esodbc_rec_st *irec, char *dest, size_t *len)
36663666
{
3667-
assert(irec->concise_type == ESODBC_SQL_NULL);
36683667
if (dest) {
36693668
memcpy(dest, JSON_VAL_NULL, sizeof(JSON_VAL_NULL) - /*\0*/1);
36703669
}
@@ -4615,6 +4614,37 @@ SQLRETURN c2sql_interval(esodbc_rec_st *arec, esodbc_rec_st *irec,
46154614
# undef ASSIGN_SIGNED
46164615
}
46174616

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

4630-
cnt = octet_len_ptr ? *octet_len_ptr : strlen((char *)data_ptr);
4660+
cnt = get_octet_len(octet_len_ptr, data_ptr, /*wide*/FALSE);
46314661

46324662
if (dest) {
46334663
*dest = '"';
@@ -4661,7 +4691,7 @@ static SQLRETURN c2sql_wstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
46614691
/* pointer to app's buffer */
46624692
data_ptr = deferred_address(SQL_DESC_DATA_PTR, pos, arec);
46634693

4664-
cnt = octet_len_ptr ? *octet_len_ptr : wcslen((wchar_t *)data_ptr);
4694+
cnt = get_octet_len(octet_len_ptr, data_ptr, /*wide*/TRUE);
46654695

46664696
if (dest) {
46674697
*dest = '"';
@@ -4679,8 +4709,8 @@ static SQLRETURN c2sql_wstr2qstr(esodbc_rec_st *arec, esodbc_rec_st *irec,
46794709
SetLastError(0);
46804710
octets = WCS2U8((wchar_t *)data_ptr, (int)cnt, dest + !!dest,
46814711
dest ? INT_MAX : 0);
4682-
if ((err = GetLastError())) {
4683-
ERRH(stmt, "converting to multibyte string failed: %d", err);
4712+
if ((err = WCS2U8_ERRNO()) != ERROR_SUCCESS) {
4713+
ERRH(stmt, "converting to multibyte string failed: 0x%x", err);
46844714
RET_HDIAGS(stmt, SQL_STATE_HY000);
46854715
}
46864716
} else {

test/test_conversion_c2sql_null.cc

+45
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

+30
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)