Skip to content

Commit b608f56

Browse files
authored
Fix SQLDescribeCol numeric attribute param handling (#306)
The last parameter of SQLDescribeCol(), the "NumericAttributePtr" is an out paramter that should always point to a SQLLEN value (for both x64 and x32 builds), despite this value being assigned different width values. The x32 API definition uses a "void *" that would allow passing pointers to various width integers, but the spec does require an SQLLEN and other drivers and apps alligned to it. Some apps seem however to not cast down to the width corresponding to the various attributes, which can lead to incorrect behaviour. The driver now scales up all values to an SQLLEN.
1 parent 23133dc commit b608f56

File tree

5 files changed

+161
-29
lines changed

5 files changed

+161
-29
lines changed

driver/connect.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,7 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs)
15031503
INFOH(dbc, "early execution: %s.", dbc->early_exec ? "true" : "false");
15041504
/* default current catalog */
15051505
if (attrs->catalog.cnt &&
1506-
(! SQL_SUCCEEDED(set_current_catalog(dbc, &attrs->catalog)))) {
1506+
(! SQL_SUCCEEDED(set_current_catalog(dbc, &attrs->catalog)))) {
15071507
goto err;
15081508
}
15091509

@@ -3478,8 +3478,8 @@ SQLRETURN EsSQLGetConnectAttrW(
34783478
}
34793479
if (dbc->catalog.w.cnt) {
34803480
if (! SQL_SUCCEEDED(write_wstr(dbc, (SQLWCHAR *)ValuePtr,
3481-
&dbc->catalog.w, (SQLSMALLINT)BufferLength,
3482-
&used))) {
3481+
&dbc->catalog.w, (SQLSMALLINT)BufferLength,
3482+
&used))) {
34833483
ERRH(dbc, "failed to copy current catalog out.");
34843484
RET_STATE(dbc->hdr.diag.state);
34853485
}

driver/odbc.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -837,13 +837,8 @@ SQLRETURN SQL_API SQLColAttributeW
837837
ret = EsSQLColAttributeW(hstmt, iCol, iField, pCharAttr,
838838
cbDescMax, pcbCharAttr, pNumAttr);
839839
HND_UNLOCK(hstmt);
840-
#ifdef _WIN64
841840
TRACE8(_OUT, hstmt, "dpHHphtn", ret, hstmt, iCol, iField, pCharAttr,
842841
cbDescMax, pcbCharAttr, pNumAttr);
843-
#else /* _WIN64 */
844-
TRACE8(_OUT, hstmt, "dpddpdtg", ret, hstmt, iCol, iField, pCharAttr,
845-
cbDescMax, pcbCharAttr, pNumAttr);
846-
#endif /* _WIN64 */
847842
return ret;
848843
}
849844

