Skip to content

Data conversions: strings #10

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 3 commits into from
Jul 1, 2018
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.swp
driver/*.swp
cscope.out
14 changes: 7 additions & 7 deletions driver/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, config_attrs_st *attrs)
int n, cnt;
SQLWCHAR urlw[ESODBC_MAX_URL_LEN];
BOOL secure;
long timeout, max_body_size, max_fetch_size;
long long timeout, max_body_size, max_fetch_size;

/*
* build connection URL
Expand Down Expand Up @@ -1002,8 +1002,8 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, config_attrs_st *attrs)
/*
* request timeout for liburl: negative reset to 0
*/
if (! wstr2long(&attrs->timeout, &timeout)) {
ERRH(dbc, "failed to convert '" LWPDL "' to long.",
if (! wstr2llong(&attrs->timeout, &timeout)) {
ERRH(dbc, "failed to convert '" LWPDL "' to long long.",
LWSTR(&attrs->timeout));
goto err;
}
Expand All @@ -1017,8 +1017,8 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, config_attrs_st *attrs)
/*
* set max body size
*/
if (! wstr2long(&attrs->max_body_size, &max_body_size)) {
ERRH(dbc, "failed to convert '" LWPDL "' to long.",
if (! wstr2llong(&attrs->max_body_size, &max_body_size)) {
ERRH(dbc, "failed to convert '" LWPDL "' to long long.",
LWSTR(&attrs->max_body_size));
goto err;
}
Expand All @@ -1034,8 +1034,8 @@ static SQLRETURN process_config(esodbc_dbc_st *dbc, config_attrs_st *attrs)
/*
* set max fetch size
*/
if (! wstr2long(&attrs->max_fetch_size, &max_fetch_size)) {
ERRH(dbc, "failed to convert '" LWPDL "' to long.",
if (! wstr2llong(&attrs->max_fetch_size, &max_fetch_size)) {
ERRH(dbc, "failed to convert '" LWPDL "' to long long.",
LWSTR(&attrs->max_fetch_size));
goto err;
}
Expand Down
106 changes: 97 additions & 9 deletions driver/queries.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <windows.h> /* WideCharToMultiByte() */
#include <float.h>
#include <math.h>
#include <errno.h>

#include "ujdecode.h"
#include "timestamp.h"
Expand Down Expand Up @@ -53,7 +54,7 @@
#define REJECT_AS_OOR(_stmt, _val, _fix_val, _target) /* Out Of Range */ \
do { \
if (_fix_val) { \
ERRH(_stmt, "can't convert value %lld to %s: out of range", \
ERRH(_stmt, "can't convert value %llx to %s: out of range", \
_val, STR(_target)); \
} else { \
ERRH(_stmt, "can't convert value %f to %s: out of range", \
Expand Down Expand Up @@ -972,7 +973,7 @@ static SQLRETURN double_to_numeric(esodbc_rec_st *arec, esodbc_rec_st *irec,
memcpy(numeric->val, (char *)&ullng, sizeof(ullng));
memset(numeric->val+sizeof(ullng), 0, sizeof(numeric->val)-sizeof(ullng));

DBGH(stmt, "double %.15f converted to numeric: .sign=%d, precision=%d "
DBGH(stmt, "double %.6e converted to numeric: .sign=%d, precision=%d "
"(req: %d), .scale=%d (req: %d), .val:`" LCPDL "` (0x%lx).", src,
numeric->sign, numeric->precision, arec->precision,
numeric->scale, arec->scale, (int)sizeof(numeric->val), numeric->val,
Expand Down Expand Up @@ -1318,11 +1319,11 @@ static SQLRETURN double_to_str(esodbc_rec_st *arec, esodbc_rec_st *irec,
}
}

if (wide) { /* 15-16 decimals precision for x64 double (TODO: 32b) */
DBGH(stmt, "double %.16f converted to w-string `" LWPD "` on %zd "
if (wide) {
DBGH(stmt, "double %.6e converted to w-string `" LWPD "` on %zd "
"octets (state: %d; scale: %d).", dbl, buff, octets, state, scale);
} else {
DBGH(stmt, "double %.16f converted to string `" LCPD "` on %zd "
DBGH(stmt, "double %.6e converted to string `" LCPD "` on %zd "
"octets (state: %d; scale: %d).", dbl, buff, octets, state, scale);
}

Expand Down Expand Up @@ -1440,7 +1441,7 @@ static SQLRETURN copy_double(esodbc_rec_st *arec, esodbc_rec_st *irec,
return SQL_ERROR;
}

DBGH(stmt, "REC@0x%p, data_ptr@0x%p, copied double: %f.", arec,
DBGH(stmt, "REC@0x%p, data_ptr@0x%p, copied double: %.6e.", arec,
data_ptr, dbl);

return SQL_SUCCESS;
Expand Down Expand Up @@ -1808,6 +1809,12 @@ static SQLRETURN copy_string(esodbc_rec_st *arec, esodbc_rec_st *irec,
void *data_ptr;
SQLLEN *octet_len_ptr;
esodbc_desc_st *ard, *ird;
SQLSMALLINT ctarget;
long long ll;
unsigned long long ull;
wstr_st wval;
double dbl;
SQLWCHAR *endp;

stmt = arec->desc->hdr.stmt;
ird = stmt->ird;
Expand All @@ -1818,10 +1825,11 @@ static SQLRETURN copy_string(esodbc_rec_st *arec, esodbc_rec_st *irec,
/* pointer to app's buffer */
data_ptr = deferred_address(SQL_DESC_DATA_PTR, pos, arec);

switch (get_c_target_type(arec, irec)) {
switch ((ctarget = get_c_target_type(arec, irec))) {
case SQL_C_CHAR:
return wstr_to_cstr(arec, irec, data_ptr, octet_len_ptr,
wstr, chars_0);
case SQL_C_BINARY: /* treat binary as WCHAR */
case SQL_C_WCHAR:
return wstr_to_wstr(arec, irec, data_ptr, octet_len_ptr,
wstr, chars_0);
Expand All @@ -1836,9 +1844,89 @@ static SQLRETURN copy_string(esodbc_rec_st *arec, esodbc_rec_st *irec,
return wstr_to_time(arec, irec, data_ptr, octet_len_ptr,
wstr, chars_0);

case SQL_C_TINYINT:
case SQL_C_STINYINT:
case SQL_C_SHORT:
case SQL_C_SSHORT:
case SQL_C_LONG:
case SQL_C_SLONG:
case SQL_C_SBIGINT:
/* trim any white spaces */
wval.cnt = chars_0 - 1;
wval.str = (SQLWCHAR *)trim_ws(wstr, &wval.cnt);
/* convert to integer type */
errno = 0;
if (! wstr2llong(&wval, &ll)) { /* string is 0-term -> wcstoll? */
ERRH(stmt, "can't convert `" LWPD "` to long long.", wstr);
RET_HDIAGS(stmt, errno == ERANGE ? SQL_STATE_22003 :
SQL_STATE_22018);
}
DBGH(stmt, "string `" LWPD "` converted to LL=%lld.", wstr, ll);
/* delegate to existing functionality */
return copy_longlong(arec, irec, pos, ll);

case SQL_C_UTINYINT:
case SQL_C_USHORT:
case SQL_C_ULONG:
case SQL_C_UBIGINT:
/* trim any white spaces */
wval.cnt = chars_0 - 1;
wval.str = (SQLWCHAR *)trim_ws(wstr, &wval.cnt);
/* convert to integer type */
errno = 0;
if (! wstr2ullong(&wval, &ull)) { /* string is 0-term: wcstoull? */
ERRH(stmt, "can't convert `" LWPD "` to unsigned long long.",
wstr);
RET_HDIAGS(stmt, errno == ERANGE ? SQL_STATE_22003 :
SQL_STATE_22018);
}
DBGH(stmt, "string `" LWPD "` converted to ULL=%llu.", wstr, ull);
if (ull <= LLONG_MAX) {
/* the cast is safe, delegate to existing functionality */
return copy_longlong(arec, irec, pos, (long long)ull);
}
/* value is larger than what long long can hold: can only convert
* to SQLUBIGINT (and SQLULONG, if it has the same size), or fail
* as out-of-range */
assert(sizeof(SQLUBIGINT) == sizeof(unsigned long long));
if ((ctarget == SQL_C_UBIGINT) || (ctarget == SQL_C_ULONG &&
sizeof(SQLUINTEGER) == sizeof(SQLUBIGINT))) {
/* write out the converted value */
REJECT_IF_NULL_DEST_BUFF(stmt, data_ptr);
*(SQLUBIGINT *)data_ptr = (SQLUBIGINT)ull;
write_out_octets(octet_len_ptr, sizeof(SQLUBIGINT), irec);
DBGH(stmt, "converted string `" LWPD "` to "
"unsigned long long %llu.", wstr, ull);
} else {
REJECT_AS_OOR(stmt, ull, /*fixed?*/TRUE, "non-ULL");
}
break;

case SQL_C_FLOAT:
case SQL_C_DOUBLE:
case SQL_C_NUMERIC:
case SQL_C_BIT:
/* trim any white spaces */
wval.cnt = chars_0 - 1;
wval.str = (SQLWCHAR *)trim_ws(wstr, &wval.cnt);
/* convert to double */
errno = 0;
dbl = wcstod((wchar_t *)wval.str, (wchar_t **)&endp);
DBGH(stmt, "string `" LWPD "` converted to dbl=%.6e.", wstr, dbl);
/* if empty string, non-numeric or under/over-flow, bail out */
if ((! wval.cnt) || (wval.str + wval.cnt != endp) || errno) {
ERRH(stmt, "can't convert `" LWPD "` to double.", wstr);
RET_HDIAGS(stmt, errno == ERANGE ? SQL_STATE_22003 :
SQL_STATE_22018);
}
/* delegate to existing functionality */
return copy_double(arec, irec, pos, dbl);


default:
// FIXME: convert data
FIXME;
BUGH(stmt, "unexpected unhandled data type: %d.",
get_c_target_type(arec, irec));
return SQL_ERROR;
}

return SQL_SUCCESS;
Expand Down
104 changes: 80 additions & 24 deletions driver/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

#include <string.h>
#include <errno.h>

#include "util.h"
#include "log.h"
Expand All @@ -24,46 +25,101 @@ BOOL wstr2bool(wstr_st *val)
return TRUE;
}

BOOL wstr2long(wstr_st *val, long *out)
BOOL wstr2ullong(wstr_st *val, unsigned long long *out)
{
long res = 0, digit;
unsigned long long res, digit;
static const unsigned long long max_div_10 = ULLONG_MAX / 10ULL;
int i = 0;
BOOL negative;

if (val->cnt < 1) {
errno = EINVAL;
return FALSE;
} else if (val->str[0] == L'+') {
i ++;
}

switch (val->str[0]) {
case L'-':
negative = TRUE;
i ++;
break;
case '+':
negative = FALSE;
i ++;
break;
default:
negative = FALSE;
}

for ( ; i < val->cnt; i ++) {
for (res = 0; i < val->cnt; i ++) {
/* is it a number? */
if (val->str[i] < L'0' || L'9' < val->str[i]) {
errno = EINVAL;
return FALSE;
} else {
digit = val->str[i] - L'0';
}
digit = val->str[i] - L'0';
/* would it overflow?*/
if (LONG_MAX - res < digit) {
return FALSE;
assert(sizeof(unsigned long long) == sizeof(uint64_t));
if (i < ESODBC_PRECISION_UINT64 - 1) {
res *= 10;
res += digit;
} else {
/* would it overflow? */
if (max_div_10 < res) {
errno = ERANGE;
return FALSE;
} else {
res *= 10;
}
if (ULLONG_MAX - res < digit) {
errno = ERANGE;
return FALSE;
} else {
res += digit;
}
}
res *= 10;
res += digit;
}
*out = negative ? - res : res;
*out = res;
return TRUE;
}

BOOL wstr2llong(wstr_st *val, long long *out)
{
unsigned long long ull;
wstr_st uval;
int i = 0;
BOOL negative;

if (val->cnt < 1) {
errno = EINVAL;
return FALSE;
} else {
switch (val->str[0]) {
case L'-':
negative = TRUE;
i ++;
break;
case '+':
negative = FALSE;
i ++;
break;
default:
negative = FALSE;
}
}

uval = (wstr_st) {
.str = val->str + i, .cnt = val->cnt - i
};
if (! wstr2ullong(&uval, &ull)) {
return FALSE;
}
if (negative) {
if ((unsigned long long)LLONG_MIN < ull) {
errno = ERANGE;
return FALSE; /* underflow */
} else {
*out = -(long long)ull;
}
} else {
if ((unsigned long long)LLONG_MAX < ull) {
errno = ERANGE;
return FALSE; /* overflow */
} else {
*out = (long long)ull;
}
}
return TRUE;
}


size_t i64tot(int64_t i64, void *buff, BOOL wide)
{
if (wide) {
Expand Down
3 changes: 2 additions & 1 deletion driver/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ typedef struct wstr {
wmemncasecmp((s1)->str, (s2)->str, (s1)->cnt) == 0)

BOOL wstr2bool(wstr_st *val);
BOOL wstr2long(wstr_st *val, long *out);
BOOL wstr2ullong(wstr_st *val, unsigned long long *out);
BOOL wstr2llong(wstr_st *val, long long *out);

/* converts the int types to a C or wide string, returning the string length */
size_t i64tot(int64_t i64, void *buff, BOOL wide);
Expand Down
Loading