Skip to content

Prepare the driver to work with newer server releases than its own #215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 94 additions & 72 deletions driver/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
#define ESINFO_KEY_VERSION "version"
#define ESINFO_KEY_NUMBER "number"

/* "base" of the version number (how many values supported for each version
* constituent: major, minor, revision) */
#define VER_LEVEL_MULTI 100L


/* structure for one row returned by the ES.
* This is a mirror of elasticsearch_type, with length-or-indicator fields
* for each of the members in elasticsearch_type */
Expand Down Expand Up @@ -1411,28 +1416,6 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs)
goto err;
}

/*
* Version checking mode
*/
if (EQ_CASE_WSTR(&attrs->version_checking,
&MK_WSTR(ESODBC_DSN_VC_STRICT))
|| EQ_CASE_WSTR(&attrs->version_checking,
&MK_WSTR(ESODBC_DSN_VC_MAJOR))
# ifndef NDEBUG
|| EQ_CASE_WSTR(&attrs->version_checking,
&MK_WSTR(ESODBC_DSN_VC_NONE))
# endif /* NDEBUG */
) {
dbc->srv_ver.checking = (unsigned char)attrs->version_checking.str[0];
DBGH(dbc, "version checking mode: %c.", dbc->srv_ver.checking);
} else {
ERRH(dbc, "unknown version checking mode '" LWPDL "'.",
LWSTR(&attrs->version_checking));
SET_HDIAG(dbc, SQL_STATE_HY000, "invalid version checking mode "
"setting", 0);
goto err;
}

