mirror of
https://github.com/mdbtools/mdbtools.git
synced 2025-04-05 20:31:00 +08:00
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:
parent
5ce4cc5528
commit
e2449c11f3
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -46,6 +46,7 @@ null { return NUL; }
|
||||
(>=) { return GTEQ; }
|
||||
like { return LIKE; }
|
||||
count { return COUNT; }
|
||||
strptime { return STRPTIME; }
|
||||
[ \t\r] ;
|
||||
|
||||
\"[^"]*\"\" {
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user