Added strptime function to SQL parser as a feature. This is needed to i.e. be able to query time-fields in DB and to use dates <1.1.1970 and >19.1.2038. All changes should be backwards-compatible to not break existing queries.

This commit is contained in:
leecher1337 2015-08-30 11:23:57 +02:00
parent 5ce4cc5528
commit e2449c11f3
9 changed files with 163 additions and 18 deletions

View File

@ -232,6 +232,7 @@ AM_CONDITIONAL(ENABLE_DOCBOOK, test -n "$DOCBOOK_DSL")
dnl Checks for library functions.
VL_LIB_READLINE
AC_CHECK_FUNC(strptime,[ AC_DEFINE(HAVE_STRPTIME, 1, [strptime check]) AM_CONDITIONAL(HAVE_STRPTIME, true) ],[ AM_CONDITIONAL(HAVE_STRPTIME, false) ])
localedir=${datadir}/locale
AC_SUBST(localedir)

View File

@ -83,6 +83,7 @@ extern void mdb_sql_all_columns(MdbSQL *sql);
extern void mdb_sql_sel_count(MdbSQL *sql);
extern int mdb_sql_add_column(MdbSQL *sql, char *column_name);
extern int mdb_sql_add_table(MdbSQL *sql, char *table_name);
extern char *mdb_sql_strptime(MdbSQL *sql, char *data, char *format);
extern void mdb_sql_dump(MdbSQL *sql);
extern void mdb_sql_exit(MdbSQL *sql);
extern void mdb_sql_reset(MdbSQL *sql);

View File

@ -336,6 +336,7 @@ typedef struct {
struct mdbsargtree {
int op;
MdbColumn *col;
unsigned char val_type;
MdbAny value;
void *parent;
MdbSargNode *left;
@ -482,6 +483,7 @@ extern const char *mdb_col_get_prop(const MdbColumn *col, const gchar *key);
extern int mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int *len_ptr);
extern void mdb_data_dump(MdbTableDef *table);
extern void mdb_date_to_tm(double td, struct tm *t);
extern void mdb_tm_to_date(struct tm *t, double *td);
extern void mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr, int *len_ptr);
extern int mdb_rewind_table(MdbTableDef *table);
extern int mdb_fetch_row(MdbTableDef *table);

View File

@ -16,6 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _XOPEN_SOURCE
#include <time.h>
#include <math.h>
#include "mdbtools.h"
@ -37,6 +38,9 @@ static size_t mdb_copy_ole(MdbHandle *mdb, void *dest, int start, int size);
#endif
static char date_fmt[64] = "%x %X";
static int noleap_cal[] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
static int leap_cal[] = {0,31,60,91,121,152,182,213,244,274,305,335,366};
void mdb_set_date_fmt(const char *fmt)
{
@ -778,18 +782,29 @@ static int trim_trailing_zeros(char * buff)
}
#endif
/* Date/Time is stored as a double, where the whole
part is the days from 12/30/1899 and the fractional
part is the fractional part of one day. */
void
mdb_tm_to_date(struct tm *t, double *td)
{
short yr = t->tm_year + 1900;
char leap = ((yr & 3) == 0) && ((yr % 100) != 0 || (yr % 400) == 0);
int *cal = leap ? leap_cal : noleap_cal;
long int time = (yr*365+(yr/4)-(yr/100)+(yr/400)+cal[t->tm_mon]+t->tm_mday)-693959;
*td = (((long)t->tm_hour * 3600)+((long)t->tm_min * 60)+((long)t->tm_sec)) / 86400.0;
if (time>=0) *td+=time; else *td=time-*td;
}
void
mdb_date_to_tm(double td, struct tm *t)
{
long int day, time;
int yr, q;
int *cal;
int noleap_cal[] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
int leap_cal[] = {0,31,60,91,121,152,182,213,244,274,305,335,366};
day = (long int)(td);
time = (long int)(fabs(td - day) * 86400.0 + 0.5);

View File

@ -98,6 +98,43 @@ int mdb_test_int(MdbSargNode *node, gint32 i)
return 0;
}
/* Actually used to not rely on libm.
* Maybe there is a cleaner and faster solution? */
static double poor_mans_trunc(double x)
{
char buf[16];
sprintf(buf, "%.6f", x);
sscanf(buf, "%lf", &x);
return x;
}
int mdb_test_double(int op, double vd, double d)
{
switch (op) {
case MDB_EQUAL:
//fprintf(stderr, "comparing %lf and %lf\n", d, node->value.d);
if (vd == d) return 1;
break;
case MDB_GT:
if (vd < d) return 1;
break;
case MDB_LT:
if (vd > d) return 1;
break;
case MDB_GTEQ:
if (vd <= d) return 1;
break;
case MDB_LTEQ:
if (vd >= d) return 1;
break;
default:
fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_double() for operator %d\n",op);
break;
}
return 0;
}
#if 0 // Obsolete
int
mdb_test_date(MdbSargNode *node, double td)
{
@ -140,6 +177,7 @@ mdb_test_date(MdbSargNode *node, double td)
}
return 0;
}
#endif
int
@ -196,7 +234,7 @@ mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field
mdb_unicode2ascii(mdb, field->value, field->siz, tmpbuf, 256);
return mdb_test_string(node, tmpbuf);
case MDB_DATETIME:
return mdb_test_date(node, mdb_get_double(field->value, 0));
return mdb_test_double(node->op, poor_mans_trunc(node->value.d), poor_mans_trunc(mdb_get_double(field->value, 0)));
default:
fprintf(stderr, "Calling mdb_test_sarg on unknown type. Add code to mdb_test_sarg() for type %d\n",col->col_type);
break;

