Skip to content

Commit ef53ecb

Browse files
committed
Prepare the driver to work with newer server releases than its own (#215)
* relax server's version checking This commit relaxes the version check the driver does on connect: this version compatibility is migrated on the server and the driver only checks that server's version is more recent than a set minimum (that of the release that applies version policy on the server). * remove unused defines remove superfluous defines * add 'version' field into request objects This commit adds the newly required 'version' field into the REST request objects. * only add the "version" to "query" objects This commit fixes two issues: - the "version" field is only required for "query" requst objects, but not needed for "cursor" (paginating) ones; - the lenght of the version field value is now correctly estimated in CBOR encoding. * update unit tests to "version" in query-req only Update position of the "version" field in expected output of the request object. (cherry picked from commit 8b045b1)
1 parent f689ecc commit ef53ecb

File tree

10 files changed

+145
-97
lines changed

10 files changed

+145
-97
lines changed

driver/connect.c

Lines changed: 94 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@
9797
#define ESINFO_KEY_VERSION "version"
9898
#define ESINFO_KEY_NUMBER "number"
9999

100+
/* "base" of the version number (how many values supported for each version
101+
* constituent: major, minor, revision) */
102+
#define VER_LEVEL_MULTI 100L
103+
104+
100105
/* structure for one row returned by the ES.
101106
* This is a mirror of elasticsearch_type, with length-or-indicator fields
102107
* for each of the members in elasticsearch_type */
@@ -1411,28 +1416,6 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs)
14111416
goto err;
14121417
}
14131418

