Skip to content

Interval type support #80

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
Dec 17, 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
477 changes: 371 additions & 106 deletions driver/connect.c

Large diffs are not rendered by default.

1,572 changes: 1,437 additions & 135 deletions driver/convert.c

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions driver/convert.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,7 @@ SQLRETURN c2sql_varchar(esodbc_rec_st *arec, esodbc_rec_st *irec,
SQLULEN pos, char *dest, size_t *len);
SQLRETURN c2sql_timestamp(esodbc_rec_st *arec, esodbc_rec_st *irec,
SQLULEN pos, char *dest, size_t *len);
SQLRETURN c2sql_interval(esodbc_rec_st *arec, esodbc_rec_st *irec,
SQLULEN pos, char *dest, size_t *len);

#endif /* __CONVERT_H__ */
87 changes: 22 additions & 65 deletions driver/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@
#define ESODBC_MAX_FIX_PRECISION ESODBC_PRECISION_UINT64
/* maximum floating numeric precision */
#define ESODBC_MAX_FLT_PRECISION ESODBC_PRECISION_DOUBLE
/* maximum seconds precision */
#define ESODBC_MAX_SEC_PRECISION ESODBC_PRECISION_UINT64
/* maximum seconds precision (i.e. sub-second accuracy) */
/* Seconds precision is currently 3, with ES/SQL's ISO8601 millis.
* (Should move to 9 with nanosecond implementation) */
#define ESODBC_MAX_SEC_PRECISION 9
/*
* standard specified defaults:
* https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetdescfield-function##record-fields
Expand All @@ -88,11 +90,26 @@
#define ESODBC_DEF_DATETIME_PRECISION 0
#define ESODBC_DEF_TIMESTAMP_PRECISION 6
/* interval */
#define ESODBC_DEF_INTVL_WS_PRECISION 6
#define ESODBC_DEF_INTVL_WOS_DT_PREC 2
#define ESODBC_DEF_IVL_WS_PRECISION 6
#define ESODBC_DEF_IVL_WOS_DT_PREC 2
/* decimal, numeric */
#define ESODBC_DEF_DECNUM_SCALE 0

/*
* interval lead precision maxes:
* - YEAR,MONTH: LONG_MAX (2147483647)
* - DAY: 106751991167300
* - HOUR: 2562047788015215
* - MINUTE: 153722867280912930 (0x0222,2222,2222,2222)
* - SECOND: LLONG_MAX (9223372036854775807)
* with Duration/Period of 11.0.1+13-LTS Win x64 O-JVM.
* TODO: reasoning for these maxes mix??
*/
#define ESODBC_MAX_IVL_YEAR_LEAD_PREC (sizeof("-2147483647") - 1)
#define ESODBC_MAX_IVL_MONTH_LEAD_PREC (sizeof("-2147483647") - 1)
#define ESODBC_MAX_IVL_DAY_LEAD_PREC (sizeof("-106751991167300") - 1)
#define ESODBC_MAX_IVL_HOUR_LEAD_PREC (sizeof("-2562047788015215") - 1)
#define ESODBC_MAX_IVL_MINUTE_LEAD_PREC (sizeof("-153722867280912930") - 1)
#define ESODBC_MAX_IVL_SECOND_LEAD_PREC (sizeof("-9223372036854775807") - 1)



Expand Down Expand Up @@ -359,66 +376,6 @@
#define ESODBC_DATE_TEMPLATE "yyyy-mm-ddT"
#define ESODBC_TIME_TEMPLATE "hh:mm:ss.9999999"