View File

@ -44,6 +44,8 @@ static SQLRETURN SQL_API _SQLAllocStmt(SQLHDBC hdbc, SQLHSTMT *phstmt);
static SQLRETURN SQL_API _SQLFreeConnect(SQLHDBC hdbc);
static SQLRETURN SQL_API _SQLFreeEnv(SQLHENV henv);
static SQLRETURN SQL_API _SQLFreeStmt(SQLHSTMT hstmt, SQLUSMALLINT fOption);
static SQLRETURN SQL_API _SQLPrepare(SQLHSTMT hstmt, SQLCHAR *szSqlStr, SQLINTEGER cbSqlStr);
static void bind_columns (struct _hstmt*);
static void unbind_columns (struct _hstmt*);
@ -1111,11 +1113,7 @@ static SQLRETURN SQL_API _SQLExecDirect(
SQLCHAR *szSqlStr,
SQLINTEGER cbSqlStr)
{
struct _hstmt *stmt = (struct _hstmt *) hstmt;
TRACE("_SQLExecDirect");
strcpy(stmt->query, (char*)szSqlStr);
_SQLPrepare(hstmt, szSqlStr, cbSqlStr);
return _SQLExecute(hstmt);
}
@ -1362,7 +1360,7 @@ SQLRETURN SQL_API SQLNumResultCols(
return SQL_SUCCESS;
}
SQLRETURN SQL_API SQLPrepare(
static SQLRETURN SQL_API _SQLPrepare(
SQLHSTMT hstmt,
SQLCHAR *szSqlStr,
SQLINTEGER cbSqlStr)
@ -1378,6 +1376,14 @@ SQLRETURN SQL_API SQLPrepare(
return SQL_SUCCESS;
}
SQLRETURN SQL_API SQLPrepare(
SQLHSTMT hstmt,
SQLCHAR *szSqlStr,
SQLINTEGER cbSqlStr)
{
return _SQLPrepare(hstmt, szSqlStr, cbSqlStr);
}
SQLRETURN SQL_API SQLRowCount(
SQLHSTMT hstmt,
SQLLEN *pcrow)

View File

@ -46,6 +46,7 @@ null { return NUL; }
(>=) { return GTEQ; }
like { return LIKE; }
count { return COUNT; }
strptime { return STRPTIME; }
[ \t\r] ;
\"[^"]*\"\" {

View File