1414-
/*
1415-
* Version checking mode
1416-
*/
1417-
if (EQ_CASE_WSTR(&attrs->version_checking,
1418-
&MK_WSTR(ESODBC_DSN_VC_STRICT))
1419-
|| EQ_CASE_WSTR(&attrs->version_checking,
1420-
&MK_WSTR(ESODBC_DSN_VC_MAJOR))
1421-
# ifndef NDEBUG
1422-
|| EQ_CASE_WSTR(&attrs->version_checking,
1423-
&MK_WSTR(ESODBC_DSN_VC_NONE))
1424-
# endif /* NDEBUG */
1425-
) {
1426-
dbc->srv_ver.checking = (unsigned char)attrs->version_checking.str[0];
1427-
DBGH(dbc, "version checking mode: %c.", dbc->srv_ver.checking);
1428-
} else {
1429-
ERRH(dbc, "unknown version checking mode '" LWPDL "'.",
1430-
LWSTR(&attrs->version_checking));
1431-
SET_HDIAG(dbc, SQL_STATE_HY000, "invalid version checking mode "
1432-
"setting", 0);
1433-
goto err;
1434-
}
1435-
14361419
/* "multifield leniency" param */
14371420
dbc->mfield_lenient = wstr2bool(&attrs->mfield_lenient);
14381421
INFOH(dbc, "multifield lenient: %s.",
@@ -1523,10 +1506,10 @@ void cleanup_dbc(esodbc_dbc_st *dbc)
15231506
} else {
15241507
assert(dbc->no_types == 0);
15251508
}
1526-
if (dbc->srv_ver.string.cnt) { /* .str might be compromized by the union */
1527-
free(dbc->srv_ver.string.str);
1528-
dbc->srv_ver.string.str = NULL;
1529-
dbc->srv_ver.string.cnt = 0;
1509+
if (dbc->srv_ver.str) {
1510+
free(dbc->srv_ver.str);
1511+
dbc->srv_ver.str = NULL;
1512+
dbc->srv_ver.cnt = 0;
15301513
}
15311514
if (dbc->catalog.str) {
15321515
free(dbc->catalog.str);
@@ -1652,10 +1635,59 @@ static BOOL parse_es_version_json(esodbc_dbc_st *dbc, cstr_st *rsp_body,
16521635
return FALSE;
16531636
}
16541637
version->str = (SQLWCHAR *)UJReadString(o_number, &version->cnt);
1655-
DBGH(dbc, "Elasticsearch'es version number: [%zu] `" LWPDL "`.",
1638+
DBGH(dbc, "Elasticsearch's version number: [%zu] `" LWPDL "`.",
16561639
version->cnt, LWSTR(version));
16571640
return TRUE;
16581641
}
1642+
1643+
/* parses a Major.Minor.Revison version format and returns a numerical value
1644+
* of it */
1645+
static long version_to_id(wstr_st *ver)
1646+
{
1647+
SQLWCHAR *stop;
1648+
long id, val;
1649+
1650+
assert(ver->str[ver->cnt] == L'\0');
1651+
1652+
errno = 0;
1653+
stop = ver->str;
1654+
1655+
/* parse major */
1656+
id = wcstol(stop, &stop, /*base*/10);
1657+
if (errno || VER_LEVEL_MULTI <= id || id <= 0) {
1658+
return -1;
1659+
}
1660+
if (*stop != L'.') {
1661+
return -1;
1662+
} else {
1663+
stop ++;
1664+
}
1665+
id *= VER_LEVEL_MULTI;
1666+
1667+
/* parse minor */
1668+
val = wcstol(stop, &stop, /*base*/10);
1669+
if (errno || VER_LEVEL_MULTI <= val || val < 0) {
1670+
return -1;
1671+
}
1672+
if (*stop != L'.') {
1673+
return -1;
1674+
} else {
1675+
stop ++;
1676+
}
1677+
id += val;
1678+
id *= VER_LEVEL_MULTI;
1679+
1680+
/* parse minor */
1681+
val = wcstol(stop, &stop, /*base*/10);
1682+
if (errno || VER_LEVEL_MULTI <= val || val < 0) {
1683+
return -1;
1684+
}
1685+
id += val;
1686+
id *= VER_LEVEL_MULTI;
1687+
1688+
return id;
1689+
}
1690+
16591691
/*
16601692
* Note: not thread safe: only usable on connection setup.
16611693
*/
@@ -1667,16 +1699,17 @@ static SQLRETURN check_server_version(esodbc_dbc_st *dbc)
16671699
BOOL is_json;
16681700
SQLRETURN ret;
16691701
void *state = NULL;
1670-
unsigned char ver_checking;
1671-
wstr_st own_ver = WSTR_INIT(STR(DRV_VERSION)); /*build-time define*/
1672-
wstr_st es_ver, ver_no;
1702+
static wstr_st min_es_ver = WSTR_INIT(ESODBC_MIN_ES_VER);
1703+
wstr_st es_ver;
16731704
cstr_st es_ver_c;
1674-
static const wchar_t err_msg_fmt[] = L"Version mismatch between server ("
1675-
WPFWP_LDESC ") and driver (" WPFWP_LDESC "). Please use a driver whose"
1676-
" version matches that of your server.";
1705+
long es_ver_l, min_es_ver_l;
1706+
static const wchar_t err_msg_fmt[] = L"Elasticsearch's version ("
1707+
WPFWP_LDESC ") is below minimum required (" ESODBC_MIN_ES_VER ") "
1708+
"version. Please use a driver whose version matches that of your "
1709+
"server.";
16771710
/* 32: max length of the version strings for which the explicit message
16781711
* above is provided. */
1679-
SQLWCHAR wbuff[sizeof(err_msg_fmt)/sizeof(err_msg_fmt[0]) + 2*32];
1712+
SQLWCHAR wbuff[sizeof(err_msg_fmt)/sizeof(err_msg_fmt[0]) + 32];
16801713
int n;
16811714

16821715
ret = dbc_curl_set_url(dbc, ESODBC_CURL_ROOT);
@@ -1711,54 +1744,43 @@ static SQLRETURN check_server_version(esodbc_dbc_st *dbc)
17111744
goto err;
17121745
}
17131746

1714-
ver_checking = dbc->srv_ver.checking;
17151747
/* version is returned to application, which requires a NTS => +1 for \0 */
1716-
dbc->srv_ver.string.str = malloc((n + 1) * sizeof(SQLWCHAR));
1717-
if (! dbc->srv_ver.string.str) {
1748+
dbc->srv_ver.str = malloc((n + 1) * sizeof(SQLWCHAR));
1749+
if (! dbc->srv_ver.str) {
17181750
ERRNH(dbc, "OOM for %zu.", (n + 1) * sizeof(SQLWCHAR));
17191751
post_diagnostic(dbc, SQL_STATE_HY001, NULL, 0);
17201752
goto err;
17211753
} else if (is_json) {
1722-
memcpy(dbc->srv_ver.string.str, es_ver.str, n * sizeof(SQLWCHAR));
1723-
} else if (ascii_c2w(es_ver_c.str, dbc->srv_ver.string.str, n) < 0) {
1754+
memcpy(dbc->srv_ver.str, es_ver.str, n * sizeof(SQLWCHAR));
1755+
} else if (ascii_c2w(es_ver_c.str, dbc->srv_ver.str, n) < 0) {
17241756
/* non-ASCII or empty */
1725-
ERRH(dbc, "Elasticsearch version string is invalid.");
1757+
BUGH(dbc, "Elasticsearch version string is invalid.");
17261758
goto err;
17271759
}
1728-
dbc->srv_ver.string.cnt = n;
1729-
dbc->srv_ver.string.str[n] = 0;
1730-
ver_no = dbc->srv_ver.string;
1760+
dbc->srv_ver.cnt = n;
1761+
dbc->srv_ver.str[n] = 0;
17311762

1732-
# ifndef NDEBUG
1733-
/* strip any qualifiers (=anything following a first `-`) in debug mode */
1734-
wtrim_at(&ver_no, L'-');
1735-
wtrim_at(&own_ver, L'-');
1736-
# endif /* !NDEBUG */
1737-
1738-
if (tolower(ver_checking) == tolower(ESODBC_DSN_VC_MAJOR[0])) {
1739-
/* trim versions to the first dot, i.e. major version */
1740-
wtrim_at(&ver_no, L'.');
1741-
wtrim_at(&own_ver, L'.');
1742-
}
1743-
1744-
if (tolower(ver_checking) != tolower(ESODBC_DSN_VC_NONE[0])) {
1745-
if (! EQ_WSTR(&ver_no, &own_ver)) {
1746-
ERRH(dbc, "version mismatch: server: " LWPDL ", "
1747-
"own: " LWPDL ".", LWSTR(&ver_no), LWSTR(&own_ver));
1748-
n = swprintf(wbuff, sizeof(wbuff)/sizeof(wbuff[0]),
1749-
err_msg_fmt, LWSTR(&ver_no), LWSTR(&own_ver));
1750-
ret = post_diagnostic(dbc, SQL_STATE_HY000, (n <= 0) ?
1751-
L"Version mismatch between server and driver" :
1752-
wbuff, 0);
1753-
} else {
1754-
INFOH(dbc, "server and driver versions aligned to: " LWPDL ".",
1755-
LWSTR(&own_ver));
1756-
ret = SQL_SUCCESS;
1757-
}
1758-
# ifndef NDEBUG
1763+
min_es_ver_l = version_to_id(&min_es_ver);
1764+
assert(0 < min_es_ver_l);
1765+
es_ver_l = version_to_id(&dbc->srv_ver);
1766+
if (es_ver_l <= 0) {
1767+
BUGH(dbc, "failed to parse Elasticsearch version `" LWPDL "`.",
1768+
LWSTR(&dbc->srv_ver));
1769+
goto err;
1770+
}
1771+
if (es_ver_l < min_es_ver_l) {
1772+
ERRH(dbc, "Elasticsearch version `" LWPDL "` is below minimum required"
1773+
" `" LWPDL "`.", LWSTR(&dbc->srv_ver), LWSTR(&min_es_ver));
1774+
n = swprintf(wbuff, sizeof(wbuff)/sizeof(wbuff[0]),
1775+
err_msg_fmt, LWSTR(&dbc->srv_ver));
1776+
ret = post_diagnostic(dbc, SQL_STATE_HY000, (n <= 0) ?
1777+
L"Version mismatch between server and driver" :
1778+
wbuff, 0);
17591779
} else {
1760-
WARNH(dbc, "version checking disabled.");
1761-
# endif /* !NDEBUG */
1780+
INFOH(dbc, "server version (" LWPDL ") %s minimum required "
1781+
"(" ESODBC_MIN_ES_VER ").", LWSTR(&dbc->srv_ver),
1782+
es_ver_l == min_es_ver_l ? "meets" : "exceeds");
1783+
ret = SQL_SUCCESS;
17621784
}
17631785

17641786
free(rsp_body.str);

driver/defs.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,6 @@
122122
/* maximum DNS attribute value lenght (should be long enought to accomodate a
123123
* decently long FQ file path name) */
124124
#define ESODBC_DSN_MAX_ATTR_LEN 1024
125-
/* sample DSN name provisioned with the installation */
126-
#define ESODBC_DSN_SAMPLE_NAME "Elasticsearch ODBC Sample DSN"
127125

128126
/* SQL plugin's REST endpoint for SQL */
129127
#define ELASTIC_SQL_PATH "/_sql"
@@ -139,6 +137,8 @@
139137
#define ESODBC_DRIVER_VER STR(DRV_VERSION) \
140138
"(" STR(DRV_SRC_VER) "," STR(DRV_ENCODING) "," STR(DRV_BUILD_TYPE) ")"
141139
#define ESODBC_ELASTICSEARCH_NAME "Elasticsearch"
140+
/* the driver will work with an ES node of this version and higher */
141+
#define ESODBC_MIN_ES_VER "7.7.0"
142142

143143
/*
144144
* Config defaults
@@ -176,8 +176,6 @@
176176
/* default of scientific floats printing */
177177
#define ESODBC_DEF_SCI_FLOATS ESODBC_DSN_FLTS_DEF
178178
#define ESODBC_PWD_VAL_SUBST "<redacted>"
179-
/* default version checking mode: strict, major, none (dbg only) */
180-
#define ESODBC_DEF_VERSION_CHECKING ESODBC_DSN_VC_STRICT
181179
#define ESODBC_DEF_MFIELD_LENIENT "true"
182180
#define ESODBC_DEF_ESC_PVA "true"
183181
#define ESODBC_DEF_IDX_INC_FROZEN "false"

driver/dsn.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -880,9 +880,6 @@ void assign_dsn_defaults(esodbc_dsn_attrs_st *attrs)
880880
res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB),
881881
&MK_WSTR(ESODBC_DEF_MAX_BODY_SIZE_MB),
882882
/*overwrite?*/FALSE);
883-
res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_VERSION_CHECKING),
884-
&MK_WSTR(ESODBC_DEF_VERSION_CHECKING),
885-
/*overwrite?*/FALSE);
886883

