Skip to content

Commit ef5dccb

Browse files
authored
fix handling the output conn str buff if too small (#194)
If the application provides an output buffer for the constructed connection string, the driver will currently return an error and thus fail the connection. The correct handling is however to apply truncation but return the number of characters available without trunctation. This commit works around swprintf() returning an error code, but otherwise correctly 0-terminating the destination buffer and not setting the errno.
1 parent 8591576 commit ef5dccb

File tree

7 files changed

+158
-80
lines changed

7 files changed

+158
-80
lines changed

driver/connect.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3206,7 +3206,7 @@ SQLRETURN EsSQLSetConnectAttrW(
32063206
RET_HDIAGS(dbc, SQL_STATE_HYC00);
32073207

32083208
#ifndef NDEBUG
3209-
/* MicroStrategy Desktop invoked */
3209+
/* MicroStrategy Desktop, Oracle BI invoked */
32103210
case 1041:
32113211
case 1042:
32123212
/* MS Access/Jet proprietary info type */

driver/defs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
#define ESODBC_MAX_IDENTIFIER_LEN SHRT_MAX
6262
/* "the relationship between the columns in the GROUP BY clause and the
6363
* nonaggregated columns in the select list" */
64-
#define ESODBC_GROUP_BY SQL_GB_NO_RELATION
64+
#define ESODBC_GROUP_BY SQL_GB_NO_RELATION
6565

6666
/* 20 = len("18446744073709551616"), 1 << (sizeof(uint64_t) * 8bits) */
6767
#define ESODBC_PRECISION_UINT64 20

driver/dsn.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -806,13 +806,19 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs,
806806
} else {
807807
format = WPFWP_LDESC "=" WPFWP_LDESC ";";
808808
}
809+
errno = 0;
809810
n = swprintf(szConnStrOut + pos, cchConnStrOutMax - pos,
810811
format, LWSTR(iter->kw), LWSTR(iter->val));
812+
/* on buffer too small, swprintf() will 0-terminate it,
813+
* return negative, but not set errno. */
811814
if (n < 0) {
812-
ERRN("failed to outprint connection string (keyword: "
813-
LWPDL ", room: %hd, position: %zu).",
814-
LWSTR(iter->kw), cchConnStrOutMax, pos);
815-
return -1;
815+
if (errno != 0) {
816+
ERRN("failed to print connection string (keyword: "
817+
LWPDL ", room: %hd, position: %zu).",
818+
LWSTR(iter->kw), cchConnStrOutMax, pos);
819+
return -1;
820+
}
821+
assert(szConnStrOut[cchConnStrOutMax - 1] == L'\0');
816822
}
817823
}
818824
}