@ -17,6 +17,7 @@
*/
#include <stdarg.h>
#define _XOPEN_SOURCE
#include "mdbsql.h"
#ifdef DMALLOC
@ -28,6 +29,12 @@
#include <wordexp.h>
#endif
#ifdef HAVE_STRPTIME
#include <time.h>
#include <stdio.h>
#include <locale.h>
#endif
char *g_input_ptr;
/* Prevent warnings from -Wmissing-prototypes. */
@ -334,6 +341,50 @@ mdb_sql_dump_node(MdbSargNode *node, int level)
mdb_sql_dump_node(node->right, mylevel);
}
}
/* Parses date format specifier into JET date representation (double) */
char *
mdb_sql_strptime(MdbSQL *sql, char *data, char *format)
{
#ifndef HAVE_STRPTIME
mdb_sql_error(sql, "Your system doesn't support strptime.");
mdb_sql_reset(sql);
return NULL;
#else
char *p, *pszDate;
struct tm tm={0};
double date=0;
if (data[0]!='\'' || *(p=&data[strlen(data)-1])!='\'') {
mdb_sql_error(sql, "First parameter of strptime (data) must be a string.");
mdb_sql_reset(sql);
return NULL;
}
*p=0; ++data;
if (format[0]!='\'' || *(p=&format[strlen(format)-1])!='\'') {
mdb_sql_error(sql, "Second parameter of strptime (format) must be a string.");
mdb_sql_reset(sql);
return NULL;
}
*p=0; ++format;
if (!strptime(data, format, &tm)) {
mdb_sql_error(sql, "strptime('%s','%s') failed.", data, format);
mdb_sql_reset(sql);
return NULL;
}
mdb_tm_to_date(&tm, &date);
/* It seems that when just using a time offset without date in strptime,
* we always get 1 as decimal part, een though it should be 0 for time */
if (date < 2 && date > 1) date--;
if ((pszDate=malloc(16))) {
char cLocale=localeconv()->decimal_point[0], *p;
sprintf(pszDate, "%lf", date);
if (cLocale!='.') for (p=pszDate; *p; p++) if (*p==cLocale) *p='.';
}
return pszDate;
#endif
}
/* evaluate a expression involving 2 constants and add answer to the stack */
int
mdb_sql_eval_expr(MdbSQL *sql, char *const1, int op, char *const2)
@ -387,6 +438,7 @@ int
mdb_sql_add_sarg(MdbSQL *sql, char *col_name, int op, char *constant)
{
int lastchar;
char *p;
MdbSargNode *node;
node = mdb_sql_alloc_node();
@ -406,8 +458,14 @@ mdb_sql_add_sarg(MdbSQL *sql, char *col_name, int op, char *constant)
lastchar = strlen(constant) > 256 ? 256 : strlen(constant);
strncpy(node->value.s, &constant[1], lastchar - 2);;
node->value.s[lastchar - 1]='\0';
node->val_type = MDB_TEXT;
} else if ((p=strchr(constant, '.'))) {
*p=localeconv()->decimal_point[0];
node->value.d = strtod(constant, NULL);
node->val_type = MDB_DOUBLE;
} else {
node->value.i = atoi(constant);
node->val_type = MDB_INT;
}
mdb_sql_push_node(sql, node);
@ -478,6 +536,8 @@ void mdb_sql_exit(MdbSQL *sql)
}
void mdb_sql_reset(MdbSQL *sql)
{
unsigned int i;
if (sql->cur_table) {
mdb_index_scan_free(sql->cur_table);
if (sql->cur_table->sarg_tree) {
@ -489,7 +549,6 @@ void mdb_sql_reset(MdbSQL *sql)
}
/* Reset bound values */
unsigned int i;
for (i=0;i<sql->num_columns;i++) {
g_free(sql->bound_values[i]);
sql->bound_values[i] = NULL;
@ -653,24 +712,40 @@ void mdb_sql_describe_table(MdbSQL *sql)
sql->cur_table = ttable;
}
MdbColumn *mdb_sql_find_colbyname(MdbTableDef *table, char *name)
{
unsigned int i;
MdbColumn *col;
for (i=0;i<table->num_cols;i++) {
col=g_ptr_array_index(table->columns,i);
if (!g_ascii_strcasecmp(col->name, name)) return col;
}
return NULL;
}
int mdb_sql_find_sargcol(MdbSargNode *node, gpointer data)
{
MdbTableDef *table = data;
unsigned int i;
MdbColumn *col;
if (!mdb_is_relational_op(node->op)) return 0;
if (!node->parent) return 0;
for (i=0;i<table->num_cols;i++) {
col=g_ptr_array_index(table->columns,i);
if (!g_ascii_strcasecmp(col->name, (char *)node->parent)) {
node->col = col;
break;
if ((col = mdb_sql_find_colbyname(table, (char *)node->parent))) {
node->col = col;
/* Do conversion to required target value type.
* Plain integers are UNIX timestamps for backwards compatibility of parser
*/
if (col->col_type == MDB_DATETIME && node->val_type == MDB_INT) {
struct tm *tm = gmtime((time_t*)&node->value.i);
mdb_tm_to_date(tm, &node->value.d);
node->val_type = MDB_DOUBLE;
}
}
return 0;
}
void
mdb_sql_select(MdbSQL *sql)
{
@ -687,6 +762,7 @@ int found = 0;
return;
}
if (!sql->num_tables) return;
sql_tab = g_ptr_array_index(sql->tables,0);
table = mdb_read_table_by_name(mdb, sql_tab->name, MDB_TABLE);

View File

@ -42,7 +42,7 @@ static MdbSQL *g_sql;
%token <name> IDENT NAME PATH STRING NUMBER
%token SELECT FROM WHERE CONNECT DISCONNECT TO LIST TABLES AND OR NOT COUNT
%token SELECT FROM WHERE CONNECT DISCONNECT TO LIST TABLES AND OR NOT COUNT STRPTIME
%token DESCRIBE TABLE
%token LTEQ GTEQ LIKE IS NUL
@ -132,7 +132,12 @@ nulloperator:
;
constant:
NUMBER { $$ = $1; }
STRPTIME '(' constant ',' constant ')' {
$$ = mdb_sql_strptime(_mdb_sql(NULL), $3, $5);
free($3);
free($5);
}
| NUMBER { $$ = $1; }
| STRING { $$ = $1; }
;