Skip to content

Commit

Permalink
Merge pull request #10 from bpintea/feature/string_conversions
Browse files Browse the repository at this point in the history
Data conversions: strings
  • Loading branch information
bpintea authored Jul 1, 2018
2 parents fbd1e00 + f194e5c commit 8bfa95a
Show file tree
Hide file tree
Showing 6 changed files with 637 additions and 41 deletions.
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

0 comments on commit 8bfa95a

Please sign in to comment.