driver/handles.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,8 @@ SQLRETURN EsSQLSetEnvAttr(SQLHENV EnvironmentHandle,
521521
// review of the options.
522522
case SQL_OV_ODBC2:
523523
case SQL_OV_ODBC3:
524+
WARNH(EnvironmentHandle, "application version %d not fully"
525+
" supported.", (intptr_t)Value);
524526
case SQL_OV_ODBC3_80:
525527
break;
526528
default:

test/connected_dbc.cc

Lines changed: 5 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -14,80 +14,13 @@ extern "C" {
1414

1515
#include "connected_dbc.h"
1616

17-
/*
18-
* Answer ES/SQL sends to SYS TYPES
19-
*/
20-
static const char systypes_answer[] = "\
21-
{\
22-
\"columns\":[\
23-
{\"name\":\"TYPE_NAME\",\"type\":\"keyword\",\"display_size\":32766},\
24-
{\"name\":\"DATA_TYPE\",\"type\":\"integer\",\"display_size\":11},\
25-
{\"name\":\"PRECISION\",\"type\":\"integer\",\"display_size\":11},\
26-
{\"name\":\"LITERAL_PREFIX\",\"type\":\"keyword\",\"display_size\":32766},\
27-
{\"name\":\"LITERAL_SUFFIX\",\"type\":\"keyword\",\"display_size\":32766},\
28-
{\"name\":\"CREATE_PARAMS\",\"type\":\"keyword\",\"display_size\":32766},\
29-
{\"name\":\"NULLABLE\",\"type\":\"short\",\"display_size\":6},\
30-
{\"name\":\"CASE_SENSITIVE\",\"type\":\"boolean\",\"display_size\":1},\
31-
{\"name\":\"SEARCHABLE\",\"type\":\"short\",\"display_size\":6},\
32-
{\"name\":\"UNSIGNED_ATTRIBUTE\",\"type\":\"boolean\",\"display_size\":1},\
33-
{\"name\":\"FIXED_PREC_SCALE\",\"type\":\"boolean\",\"display_size\":1},\
34-
{\"name\":\"AUTO_INCREMENT\",\"type\":\"boolean\",\"display_size\":1},\
35-
{\"name\":\"LOCAL_TYPE_NAME\",\"type\":\"keyword\",\"display_size\":32766},\
36-
{\"name\":\"MINIMUM_SCALE\",\"type\":\"short\",\"display_size\":6},\
37-
{\"name\":\"MAXIMUM_SCALE\",\"type\":\"short\",\"display_size\":6},\
38-
{\"name\":\"SQL_DATA_TYPE\",\"type\":\"integer\",\"display_size\":11},\
39-
{\"name\":\"SQL_DATETIME_SUB\",\"type\":\"integer\",\"display_size\":11},\
40-
{\"name\":\"NUM_PREC_RADIX\",\"type\":\"integer\",\"display_size\":11},\
41-
{\"name\":\"INTERVAL_PRECISION\",\"type\":\"integer\",\"display_size\":11}\
42-
],\
43-
\"rows\":[\
44-
[\"BYTE\",-6,3,\"'\",\"'\",null,2,false,3,false,false,false,null,0,0,-6,0,10,null],\
45-
[\"LONG\",-5,19,\"'\",\"'\",null,2,false,3,false,false,false,null,0,0,-5,0,10,null],\
46-
[\"BINARY\",-3,2147483647,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,-3,0,null,null],\
47-
[\"NULL\",0,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,0,0,null,null],\
48-
[\"INTEGER\",4,10,\"'\",\"'\",null,2,false,3,false,false,false,null,0,0,4,0,10,null],\
49-
[\"SHORT\",5,5,\"'\",\"'\",null,2,false,3,false,false,false,null,0,0,5,0,10,null],\
50-
[\"HALF_FLOAT\",6,3,\"'\",\"'\",null,2,false,3,false,false,false,null,3,3,6,0,2,null],\
51-
[\"FLOAT\",7,7,\"'\",\"'\",null,2,false,3,false,false,false,null,7,7,7,0,2,null],\
52-
[\"DOUBLE\",8,15,\"'\",\"'\",null,2,false,3,false,false,false,null,15,15,8,0,2,null],\
53-
[\"SCALED_FLOAT\",8,15,\"'\",\"'\",null,2,false,3,false,false,false,null,15,15,8,0,2,null],\
54-
[\"KEYWORD\",12,32766,\"'\",\"'\",null,2,true,3,true,false,false,null,null,null,12,0,null,null],\
55-
[\"TEXT\",12,2147483647,\"'\",\"'\",null,2,true,3,true,false,false,null,null,null,12,0,null,null],\
56-
[\"IP\",12,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,12,0,null,null],\
57-
[\"BOOLEAN\",16,1,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,16,0,null,null],\
58-
[\"DATE\",91,29,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,91,1,null,null],\
59-
[\"TIME\",92,18,\"'\",\"'\",null,2,false,3,true,false,false,null,3,3,92,2,null,null],\
60-
[\"DATETIME\",93,29,\"'\",\"'\",null,2,false,3,true,false,false,null,3,3,9,3,null,null],\
61-
[\"INTERVAL_YEAR\",101,7,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,101,0,null,null],\
62-
[\"INTERVAL_MONTH\",102,7,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,102,0,null,null],\
63-
[\"INTERVAL_DAY\",103,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,103,0,null,null],\
64-
[\"INTERVAL_HOUR\",104,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,104,0,null,null],\
65-
[\"INTERVAL_MINUTE\",105,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,105,0,null,null],\
66-
[\"INTERVAL_SECOND\",106,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,106,0,null,null],\
67-
[\"INTERVAL_YEAR_TO_MONTH\",107,7,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,107,0,null,null],\
68-
[\"INTERVAL_DAY_TO_HOUR\",108,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,108,0,null,null],\
69-
[\"INTERVAL_DAY_TO_MINUTE\",109,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,109,0,null,null],\
70-
[\"INTERVAL_DAY_TO_SECOND\",110,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,110,0,null,null],\
71-
[\"INTERVAL_HOUR_TO_MINUTE\",111,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,111,0,null,null],\
72-
[\"INTERVAL_HOUR_TO_SECOND\",112,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,112,0,null,null],\
73-
[\"INTERVAL_MINUTE_TO_SECOND\",113,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,113,0,null,null],\
74-
[\"UNSUPPORTED\",1111,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,1111,0,null,null],\
75-
[\"OBJECT\",2002,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,2002,0,null,null],\
76-
[\"NESTED\",2002,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,2002,0,null,null]\
77-
]\
78-
}";
79-
80-
/* minimal, valid connection string */
81-
static const SQLWCHAR connect_string[] = L"Driver=ElasticODBC";
82-
8317

8418
/*
8519
* Class will provide a "connected" DBC: the ES types are loaded.
8620
*/
8721
ConnectedDBC::ConnectedDBC()
8822
{
89-
SQLRETURN ret;
90-
cstr_st types;
23+
cstr_st types = {0};
9124

9225
assert(getenv("TZ") == NULL);
9326

@@ -101,14 +34,12 @@ ConnectedDBC::ConnectedDBC()
10134
ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
10235
assert(SQL_SUCCEEDED(ret));
10336

104-
105-
types.cnt = sizeof(systypes_answer) - 1;
106-
types.str = (SQLCHAR *)malloc(types.cnt);
37+
types.str = (SQLCHAR *)strdup(SYSTYPES_ANSWER);
10738
assert(types.str != NULL);
108-
memcpy(types.str, systypes_answer, types.cnt);
39+
types.cnt = sizeof(SYSTYPES_ANSWER) - 1;
10940

110-
ret = SQLDriverConnect(dbc, (SQLHWND)&types, (SQLWCHAR *)connect_string,
111-
sizeof(connect_string) / sizeof(connect_string[0]) - 1, NULL, 0, NULL,
41+
ret = SQLDriverConnect(dbc, (SQLHWND)&types, (SQLWCHAR *)CONNECT_STRING,
42+
sizeof(CONNECT_STRING) / sizeof(CONNECT_STRING[0]) - 1, NULL, 0, NULL,
11243
ESODBC_SQL_DRIVER_TEST);
11344
assert(SQL_SUCCEEDED(ret));
11445

test/connected_dbc.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,71 @@ extern "C" {
3636
ASSERT_EQ(strncmp((char *)_c1.str, (char *)_c2.str, _c1.cnt), 0); \
3737
} while (0)
3838

39+
/*
40+
* Answer ES/SQL sends to SYS TYPES
41+
*/
42+
#define SYSTYPES_ANSWER "\
43+
{\
44+
\"columns\":[\
45+
{\"name\":\"TYPE_NAME\",\"type\":\"keyword\",\"display_size\":32766},\
46+
{\"name\":\"DATA_TYPE\",\"type\":\"integer\",\"display_size\":11},\
47+
{\"name\":\"PRECISION\",\"type\":\"integer\",\"display_size\":11},\
48+
{\"name\":\"LITERAL_PREFIX\",\"type\":\"keyword\",\"display_size\":32766},\
49+
{\"name\":\"LITERAL_SUFFIX\",\"type\":\"keyword\",\"display_size\":32766},\
50+
{\"name\":\"CREATE_PARAMS\",\"type\":\"keyword\",\"display_size\":32766},\
51+
{\"name\":\"NULLABLE\",\"type\":\"short\",\"display_size\":6},\
52+
{\"name\":\"CASE_SENSITIVE\",\"type\":\"boolean\",\"display_size\":1},\
53+
{\"name\":\"SEARCHABLE\",\"type\":\"short\",\"display_size\":6},\
54+
{\"name\":\"UNSIGNED_ATTRIBUTE\",\"type\":\"boolean\",\"display_size\":1},\
55+
{\"name\":\"FIXED_PREC_SCALE\",\"type\":\"boolean\",\"display_size\":1},\
56+
{\"name\":\"AUTO_INCREMENT\",\"type\":\"boolean\",\"display_size\":1},\
57+
{\"name\":\"LOCAL_TYPE_NAME\",\"type\":\"keyword\",\"display_size\":32766},\
58+
{\"name\":\"MINIMUM_SCALE\",\"type\":\"short\",\"display_size\":6},\
59+
{\"name\":\"MAXIMUM_SCALE\",\"type\":\"short\",\"display_size\":6},\
60+
{\"name\":\"SQL_DATA_TYPE\",\"type\":\"integer\",\"display_size\":11},\
61+
{\"name\":\"SQL_DATETIME_SUB\",\"type\":\"integer\",\"display_size\":11},\
62+
{\"name\":\"NUM_PREC_RADIX\",\"type\":\"integer\",\"display_size\":11},\
63+
{\"name\":\"INTERVAL_PRECISION\",\"type\":\"integer\",\"display_size\":11}\
64+
],\
65+
\"rows\":[\
66+
[\"BYTE\",-6,3,\"'\",\"'\",null,2,false,3,false,false,false,null,0,0,-6,0,10,null],\
67+
[\"LONG\",-5,19,\"'\",\"'\",null,2,false,3,false,false,false,null,0,0,-5,0,10,null],\
68+
[\"BINARY\",-3,2147483647,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,-3,0,null,null],\
69+
[\"NULL\",0,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,0,0,null,null],\
70+
[\"INTEGER\",4,10,\"'\",\"'\",null,2,false,3,false,false,false,null,0,0,4,0,10,null],\
71+
[\"SHORT\",5,5,\"'\",\"'\",null,2,false,3,false,false,false,null,0,0,5,0,10,null],\
72+
[\"HALF_FLOAT\",6,3,\"'\",\"'\",null,2,false,3,false,false,false,null,3,3,6,0,2,null],\
73+
[\"FLOAT\",7,7,\"'\",\"'\",null,2,false,3,false,false,false,null,7,7,7,0,2,null],\
74+
[\"DOUBLE\",8,15,\"'\",\"'\",null,2,false,3,false,false,false,null,15,15,8,0,2,null],\
75+
[\"SCALED_FLOAT\",8,15,\"'\",\"'\",null,2,false,3,false,false,false,null,15,15,8,0,2,null],\
76+
[\"KEYWORD\",12,32766,\"'\",\"'\",null,2,true,3,true,false,false,null,null,null,12,0,null,null],\
77+
[\"TEXT\",12,2147483647,\"'\",\"'\",null,2,true,3,true,false,false,null,null,null,12,0,null,null],\
78+
[\"IP\",12,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,12,0,null,null],\
79+
[\"BOOLEAN\",16,1,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,16,0,null,null],\
80+
[\"DATE\",91,29,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,91,1,null,null],\
81+
[\"TIME\",92,18,\"'\",\"'\",null,2,false,3,true,false,false,null,3,3,92,2,null,null],\
82+
[\"DATETIME\",93,29,\"'\",\"'\",null,2,false,3,true,false,false,null,3,3,9,3,null,null],\
83+
[\"INTERVAL_YEAR\",101,7,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,101,0,null,null],\
84+
[\"INTERVAL_MONTH\",102,7,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,102,0,null,null],\
85+
[\"INTERVAL_DAY\",103,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,103,0,null,null],\
86+
[\"INTERVAL_HOUR\",104,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,104,0,null,null],\
87+
[\"INTERVAL_MINUTE\",105,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,105,0,null,null],\
88+
[\"INTERVAL_SECOND\",106,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,106,0,null,null],\
89+
[\"INTERVAL_YEAR_TO_MONTH\",107,7,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,107,0,null,null],\
90+
[\"INTERVAL_DAY_TO_HOUR\",108,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,108,0,null,null],\
91+
[\"INTERVAL_DAY_TO_MINUTE\",109,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,109,0,null,null],\
92+
[\"INTERVAL_DAY_TO_SECOND\",110,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,110,0,null,null],\
93+
[\"INTERVAL_HOUR_TO_MINUTE\",111,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,111,0,null,null],\
94+
[\"INTERVAL_HOUR_TO_SECOND\",112,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,112,0,null,null],\
95+
[\"INTERVAL_MINUTE_TO_SECOND\",113,23,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,113,0,null,null],\
96+
[\"UNSUPPORTED\",1111,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,1111,0,null,null],\
97+
[\"OBJECT\",2002,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,2002,0,null,null],\
98+
[\"NESTED\",2002,0,\"'\",\"'\",null,2,false,3,true,false,false,null,null,null,2002,0,null,null]\
99+
]\
100+
}"
101+
102+
/* minimal, valid connection string */
103+
#define CONNECT_STRING L"Driver=ElasticODBC"
39104

40105
class ConnectedDBC {
41106
protected:

test/test_driverconnect.cc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 DriverConnect : public ::testing::Test, public ConnectedDBC
13+
{
14+
protected:
15+
cstr_st types = {0};
16+
SQLHANDLE my_dbc;
17+
SQLSMALLINT out_avail = -1;
18+
19+
void SetUp() override
20+
{
21+
ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &my_dbc);
22+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
23+
24+
types.str = (SQLCHAR *)strdup(SYSTYPES_ANSWER);
25+
ASSERT_TRUE(types.str != NULL);
26+
types.cnt = sizeof(SYSTYPES_ANSWER) - 1;
27+
}
28+
29+
void TearDown() override
30+
{
31+
ret = SQLFreeHandle(SQL_HANDLE_DBC, my_dbc);
32+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
33+
}
34+
};
35+
36+
TEST_F(DriverConnect, OutputCount)
37+
{
38+
ret = SQLDriverConnect(my_dbc, (SQLHWND)&types, (SQLWCHAR *)CONNECT_STRING,
39+
sizeof(CONNECT_STRING) / sizeof(CONNECT_STRING[0]) - 1, NULL, 0,
40+
&out_avail, ESODBC_SQL_DRIVER_TEST);
41+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
42+
ASSERT_TRUE(0 < out_avail);
43+
}
44+
45+
TEST_F(DriverConnect, OutputZeroTerm)
46+
{
47+
static const size_t buff_sz = 1024;
48+
SQLWCHAR out_buff[buff_sz];
49+
50+
ret = SQLDriverConnect(my_dbc, (SQLHWND)&types, (SQLWCHAR *)CONNECT_STRING,
51+
sizeof(CONNECT_STRING) / sizeof(CONNECT_STRING[0]) - 1,
52+
out_buff, buff_sz, &out_avail, ESODBC_SQL_DRIVER_TEST);
53+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
54+
ASSERT_TRUE(out_avail < buff_sz);
55+
ASSERT_EQ(out_buff[out_avail], (SQLWCHAR)L'\0');
56+
57+
}
58+
59+
TEST_F(DriverConnect, OutputTruncated)
60+
{
61+
static const size_t buff_sz = 3;
62+
SQLWCHAR out_buff[buff_sz];
63+
64+
ret = SQLDriverConnect(my_dbc, (SQLHWND)&types, (SQLWCHAR *)CONNECT_STRING,
65+
sizeof(CONNECT_STRING) / sizeof(CONNECT_STRING[0]) - 1,
66+
out_buff, buff_sz, &out_avail, ESODBC_SQL_DRIVER_TEST);
67+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
68+
ASSERT_TRUE(buff_sz < out_avail);
69+
ASSERT_EQ(out_buff[buff_sz - 1], (SQLWCHAR)L'\0');
70+
71+
}
72+
73+
} // test namespace
74+

0 commit comments

Comments
 (0)