887884
res |= assign_dsn_attr(attrs,
888885
&MK_WSTR(ESODBC_DSN_APPLY_TZ), &MK_WSTR(ESODBC_DEF_APPLY_TZ),

driver/dsn.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@
5151
#define ESODBC_DSN_CMPSS_AUTO "auto"
5252
#define ESODBC_DSN_CMPSS_ON "on"
5353
#define ESODBC_DSN_CMPSS_OFF "off"
54-
/* VersionChecking values */
55-
#define ESODBC_DSN_VC_STRICT "strict"
56-
#define ESODBC_DSN_VC_MAJOR "major"
57-
#define ESODBC_DSN_VC_NONE "none"
5854
/* Floats printing */
5955
#define ESODBC_DSN_FLTS_DEF "default"
6056
#define ESODBC_DSN_FLTS_SCI "scientific"

driver/handles.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,7 @@ typedef struct struct_dbc {
132132
wstr_st dsn; /* data source name SQLGetInfo(SQL_DATA_SOURCE_NAME) */
133133
wstr_st server; /* ~ name; requested with SQLGetInfo(SQL_SERVER_NAME) */
134134
wstr_st catalog; /* cached value; checked against if app setting it */
135-
union {
136-
wstr_st string; /* version: SQLGetInfo(SQL_DBMS_VER) */
137-
unsigned char checking; /* first letter of DSN config option value */
138-
} srv_ver; /* server version */
135+
wstr_st srv_ver; /* server version: SQLGetInfo(SQL_DBMS_VER) */
139136
cstr_st url; /* SQL URL (posts) */
140137
cstr_st close_url; /* SQL close URL (posts) */
141138
cstr_st root_url; /* root URL (gets) */

driver/info.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,8 @@ static SQLRETURN getinfo_dbms_product(
306306
StringLengthPtr);
307307
case SQL_DBMS_VER:
308308
DBGH(dbc, "requested: DBMS version (`" LWPDL "`).",
309-
LWSTR(&dbc->srv_ver.string));
310-
return write_wstr(dbc, InfoValue, &dbc->srv_ver.string,
309+
LWSTR(&dbc->srv_ver));
310+
return write_wstr(dbc, InfoValue, &dbc->srv_ver,
311311
BufferLength, StringLengthPtr);
312312
}
313313
*handled = FALSE;

driver/queries.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ static SQLRETURN statement_params_len_cbor(esodbc_stmt_st *stmt,
4141
size_t *enc_len, size_t *conv_len);
4242

4343
static thread_local cstr_st tz_param;
44+
static cstr_st version = CSTR_INIT(STR(DRV_VERSION)); /* build-time define */
4445

4546
static BOOL print_tz_param(long tz_dst_offt)
4647
{
@@ -142,10 +143,18 @@ static inline BOOL update_tz_param()
142143

143144
BOOL queries_init()
144145
{
146+
char *ptr;
147+
145148
/* for the casts in this module */
146149
ASSERT_INTEGER_TYPES_EQUAL(wchar_t, SQLWCHAR);
147150
ASSERT_INTEGER_TYPES_EQUAL(char, SQLCHAR);
148151

152+
/* trim qualifiers */
153+
ptr = strchr(version.str, '-');
154+
if (ptr) {
155+
version.cnt = ptr - version.str;
156+
}
157+
149158
/* needed to correctly run the unit tests */
150159
return update_tz_param();
151160
}
@@ -2973,7 +2982,9 @@ static SQLRETURN statement_len_cbor(esodbc_stmt_st *stmt, size_t *enc_len,
29732982
/* "time_zone": "-05:45" */
29742983
bodylen += cbor_str_obj_len(sizeof(REQ_KEY_TIMEZONE) - 1);
29752984
bodylen += cbor_str_obj_len(tz_param.cnt); /* lax len */
2976-
*keys += 3; /* field_m._val., idx._inc._frozen, time_zone */
2985+
bodylen += cbor_str_obj_len(sizeof(REQ_KEY_VERSION) - 1);
2986+
bodylen += cbor_str_obj_len(version.cnt);
2987+
*keys += 4; /* field_m._val., idx._inc._frozen, time_zone, version */
29772988
}
29782989
bodylen += cbor_str_obj_len(sizeof(REQ_KEY_MODE) - 1);
29792990
bodylen += cbor_str_obj_len(sizeof(REQ_VAL_MODE) - 1);
@@ -3033,9 +3044,12 @@ static SQLRETURN statement_len_json(esodbc_stmt_st *stmt, size_t *outlen)
30333044
/* "time_zone": "-05:45" */
30343045
bodylen += sizeof(JSON_KEY_TIMEZONE) - 1;
30353046
bodylen += tz_param.cnt;
3047+
/* "version": */
3048+
bodylen += sizeof(JSON_KEY_VERSION) - 1;
3049+
bodylen += version.cnt + /* 2x`"` */2;
30363050
}
3037-
bodylen += sizeof(JSON_KEY_VAL_MODE) - 1; /* "mode": */
3038-
bodylen += sizeof(JSON_KEY_CLT_ID) - 1; /* "client_id": */
3051+
bodylen += sizeof(JSON_KEY_VAL_MODE) - 1; /* "mode": "ODBC" */
3052+
bodylen += sizeof(JSON_KEY_CLT_ID) - 1; /* "client_id": "odbcXX" */
30393053
/* TODO: request_/page_timeout */
30403054
bodylen += 1; /* } */
30413055

@@ -3307,6 +3321,12 @@ static SQLRETURN serialize_to_cbor(esodbc_stmt_st *stmt, cstr_st *dest,
33073321
}
33083322
err = cbor_encode_text_string(&map, tz.str, tz.cnt);
33093323
FAIL_ON_CBOR_ERR(stmt, err);
3324+
/* version */
3325+
err = cbor_encode_text_string(&map, REQ_KEY_VERSION,
3326+
sizeof(REQ_KEY_VERSION) - 1);
3327+
FAIL_ON_CBOR_ERR(stmt, err);
3328+
err = cbor_encode_text_string(&map, version.str, version.cnt);
3329+
FAIL_ON_CBOR_ERR(stmt, err);
33103330
}
33113331
/* mode : ODBC */
33123332
err = cbor_encode_text_string(&map, REQ_KEY_MODE,
@@ -3424,10 +3444,18 @@ static SQLRETURN serialize_to_json(esodbc_stmt_st *stmt, cstr_st *dest)
34243444
sizeof(JSON_VAL_TIMEZONE_Z) - 1);
34253445
pos += sizeof(JSON_VAL_TIMEZONE_Z) - 1;
34263446
}
3447+
/* "version": ... */
3448+
memcpy(body + pos, JSON_KEY_VERSION, sizeof(JSON_KEY_VERSION) - 1);
3449+
pos += sizeof(JSON_KEY_VERSION) - 1;
3450+
body[pos ++] = '"';
3451+
memcpy(body + pos, version.str, version.cnt);
3452+
pos += version.cnt;
3453+
body[pos ++] = '"';
34273454
}
3428-
/* "mode": */
3455+
/* "mode": "ODBC" */
34293456
memcpy(body + pos, JSON_KEY_VAL_MODE, sizeof(JSON_KEY_VAL_MODE) - 1);
34303457
pos += sizeof(JSON_KEY_VAL_MODE) - 1;
3458+
/* "client_id": "odbcXX" */
34313459
memcpy(body + pos, JSON_KEY_CLT_ID, sizeof(JSON_KEY_CLT_ID) - 1);
34323460
pos += sizeof(JSON_KEY_CLT_ID) - 1;
34333461
body[pos ++] = '}';

0 commit comments

Comments
 (0)