/* "multifield leniency" param */
dbc->mfield_lenient = wstr2bool(&attrs->mfield_lenient);
INFOH(dbc, "multifield lenient: %s.",
Expand Down Expand Up @@ -1523,10 +1506,10 @@ void cleanup_dbc(esodbc_dbc_st *dbc)
} else {
assert(dbc->no_types == 0);
}
if (dbc->srv_ver.string.cnt) { /* .str might be compromized by the union */
free(dbc->srv_ver.string.str);
dbc->srv_ver.string.str = NULL;
dbc->srv_ver.string.cnt = 0;
if (dbc->srv_ver.str) {
free(dbc->srv_ver.str);
dbc->srv_ver.str = NULL;
dbc->srv_ver.cnt = 0;
}
if (dbc->catalog.str) {
free(dbc->catalog.str);
Expand Down Expand Up @@ -1652,10 +1635,59 @@ static BOOL parse_es_version_json(esodbc_dbc_st *dbc, cstr_st *rsp_body,
return FALSE;
}
version->str = (SQLWCHAR *)UJReadString(o_number, &version->cnt);
DBGH(dbc, "Elasticsearch'es version number: [%zu] `" LWPDL "`.",
DBGH(dbc, "Elasticsearch's version number: [%zu] `" LWPDL "`.",
version->cnt, LWSTR(version));
return TRUE;
}

/* parses a Major.Minor.Revison version format and returns a numerical value
* of it */
static long version_to_id(wstr_st *ver)
{
SQLWCHAR *stop;
long id, val;

assert(ver->str[ver->cnt] == L'\0');

errno = 0;
stop = ver->str;

/* parse major */
id = wcstol(stop, &stop, /*base*/10);
if (errno || VER_LEVEL_MULTI <= id || id <= 0) {
return -1;
}
if (*stop != L'.') {
return -1;
} else {
stop ++;
}
id *= VER_LEVEL_MULTI;

/* parse minor */
val = wcstol(stop, &stop, /*base*/10);
if (errno || VER_LEVEL_MULTI <= val || val < 0) {
return -1;
}
if (*stop != L'.') {
return -1;
} else {
stop ++;
}
id += val;
id *= VER_LEVEL_MULTI;

/* parse minor */
val = wcstol(stop, &stop, /*base*/10);
if (errno || VER_LEVEL_MULTI <= val || val < 0) {
return -1;
}
id += val;
id *= VER_LEVEL_MULTI;

return id;
}

/*
* Note: not thread safe: only usable on connection setup.
*/
Expand All @@ -1667,16 +1699,17 @@ static SQLRETURN check_server_version(esodbc_dbc_st *dbc)
BOOL is_json;
SQLRETURN ret;
void *state = NULL;
unsigned char ver_checking;
wstr_st own_ver = WSTR_INIT(STR(DRV_VERSION)); /*build-time define*/
wstr_st es_ver, ver_no;
static wstr_st min_es_ver = WSTR_INIT(ESODBC_MIN_ES_VER);
wstr_st es_ver;
cstr_st es_ver_c;
static const wchar_t err_msg_fmt[] = L"Version mismatch between server ("
WPFWP_LDESC ") and driver (" WPFWP_LDESC "). Please use a driver whose"
" version matches that of your server.";
long es_ver_l, min_es_ver_l;
static const wchar_t err_msg_fmt[] = L"Elasticsearch's version ("
WPFWP_LDESC ") is below minimum required (" ESODBC_MIN_ES_VER ") "
"version. Please use a driver whose version matches that of your "
"server.";
/* 32: max length of the version strings for which the explicit message
* above is provided. */
SQLWCHAR wbuff[sizeof(err_msg_fmt)/sizeof(err_msg_fmt[0]) + 2*32];
SQLWCHAR wbuff[sizeof(err_msg_fmt)/sizeof(err_msg_fmt[0]) + 32];
int n;

ret = dbc_curl_set_url(dbc, ESODBC_CURL_ROOT);
Expand Down Expand Up @@ -1711,54 +1744,43 @@ static SQLRETURN check_server_version(esodbc_dbc_st *dbc)
goto err;
}

ver_checking = dbc->srv_ver.checking;
/* version is returned to application, which requires a NTS => +1 for \0 */
dbc->srv_ver.string.str = malloc((n + 1) * sizeof(SQLWCHAR));
if (! dbc->srv_ver.string.str) {
dbc->srv_ver.str = malloc((n + 1) * sizeof(SQLWCHAR));
if (! dbc->srv_ver.str) {
ERRNH(dbc, "OOM for %zu.", (n + 1) * sizeof(SQLWCHAR));
post_diagnostic(dbc, SQL_STATE_HY001, NULL, 0);
goto err;
} else if (is_json) {
memcpy(dbc->srv_ver.string.str, es_ver.str, n * sizeof(SQLWCHAR));
} else if (ascii_c2w(es_ver_c.str, dbc->srv_ver.string.str, n) < 0) {
memcpy(dbc->srv_ver.str, es_ver.str, n * sizeof(SQLWCHAR));
} else if (ascii_c2w(es_ver_c.str, dbc->srv_ver.str, n) < 0) {
/* non-ASCII or empty */
ERRH(dbc, "Elasticsearch version string is invalid.");
BUGH(dbc, "Elasticsearch version string is invalid.");
goto err;
}
dbc->srv_ver.string.cnt = n;
dbc->srv_ver.string.str[n] = 0;
ver_no = dbc->srv_ver.string;
dbc->srv_ver.cnt = n;
dbc->srv_ver.str[n] = 0;

# ifndef NDEBUG
/* strip any qualifiers (=anything following a first `-`) in debug mode */
wtrim_at(&ver_no, L'-');
wtrim_at(&own_ver, L'-');
# endif /* !NDEBUG */

if (tolower(ver_checking) == tolower(ESODBC_DSN_VC_MAJOR[0])) {
/* trim versions to the first dot, i.e. major version */
wtrim_at(&ver_no, L'.');
wtrim_at(&own_ver, L'.');
}

if (tolower(ver_checking) != tolower(ESODBC_DSN_VC_NONE[0])) {
if (! EQ_WSTR(&ver_no, &own_ver)) {
ERRH(dbc, "version mismatch: server: " LWPDL ", "
"own: " LWPDL ".", LWSTR(&ver_no), LWSTR(&own_ver));
n = swprintf(wbuff, sizeof(wbuff)/sizeof(wbuff[0]),
err_msg_fmt, LWSTR(&ver_no), LWSTR(&own_ver));
ret = post_diagnostic(dbc, SQL_STATE_HY000, (n <= 0) ?
L"Version mismatch between server and driver" :
wbuff, 0);
} else {
INFOH(dbc, "server and driver versions aligned to: " LWPDL ".",
LWSTR(&own_ver));
ret = SQL_SUCCESS;
}
# ifndef NDEBUG
min_es_ver_l = version_to_id(&min_es_ver);
assert(0 < min_es_ver_l);
es_ver_l = version_to_id(&dbc->srv_ver);
if (es_ver_l <= 0) {
BUGH(dbc, "failed to parse Elasticsearch version `" LWPDL "`.",
LWSTR(&dbc->srv_ver));
goto err;
}
if (es_ver_l < min_es_ver_l) {
ERRH(dbc, "Elasticsearch version `" LWPDL "` is below minimum required"
" `" LWPDL "`.", LWSTR(&dbc->srv_ver), LWSTR(&min_es_ver));
n = swprintf(wbuff, sizeof(wbuff)/sizeof(wbuff[0]),
err_msg_fmt, LWSTR(&dbc->srv_ver));
ret = post_diagnostic(dbc, SQL_STATE_HY000, (n <= 0) ?
L"Version mismatch between server and driver" :
wbuff, 0);
} else {
WARNH(dbc, "version checking disabled.");
# endif /* !NDEBUG */
INFOH(dbc, "server version (" LWPDL ") %s minimum required "
"(" ESODBC_MIN_ES_VER ").", LWSTR(&dbc->srv_ver),
es_ver_l == min_es_ver_l ? "meets" : "exceeds");
ret = SQL_SUCCESS;
}

free(rsp_body.str);
Expand Down
6 changes: 2 additions & 4 deletions driver/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@
/* maximum DNS attribute value lenght (should be long enought to accomodate a
* decently long FQ file path name) */
#define ESODBC_DSN_MAX_ATTR_LEN 1024
/* sample DSN name provisioned with the installation */
#define ESODBC_DSN_SAMPLE_NAME "Elasticsearch ODBC Sample DSN"

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

/*
* Config defaults
Expand Down Expand Up @@ -176,8 +176,6 @@
/* default of scientific floats printing */
#define ESODBC_DEF_SCI_FLOATS ESODBC_DSN_FLTS_DEF
#define ESODBC_PWD_VAL_SUBST "<redacted>"
/* default version checking mode: strict, major, none (dbg only) */
#define ESODBC_DEF_VERSION_CHECKING ESODBC_DSN_VC_STRICT
#define ESODBC_DEF_MFIELD_LENIENT "true"
#define ESODBC_DEF_ESC_PVA "true"
#define ESODBC_DEF_IDX_INC_FROZEN "false"
Expand Down
3 changes: 0 additions & 3 deletions driver/dsn.c
Original file line number Diff line number Diff line change
Expand Up @@ -880,9 +880,6 @@ void assign_dsn_defaults(esodbc_dsn_attrs_st *attrs)
res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_MAX_BODY_SIZE_MB),
&MK_WSTR(ESODBC_DEF_MAX_BODY_SIZE_MB),
/*overwrite?*/FALSE);
res |= assign_dsn_attr(attrs, &MK_WSTR(ESODBC_DSN_VERSION_CHECKING),
&MK_WSTR(ESODBC_DEF_VERSION_CHECKING),
/*overwrite?*/FALSE);

res |= assign_dsn_attr(attrs,
&MK_WSTR(ESODBC_DSN_APPLY_TZ), &MK_WSTR(ESODBC_DEF_APPLY_TZ),
Expand Down
4 changes: 0 additions & 4 deletions driver/dsn.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@
#define ESODBC_DSN_CMPSS_AUTO "auto"
#define ESODBC_DSN_CMPSS_ON "on"
#define ESODBC_DSN_CMPSS_OFF "off"
/* VersionChecking values */
#define ESODBC_DSN_VC_STRICT "strict"
#define ESODBC_DSN_VC_MAJOR "major"
#define ESODBC_DSN_VC_NONE "none"
/* Floats printing */
#define ESODBC_DSN_FLTS_DEF "default"
#define ESODBC_DSN_FLTS_SCI "scientific"
Expand Down
5 changes: 1 addition & 4 deletions driver/handles.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,7 @@ typedef struct struct_dbc {
wstr_st dsn; /* data source name SQLGetInfo(SQL_DATA_SOURCE_NAME) */
wstr_st server; /* ~ name; requested with SQLGetInfo(SQL_SERVER_NAME) */
wstr_st catalog; /* cached value; checked against if app setting it */
union {
wstr_st string; /* version: SQLGetInfo(SQL_DBMS_VER) */
unsigned char checking; /* first letter of DSN config option value */
} srv_ver; /* server version */
wstr_st srv_ver; /* server version: SQLGetInfo(SQL_DBMS_VER) */
cstr_st url; /* SQL URL (posts) */
cstr_st close_url; /* SQL close URL (posts) */
cstr_st root_url; /* root URL (gets) */
Expand Down
4 changes: 2 additions & 2 deletions driver/info.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ static SQLRETURN getinfo_dbms_product(
StringLengthPtr);
case SQL_DBMS_VER:
DBGH(dbc, "requested: DBMS version (`" LWPDL "`).",
LWSTR(&dbc->srv_ver.string));
return write_wstr(dbc, InfoValue, &dbc->srv_ver.string,
LWSTR(&dbc->srv_ver));
return write_wstr(dbc, InfoValue, &dbc->srv_ver,
BufferLength, StringLengthPtr);
}
*handled = FALSE;
Expand Down
36 changes: 32 additions & 4 deletions driver/queries.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ static SQLRETURN statement_params_len_cbor(esodbc_stmt_st *stmt,
size_t *enc_len, size_t *conv_len);

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

static BOOL print_tz_param(long tz_dst_offt)
{
Expand Down Expand Up @@ -142,10 +143,18 @@ static inline BOOL update_tz_param()

BOOL queries_init()
{
char *ptr;

/* for the casts in this module */
ASSERT_INTEGER_TYPES_EQUAL(wchar_t, SQLWCHAR);
ASSERT_INTEGER_TYPES_EQUAL(char, SQLCHAR);

/* trim qualifiers */
ptr = strchr(version.str, '-');
if (ptr) {
version.cnt = ptr - version.str;
}

/* needed to correctly run the unit tests */
return update_tz_param();
}
Expand Down Expand Up @@ -2973,7 +2982,9 @@ static SQLRETURN statement_len_cbor(esodbc_stmt_st *stmt, size_t *enc_len,
/* "time_zone": "-05:45" */
bodylen += cbor_str_obj_len(sizeof(REQ_KEY_TIMEZONE) - 1);
bodylen += cbor_str_obj_len(tz_param.cnt); /* lax len */
*keys += 3; /* field_m._val., idx._inc._frozen, time_zone */
bodylen += cbor_str_obj_len(sizeof(REQ_KEY_VERSION) - 1);
bodylen += cbor_str_obj_len(version.cnt);
*keys += 4; /* field_m._val., idx._inc._frozen, time_zone, version */
}
bodylen += cbor_str_obj_len(sizeof(REQ_KEY_MODE) - 1);
bodylen += cbor_str_obj_len(sizeof(REQ_VAL_MODE) - 1);
Expand Down Expand Up @@ -3033,9 +3044,12 @@ static SQLRETURN statement_len_json(esodbc_stmt_st *stmt, size_t *outlen)
/* "time_zone": "-05:45" */
bodylen += sizeof(JSON_KEY_TIMEZONE) - 1;
bodylen += tz_param.cnt;
/* "version": */
bodylen += sizeof(JSON_KEY_VERSION) - 1;
bodylen += version.cnt + /* 2x`"` */2;
}
bodylen += sizeof(JSON_KEY_VAL_MODE) - 1; /* "mode": */
bodylen += sizeof(JSON_KEY_CLT_ID) - 1; /* "client_id": */
bodylen += sizeof(JSON_KEY_VAL_MODE) - 1; /* "mode": "ODBC" */
bodylen += sizeof(JSON_KEY_CLT_ID) - 1; /* "client_id": "odbcXX" */
/* TODO: request_/page_timeout */
bodylen += 1; /* } */

Expand Down Expand Up @@ -3307,6 +3321,12 @@ static SQLRETURN serialize_to_cbor(esodbc_stmt_st *stmt, cstr_st *dest,
}
err = cbor_encode_text_string(&map, tz.str, tz.cnt);
FAIL_ON_CBOR_ERR(stmt, err);
/* version */
err = cbor_encode_text_string(&map, REQ_KEY_VERSION,
sizeof(REQ_KEY_VERSION) - 1);
FAIL_ON_CBOR_ERR(stmt, err);
err = cbor_encode_text_string(&map, version.str, version.cnt);
FAIL_ON_CBOR_ERR(stmt, err);
}
/* mode : ODBC */
err = cbor_encode_text_string(&map, REQ_KEY_MODE,
Expand Down Expand Up @@ -3424,10 +3444,18 @@ static SQLRETURN serialize_to_json(esodbc_stmt_st *stmt, cstr_st *dest)
sizeof(JSON_VAL_TIMEZONE_Z) - 1);
pos += sizeof(JSON_VAL_TIMEZONE_Z) - 1;
}
/* "version": ... */
memcpy(body + pos, JSON_KEY_VERSION, sizeof(JSON_KEY_VERSION) - 1);
pos += sizeof(JSON_KEY_VERSION) - 1;
body[pos ++] = '"';
memcpy(body + pos, version.str, version.cnt);
pos += version.cnt;
body[pos ++] = '"';
}
/* "mode": */
/* "mode": "ODBC" */
memcpy(body + pos, JSON_KEY_VAL_MODE, sizeof(JSON_KEY_VAL_MODE) - 1);
pos += sizeof(JSON_KEY_VAL_MODE) - 1;
/* "client_id": "odbcXX" */
memcpy(body + pos, JSON_KEY_CLT_ID, sizeof(JSON_KEY_CLT_ID) - 1);
pos += sizeof(JSON_KEY_CLT_ID) - 1;
body[pos ++] = '}';
Expand Down
Loading