driver/queries.c

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3957,11 +3957,7 @@ SQLRETURN EsSQLColAttributeW(
39573957
_Out_opt_
39583958
SQLSMALLINT *pcbCharAttr, /* [out] len written in pCharAttr (w/o \0 */
39593959
_Out_opt_
3960-
#ifdef _WIN64
39613960
SQLLEN *pNumAttr /* [out] value, if numeric */
3962-
#else /* _WIN64 */
3963-
SQLPOINTER pNumAttr
3964-
#endif /* _WIN64 */
39653961
)
39663962
{
39673963
esodbc_stmt_st *stmt = STMH(hstmt);
@@ -3972,12 +3968,6 @@ SQLRETURN EsSQLColAttributeW(
39723968
SQLLEN len;
39733969
SQLINTEGER iint;
39743970

3975-
#ifdef _WIN64
3976-
#define PNUMATTR_ASSIGN(type, value) *pNumAttr = (SQLLEN)(value)
3977-
#else /* _WIN64 */
3978-
#define PNUMATTR_ASSIGN(type, value) *(type *)pNumAttr = (type)(value)
3979-
#endif /* _WIN64 */
3980-
39813971
DBGH(stmt, "IRD@0x%p, column #%d, field: %d.", ird, iCol, iField);
39823972

39833973
if (! STMT_HAS_RESULTSET(stmt)) {
@@ -4022,7 +4012,7 @@ SQLRETURN EsSQLColAttributeW(
40224012
sint = rec->es_type->fixed_prec_scale;
40234013
break;
40244014
} while (0);
4025-
PNUMATTR_ASSIGN(SQLSMALLINT, sint);
4015+
*pNumAttr = (SQLLEN)sint;
40264016
break;
40274017

40284018
/* SQLWCHAR* */
@@ -4056,14 +4046,14 @@ SQLRETURN EsSQLColAttributeW(
40564046
case SQL_COLUMN_LENGTH: /* 2.x attrib; no break */
40574047
case SQL_DESC_OCTET_LENGTH: len = rec->octet_length; break;
40584048
} while (0);
4059-
PNUMATTR_ASSIGN(SQLLEN, len);
4049+
*pNumAttr = len;
40604050
break;
40614051

40624052
/* SQLULEN */
40634053
case SQL_DESC_LENGTH:
40644054
/* "This information is returned from the SQL_DESC_LENGTH record
40654055
* field of the IRD." */
4066-
PNUMATTR_ASSIGN(SQLULEN, rec->length);
4056+
*pNumAttr = (SQLLEN)rec->length;
40674057
break;
40684058

40694059
/* SQLINTEGER */
@@ -4074,14 +4064,16 @@ SQLRETURN EsSQLColAttributeW(
40744064
case SQL_DESC_CASE_SENSITIVE:
40754065
iint = rec->es_type->case_sensitive;
40764066
break;
4077-
case SQL_DESC_NUM_PREC_RADIX: iint = rec->num_prec_radix; break;
4067+
case SQL_DESC_NUM_PREC_RADIX:
4068+
iint = rec->es_type->num_prec_radix;
4069+
break;
40784070
} while (0);
4079-
PNUMATTR_ASSIGN(SQLINTEGER, iint);
4071+
*pNumAttr = (SQLLEN)iint;
40804072
break;
40814073

40824074

40834075
case SQL_DESC_COUNT:
4084-
PNUMATTR_ASSIGN(SQLSMALLINT, ird->count);
4076+
*pNumAttr = (SQLLEN)ird->count;
40854077
break;
40864078

40874079
default:
@@ -4091,7 +4083,6 @@ SQLRETURN EsSQLColAttributeW(
40914083
/*INDENT-ON*/
40924084

40934085
return SQL_SUCCESS;
4094-
#undef PNUMATTR_ASSIGN
40954086
}
40964087

40974088
/* very simple counter of non-quoted, not-escaped single question marks.

driver/queries.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,7 @@ SQLRETURN EsSQLColAttributeW(
115115
_Out_opt_
116116
SQLSMALLINT *pcbCharAttr,
117117
_Out_opt_
118-
#ifdef _WIN64
119118
SQLLEN *pNumAttr
120-
#else /* _WIN64 */
121-
SQLPOINTER pNumAttr
122-
#endif /* _WIN64 */
123119
);
124120
SQLRETURN EsSQLNumParams(
125121
SQLHSTMT StatementHandle,

test/test_colattribute.cc

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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+
#include <string.h>
11+
#include <math.h>
12+
13+
/* placeholders; will be undef'd and redef'd */
14+
#define SQL_SCALE
15+
#define SQL_RAW
16+
#define SQL_VAL
17+
#define SQL /* attached for troubleshooting purposes */
18+
19+
namespace test {
20+
21+
class ColAttribute : public ::testing::Test, public ConnectedDBC {
22+
};
23+
24+
25+
TEST_F(ColAttribute, NumericAttributePtr_SQLSMALLINT) {
26+
SQLLEN pNumAttr = -1;
27+
28+
#undef SQL_VAL
29+
#undef SQL
30+
#define SQL_VAL "0.98765432100123456789" //20 fractional digits
31+
#define SQL "CAST(" SQL_VAL " AS SCALED_FLOAT)"
32+
33+
const char json_answer[] = "\
34+
{\
35+
\"columns\": [\
36+
{\"name\": \"" SQL "\", \"type\": \"scaled_float\"}\
37+
],\
38+
\"rows\": [\
39+
[" SQL_VAL "]\
40+
]\
41+
}\
42+
";
43+
prepareStatement(json_answer);
44+
45+
ret = SQLFetch(stmt);
46+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
47+
48+
ret = SQLColAttribute(stmt, 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL,
49+
&pNumAttr);
50+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
51+
ASSERT_EQ(pNumAttr, SQL_C_DOUBLE);
52+
}
53+
54+
TEST_F(ColAttribute, NumericAttributePtr_SQLLEN) {
55+
SQLLEN pNumAttr = -1;
56+
57+
#undef SQL_VAL
58+
#undef SQL
59+
#define SQL_VAL "0.98765432100123456789" //20 fractional digits
60+
#define SQL "CAST(" SQL_VAL " AS SCALED_FLOAT)"
61+
62+
const char json_answer[] = "\
63+
{\
64+
\"columns\": [\
65+
{\"name\": \"" SQL "\", \"type\": \"scaled_float\"}\
66+
],\
67+
\"rows\": [\
68+
[" SQL_VAL "]\
69+
]\
70+
}\
71+
";
72+
prepareStatement(json_answer);
73+
74+
ret = SQLFetch(stmt);
75+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
76+
77+
ret = SQLColAttribute(stmt, 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL,
78+
&pNumAttr);
79+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
80+
// https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/display-size
81+
// "SQL_FLOAT, SQL_DOUBLE: 24 (a sign, 15 digits, a decimal point, the
82+
// letter E, a sign, and 3 digits)."
83+
ASSERT_EQ(pNumAttr, 24);
84+
}
85+
86+
TEST_F(ColAttribute, NumericAttributePtr_SQLULEN) {
87+
SQLLEN pNumAttr = -1;
88+
89+
#undef SQL_VAL
90+
#undef SQL
91+
#define SQL_VAL "0.98765432100123456789" //20 fractional digits
92+
#define SQL "CAST(" SQL_VAL " AS SCALED_FLOAT)"
93+
94+
const char json_answer[] = "\
95+
{\
96+
\"columns\": [\
97+
{\"name\": \"" SQL "\", \"type\": \"scaled_float\"}\
98+
],\
99+
\"rows\": [\
100+
[" SQL_VAL "]\
101+
]\
102+
}\
103+
";
104+
prepareStatement(json_answer);
105+
106+
ret = SQLFetch(stmt);
107+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
108+
109+
ret = SQLColAttribute(stmt, 1, SQL_DESC_LENGTH, NULL, 0, NULL, &pNumAttr);
110+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
111+
ASSERT_EQ(pNumAttr, 0);
112+
}
113+
114+
TEST_F(ColAttribute, NumericAttributePtr_SQLINTEGER) {
115+
SQLLEN pNumAttr = -1;
116+
117+
#undef SQL_VAL
118+
#undef SQL
119+
#define SQL_VAL "0.98765432100123456789" //20 fractional digits
120+
#define SQL "CAST(" SQL_VAL " AS SCALED_FLOAT)"
121+
122+
const char json_answer[] = "\
123+
{\
124+
\"columns\": [\
125+
{\"name\": \"" SQL "\", \"type\": \"scaled_float\"}\
126+
],\
127+
\"rows\": [\
128+
[" SQL_VAL "]\
129+
]\
130+
}\
131+
";
132+
prepareStatement(json_answer);
133+
134+
ret = SQLFetch(stmt);
135+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
136+
137+
ret = SQLColAttribute(stmt, 1, SQL_DESC_NUM_PREC_RADIX, NULL, 0, NULL,
138+
&pNumAttr);
139+
ASSERT_TRUE(SQL_SUCCEEDED(ret));
140+
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgettypeinfo-function
141+
// "If the data type is an approximate numeric type, this column contains
142+
// the value 2 to indicate that COLUMN_SIZE specifies a number of bits."
143+
ASSERT_EQ(pNumAttr, 2);
144+
}
145+
146+
147+
148+
149+
} // test namespace
150+

0 commit comments

Comments
 (0)