/*
* ES-to-C-SQL mappings
* DATA_TYPE(SYS TYPES) : SQL_<type> -> SQL_C_<type>
* Collected here for a quick overview (and easy change); can't be automated.
*/
/* -6: SQL_TINYINT -> SQL_C_TINYINT */
#define ESODBC_ES_TO_CSQL_BYTE SQL_C_TINYINT
#define ESODBC_ES_TO_SQL_BYTE SQL_TINYINT
/* 5: SQL_SMALLINT -> SQL_C_SHORT */
#define ESODBC_ES_TO_CSQL_SHORT SQL_C_SSHORT
#define ESODBC_ES_TO_SQL_SHORT SQL_SMALLINT
/* 4: SQL_INTEGER -> SQL_C_LONG */
#define ESODBC_ES_TO_CSQL_INTEGER SQL_C_SLONG
#define ESODBC_ES_TO_SQL_INTEGER SQL_INTEGER
/* -5: SQL_BIGINT -> SQL_C_SBIGINT */
#define ESODBC_ES_TO_CSQL_LONG SQL_C_SBIGINT
#define ESODBC_ES_TO_SQL_LONG SQL_BIGINT
/* 6: SQL_FLOAT -> SQL_C_DOUBLE */
#define ESODBC_ES_TO_CSQL_HALF_FLOAT SQL_C_DOUBLE
#define ESODBC_ES_TO_SQL_HALF_FLOAT SQL_FLOAT
/* 6: SQL_FLOAT -> SQL_C_DOUBLE */
#define ESODBC_ES_TO_CSQL_SCALED_FLOAT SQL_C_DOUBLE
#define ESODBC_ES_TO_SQL_SCALED_FLOAT SQL_FLOAT
/* 7: SQL_REAL -> SQL_C_DOUBLE */
#define ESODBC_ES_TO_CSQL_FLOAT SQL_C_FLOAT
#define ESODBC_ES_TO_SQL_FLOAT SQL_REAL
/* 8: SQL_DOUBLE -> SQL_C_FLOAT */
#define ESODBC_ES_TO_CSQL_DOUBLE SQL_C_DOUBLE
#define ESODBC_ES_TO_SQL_DOUBLE SQL_DOUBLE
/* 16: ??? -> SQL_C_TINYINT */
#define ESODBC_ES_TO_CSQL_BOOLEAN SQL_C_BIT
#define ESODBC_ES_TO_SQL_BOOLEAN SQL_BIT
/* 12: SQL_VARCHAR -> SQL_C_WCHAR */
#define ESODBC_ES_TO_CSQL_KEYWORD SQL_C_WCHAR /* XXX: CBOR needs _CHAR */
#define ESODBC_ES_TO_SQL_KEYWORD SQL_VARCHAR
/* 12: SQL_VARCHAR -> SQL_C_WCHAR */
#define ESODBC_ES_TO_CSQL_TEXT SQL_C_WCHAR /* XXX: CBOR needs _CHAR */
#define ESODBC_ES_TO_SQL_TEXT SQL_VARCHAR
/* 12: SQL_VARCHAR -> SQL_C_WCHAR */
#define ESODBC_ES_TO_CSQL_IP SQL_C_WCHAR /* XXX: CBOR needs _CHAR */
#define ESODBC_ES_TO_SQL_IP SQL_VARCHAR
/* 93: SQL_TYPE_TIMESTAMP -> SQL_C_TYPE_TIMESTAMP */
#define ESODBC_ES_TO_CSQL_DATE SQL_C_TYPE_TIMESTAMP
#define ESODBC_ES_TO_SQL_DATE SQL_TYPE_TIMESTAMP
/* -3: SQL_VARBINARY -> SQL_C_BINARY */
#define ESODBC_ES_TO_CSQL_BINARY SQL_C_BINARY
#define ESODBC_ES_TO_SQL_BINARY SQL_VARBINARY
/* 0: SQL_TYPE_NULL -> SQL_C_TINYINT */
#define ESODBC_ES_TO_CSQL_NULL SQL_C_STINYINT
#define ESODBC_ES_TO_SQL_NULL SQL_TYPE_NULL
/* 1111: ??? -> SQL_C_BINARY */
#define ESODBC_ES_TO_CSQL_UNSUPPORTED SQL_C_BINARY
#define ESODBC_ES_TO_SQL_UNSUPPORTED ESODBC_SQL_UNSUPPORTED
/* 2002: ??? -> SQL_C_BINARY */
#define ESODBC_ES_TO_CSQL_OBJECT SQL_C_BINARY
#define ESODBC_ES_TO_SQL_OBJECT ESODBC_SQL_OBJECT
/* 2002: ??? -> SQL_C_BINARY */
#define ESODBC_ES_TO_CSQL_NESTED SQL_C_BINARY
#define ESODBC_ES_TO_SQL_NESTED ESODBC_SQL_NESTED

#endif /* __DEFS_H__ */

/* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 tw=78 : */
95 changes: 68 additions & 27 deletions driver/handles.c
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ SQLRETURN EsSQLSetStmtAttrW(
desc = stmt->apd;
break;
} while (0);
ret = EsSQLSetDescFieldW(desc, NO_REC_NR,
ret = EsSQLSetDescFieldW(desc, NO_REC_NR,
SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, BufferLength);
if (ret != SQL_SUCCESS) { /* _WITH_INFO wud be "error" here */
/* if SetDescField() fails, DM will check statement's diag */
Expand All @@ -732,7 +732,7 @@ SQLRETURN EsSQLSetStmtAttrW(
desc = stmt->ipd;
break;
} while (0);
ret = EsSQLSetDescFieldW(desc, NO_REC_NR,
ret = EsSQLSetDescFieldW(desc, NO_REC_NR,
SQL_DESC_ROWS_PROCESSED_PTR, ValuePtr, BufferLength);
if (ret != SQL_SUCCESS) { /* _WITH_INFO wud be "error" here */
/* if SetDescField() fails, DM will check statement's diag */
Expand Down Expand Up @@ -1452,7 +1452,7 @@ SQLRETURN update_rec_count(esodbc_desc_st *desc, SQLSMALLINT new_count)
*/
esodbc_rec_st *get_record(esodbc_desc_st *desc, SQLSMALLINT rec_no, BOOL grow)
{
assert(0 <= rec_no);
assert(0 < rec_no);

if (desc->count < rec_no) {
if (! grow) {
Expand Down Expand Up @@ -1671,7 +1671,7 @@ SQLRETURN EsSQLGetDescFieldW(
case SQL_DESC_LITERAL_PREFIX:
wstr = rec->es_type->literal_prefix;
break;
case SQL_DESC_LITERAL_SUFFIX:
case SQL_DESC_LITERAL_SUFFIX:
wstr = rec->es_type->literal_suffix;
break;
case SQL_DESC_LOCAL_TYPE_NAME:
Expand Down Expand Up @@ -1917,12 +1917,14 @@ void concise_to_type_code(SQLSMALLINT concise, SQLSMALLINT *type,
*type = SQL_INTERVAL;
*code = SQL_CODE_MINUTE_TO_SECOND;
break;
default:
/* "For all data types except datetime and interval data types,
* the verbose type identifier is the same as the concise type
* identifier and the value in SQL_DESC_DATETIME_INTERVAL_CODE is
* equal to 0." */
*type = concise;
*code = 0;
}
/* "For all data types except datetime and interval data types, the
* verbose type identifier is the same as the concise type identifier and
* the value in SQL_DESC_DATETIME_INTERVAL_CODE is equal to 0." */
*type = concise;
*code = 0;
}

/*
Expand All @@ -1947,10 +1949,10 @@ static void set_defaults_from_meta_type(esodbc_rec_st *rec)
}
break;
case METATYPE_INTERVAL_WSEC:
rec->precision = ESODBC_DEF_INTVL_WS_PRECISION;
rec->precision = ESODBC_DEF_IVL_WS_PRECISION;
/* no break */
case METATYPE_INTERVAL_WOSEC:
rec->datetime_interval_precision = ESODBC_DEF_INTVL_WOS_DT_PREC;
rec->datetime_interval_precision = ESODBC_DEF_IVL_WOS_DT_PREC;
break;
case METATYPE_EXACT_NUMERIC:
if (rec->concise_type == SQL_DECIMAL ||
Expand Down Expand Up @@ -2151,6 +2153,7 @@ static BOOL consistency_check(esodbc_rec_st *rec)
{
SQLSMALLINT type, code;
esodbc_desc_st *desc = rec->desc;
SQLINTEGER max_prec;

/* validity of C / SQL datatypes is checked when setting the meta_type */
assert(METATYPE_UNKNOWN <= rec->meta_type &&
Expand Down Expand Up @@ -2188,23 +2191,58 @@ static BOOL consistency_check(esodbc_rec_st *rec)
}
break;

/* check SQL_DESC_PRECISION field */
/* "a time or timestamp data type" */
case METATYPE_DATETIME:
if (rec->concise_type == SQL_TYPE_DATE) {
break;
}
/* "an interval type with a seconds component" */
case METATYPE_INTERVAL_WSEC:
// TODO: "or one of the interval data types with a time component"
/* "or one of the interval data types with a time component" */
case METATYPE_INTERVAL_WOSEC:
if (rec->precision < 0 ||
ESODBC_MAX_SEC_PRECISION < rec->precision) {
ERRH(desc, "precision (%hd) out of bounds [0, %d].",
rec->precision, ESODBC_MAX_SEC_PRECISION);
return FALSE;
if (SQL_INTERVAL_MONTH < rec->concise_type &&
rec->concise_type != SQL_INTERVAL_YEAR_TO_MONTH) {
if (rec->precision < 0 ||
ESODBC_MAX_SEC_PRECISION < rec->precision) {
ERRH(desc, "precision (%hd) out of bounds [0, %d].",
rec->precision, ESODBC_MAX_SEC_PRECISION);
return FALSE;
}
}
if (rec->meta_type == METATYPE_DATETIME) {
break;
}
// TODO: check rec->datetime_interval_precision
/* check SQL_DESC_DATETIME_INTERVAL_PRECISION */
switch (rec->concise_type) {
case SQL_INTERVAL_YEAR:
max_prec = ESODBC_MAX_IVL_YEAR_LEAD_PREC;
break;
case SQL_INTERVAL_MONTH:
max_prec = ESODBC_MAX_IVL_MONTH_LEAD_PREC;
break;
case SQL_INTERVAL_DAY:
max_prec = ESODBC_MAX_IVL_DAY_LEAD_PREC;
break;
case SQL_INTERVAL_HOUR:
max_prec = ESODBC_MAX_IVL_HOUR_LEAD_PREC;
break;
case SQL_INTERVAL_MINUTE:
max_prec = ESODBC_MAX_IVL_MINUTE_LEAD_PREC;
break;
case SQL_INTERVAL_SECOND:
max_prec = ESODBC_MAX_IVL_SECOND_LEAD_PREC;
break;
default:
max_prec = -1;
}
if (0 < max_prec &&
(rec->datetime_interval_precision < 0 ||
max_prec < rec->datetime_interval_precision)) {
ERRH(desc, "datetime_interval_precision (%hd) out of bounds "
"[0, %d].", rec->datetime_interval_precision, max_prec);
return FALSE;
}
break;
}

Expand Down Expand Up @@ -2273,6 +2311,7 @@ SQLRETURN EsSQLSetDescFieldW(
SQLINTEGER *intp;
SQLSMALLINT count, type, chk_type, chk_code;
SQLULEN ulen;
SQLLEN slen;
size_t wlen;

if (! check_access(desc, FieldIdentifier, O_RDWR)) {
Expand Down Expand Up @@ -2414,7 +2453,7 @@ SQLRETURN EsSQLSetDescFieldW(
case SQL_DESC_TYPE:
type = (SQLSMALLINT)(intptr_t)ValuePtr;
DBGH(desc, "setting type of rec@0x%p to %d.", rec, type);
/* Note: SQL_[C_]DATE == SQL_DATETIME (== 9) =>
/* Note: SQL_[C_]DATE == SQL_DATETIME (== 9) =>
* 1. one needs to always use SQL_DESC_CONCISE_TYPE for setting
* the types from within the driver (binding cols, params):
* "SQL_DESC_CONCISE_TYPE can be set by a call to SQLBindCol or
Expand All @@ -2429,7 +2468,7 @@ SQLRETURN EsSQLSetDescFieldW(
* valid and consistent." */
/* setting the verbose type only */
concise_to_type_code(rec->concise_type, &chk_type, &chk_code);
if (chk_type != type ||
if (chk_type != type ||
chk_code != rec->datetime_interval_code ||
(! rec->datetime_interval_code)) {
ERRH(desc, "type fields found inconsistent when setting "
Expand All @@ -2445,10 +2484,10 @@ SQLRETURN EsSQLSetDescFieldW(
/* no break! */
case SQL_DESC_CONCISE_TYPE:
DBGH(desc, "setting concise type of rec 0x%p to %d.", rec,
(SQLSMALLINT)(intptr_t)ValuePtr);
(SQLSMALLINT)(intptr_t)ValuePtr);
rec->concise_type = (SQLSMALLINT)(intptr_t)ValuePtr;

concise_to_type_code(rec->concise_type, &rec->type,
concise_to_type_code(rec->concise_type, &rec->type,
&rec->datetime_interval_code);
rec->meta_type = concise_to_meta(rec->concise_type, desc->type);
if (rec->meta_type == METATYPE_UNKNOWN) {
Expand Down Expand Up @@ -2513,7 +2552,7 @@ SQLRETURN EsSQLSetDescFieldW(
case SQL_DESC_NAME:
WARNH(desc, "stored procedure params (to set to `"LWPD"`) not "
"supported.", ValuePtr ? (SQLWCHAR *)ValuePtr : TWS_NULL);
RET_HDIAG(desc, SQL_STATE_HYC00,
RET_HDIAG(desc, SQL_STATE_HYC00,
"stored procedure params not supported", 0);

/* <SQLWCHAR *> */
Expand Down Expand Up @@ -2571,10 +2610,12 @@ SQLRETURN EsSQLSetDescFieldW(
DBGH(desc, "setting octet length: %ld.",
(SQLLEN)(intptr_t)ValuePtr);
/* rec field's type is signed :/; a negative is dangerous l8r */
if ((SQLLEN)(intptr_t)ValuePtr < 0) {
slen = (SQLLEN)(intptr_t)ValuePtr;
if (slen < 0 && slen != SQL_NTSL) {
ERRH(desc, "octet length attribute can't be negative (%lld)",
(SQLLEN)(intptr_t)ValuePtr);
RET_HDIAGS(desc, SQL_STATE_HY000);
slen);
RET_HDIAG(desc, SQL_STATE_HY000,
"invalid negative octet lenght attribute", 0);
}
rec->octet_length = (SQLLEN)(intptr_t)ValuePtr;
break;
Expand Down Expand Up @@ -2613,7 +2654,7 @@ SQLRETURN EsSQLSetDescFieldW(
/* <SQLINTEGER> */
do {
/* R/O field: auto_unique_value, case_sensitive */
case SQL_DESC_DATETIME_INTERVAL_PRECISION:
case SQL_DESC_DATETIME_INTERVAL_PRECISION:
intp = &rec->datetime_interval_precision;
break;
case SQL_DESC_NUM_PREC_RADIX:
Expand Down
34 changes: 10 additions & 24 deletions driver/info.c
Original file line number Diff line number Diff line change
Expand Up @@ -1189,37 +1189,24 @@ SQLRETURN EsSQLGetFunctions(SQLHDBC ConnectionHandle,
return SQL_SUCCESS;
}

/*
* Equivalent of JDBC's getTypeInfo() ([0]:900)
*/
/* "If the DataType argument specifies a data type which is valid for the
* version of ODBC supported by the driver, but is not supported by the
* driver, then it will return an empty result set." */
SQLRETURN EsSQLGetTypeInfoW(SQLHSTMT StatementHandle, SQLSMALLINT DataType)
{
#define SQL_TYPES_STMT "SYS TYPES"
#define SQL_TYPES_TYPE_SEL "TYPE"

SQLRETURN ret;
esodbc_stmt_st *stmt = STMH(StatementHandle);
SQLWCHAR wbuff[sizeof(SQL_TYPES_STMT " " SQL_TYPES_TYPE_SEL " 32767")];
SQLWCHAR wbuff[sizeof(SQL_TYPES_STMT " 32767")];
size_t cnt;

switch (DataType) {
case SQL_ALL_TYPES:
DBGH(stmt, "requested type description for all supported types.");
wcscpy(wbuff, MK_WPTR(SQL_TYPES_STMT));
cnt = sizeof(SQL_TYPES_STMT) - 1;
break;

/* "If the DataType argument specifies a data type which is valid for
* the version of ODBC supported by the driver, but is not supported
* by the driver, then it will return an empty result set." */
default:
cnt = swprintf(wbuff, sizeof(wbuff)/sizeof(*wbuff),
MK_WPTR(SQL_TYPES_STMT " " SQL_TYPES_TYPE_SEL " %hd"),
DataType);
if (cnt <= 0) {
ERRNH(stmt, "failed to print catalog query.");
RET_HDIAGS(stmt, SQL_STATE_HY000);
}
DBGH(stmt, "requested type description for type %hd.", DataType);
cnt = swprintf(wbuff, sizeof(wbuff)/sizeof(*wbuff),
MK_WPTR(SQL_TYPES_STMT " %hd"), DataType);
if (cnt <= 0) {
ERRNH(stmt, "failed to print catalog query.");
RET_HDIAGS(stmt, SQL_STATE_HY000);
}

ret = EsSQLFreeStmt(stmt, ESODBC_SQL_CLOSE);
Expand All @@ -1231,7 +1218,6 @@ SQLRETURN EsSQLGetTypeInfoW(SQLHSTMT StatementHandle, SQLSMALLINT DataType)
return ret;

# undef SQL_TYPES_STMT
# undef SQL_TYPES_TYPE_SEL
}

/* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 tw=78 : */
2 changes: 1 addition & 1 deletion driver/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ int parse_log_level(wstr_st *level)
return LOG_LEVEL_DISABLED;
}
/* first letter will indicate the log level */
switch ((unsigned)level->str[0] | 0x20) {
switch ((unsigned)level->str[0] | 0x20) { /* ~tolower(), ascii set only */
case 'e':
return LOG_LEVEL_ERR;
case 'w':
Expand Down
Loading