mirror of
https://github.com/mdbtools/mdbtools.git
synced 2025-09-19 02:27:55 +08:00
Make SQL parser/lexer reentrant
Provides thread safety for mdbtools ODBC driver
This commit is contained in:
@@ -32,7 +32,8 @@
|
||||
#endif
|
||||
#include <mdbtools.h>
|
||||
|
||||
typedef struct {
|
||||
typedef struct MdbSQL
|
||||
{
|
||||
MdbHandle *mdb;
|
||||
int all_columns;
|
||||
int sel_count;
|
||||
@@ -71,16 +72,10 @@ typedef struct {
|
||||
MdbSarg *sarg;
|
||||
} MdbSQLSarg;
|
||||
|
||||
extern char *g_input_ptr;
|
||||
|
||||
#undef YY_INPUT
|
||||
#define YY_INPUT(b, r, ms) (r = mdb_sql_yyinput(b, ms));
|
||||
|
||||
#define mdb_sql_has_error(sql) ((sql)->error_msg[0] ? 1 : 0)
|
||||
#define mdb_sql_last_error(sql) ((sql)->error_msg)
|
||||
|
||||
void mdb_sql_error(MdbSQL* sql, char *fmt, ...);
|
||||
MdbSQL *_mdb_sql(MdbSQL *sql);
|
||||
MdbSQL *mdb_sql_init(void);
|
||||
MdbSQLSarg *mdb_sql_alloc_sarg(void);
|
||||
MdbHandle *mdb_sql_open(MdbSQL *sql, char *db_name);
|
||||
@@ -110,6 +105,9 @@ int mdb_sql_add_temp_col(MdbSQL *sql, MdbTableDef *ttable, int col_num, char *na
|
||||
void mdb_sql_bind_column(MdbSQL *sql, int colnum, void *varaddr, int *len_ptr);
|
||||
int mdb_sql_add_limit(MdbSQL *sql, char *limit);
|
||||
|
||||
|
||||
int parse_sql(MdbSQL * mdb, const gchar* str);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -1,4 +1,3 @@
|
||||
%{
|
||||
/* MDB Tools - A library for reading MS Access database file
|
||||
* Copyright (C) 2000 Brian Bruns
|
||||
*
|
||||
@@ -17,14 +16,38 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "mdbsql.h"
|
||||
#include "parser.h"
|
||||
extern int mdb_sql_yyinput(char *buf, int need);
|
||||
%}
|
||||
|
||||
%option noyywrap
|
||||
%option case-insensitive
|
||||
%option never-interactive
|
||||
%option nounput
|
||||
%option noinput
|
||||
%option yylineno
|
||||
%option reentrant
|
||||
%option bison-bridge
|
||||
%option bison-locations
|
||||
|
||||
// ensure that lexer will be 8-bit (and not just 7-bit)
|
||||
%option 8bit
|
||||
|
||||
%{
|
||||
#include <string.h>
|
||||
#include "mdbsql.h"
|
||||
struct sql_context;
|
||||
#include "parser.h"
|
||||
|
||||
#define YY_NEVER_INTERACTIVE 1
|
||||
|
||||
#ifndef YY_NO_UNPUT
|
||||
#define YY_NO_UNPUT // unused
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define YY_NO_UNISTD_H
|
||||
#endif
|
||||
|
||||
%}
|
||||
|
||||
|
||||
|
||||
%%
|
||||
select { return SELECT; }
|
||||
@@ -58,48 +81,39 @@ strptime { return STRPTIME; }
|
||||
\"[^"]*\" {
|
||||
int ip, op, ilen;
|
||||
ilen = strlen(yytext);
|
||||
yylval.name = malloc(ilen-1);
|
||||
yylval->name = malloc(ilen-1);
|
||||
for (ip=1, op=0; ip<ilen-1; ip++, op++) {
|
||||
if (yytext[ip] != '"') {
|
||||
yylval.name[op] = yytext[ip];
|
||||
yylval->name[op] = yytext[ip];
|
||||
} else if (yytext[ip+1] == '"') {
|
||||
yylval.name[op] = yytext[ip++];
|
||||
yylval->name[op] = yytext[ip++];
|
||||
}
|
||||
}
|
||||
yylval.name[op]='\0';
|
||||
yylval->name[op]='\0';
|
||||
return IDENT;
|
||||
}
|
||||
|
||||
[a-z\xa0-\xff][a-z0-9_#@\xa0-\xff]* { yylval.name = g_strdup(yytext); return NAME; }
|
||||
[a-z\xa0-\xff][a-z0-9_#@\xa0-\xff]* { yylval->name = g_strdup(yytext); return NAME; }
|
||||
|
||||
'[^']*'' {
|
||||
yyless(yyleng-1);
|
||||
yymore();
|
||||
}
|
||||
'[^']*' {
|
||||
yylval.name = g_strdup(yytext);
|
||||
yylval->name = g_strdup(yytext);
|
||||
return STRING;
|
||||
}
|
||||
|
||||
(-*[0-9]+|([0-9]*\.[0-9]+)(e[-+]?[0-9]+)?) {
|
||||
yylval.name = g_strdup(yytext); return NUMBER;
|
||||
yylval->name = g_strdup(yytext); return NUMBER;
|
||||
}
|
||||
~?(\/?[a-z0-9\.\xa0-\xff]+)+ {
|
||||
yylval.name = g_strdup(yytext); return PATH;
|
||||
yylval->name = g_strdup(yytext); return PATH;
|
||||
}
|
||||
|
||||
. { return yytext[0]; }
|
||||
%%
|
||||
|
||||
int yywrap()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
void yyerror(char *s)
|
||||
{
|
||||
fprintf(stderr,"Error at Line : %s near %s\n", s, yytext);
|
||||
mdb_sql_error(_mdb_sql(NULL), "%s near %s", s, yytext);
|
||||
}
|
||||
#if 0
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
@@ -36,8 +36,6 @@
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
char *g_input_ptr;
|
||||
|
||||
/* Prevent warnings from -Wmissing-prototypes. */
|
||||
#ifdef YYPARSE_PARAM
|
||||
#if defined __STDC__ || defined __cplusplus
|
||||
@@ -67,19 +65,6 @@ va_list ap;
|
||||
fprintf(stderr, "%s\n", sql->error_msg);
|
||||
}
|
||||
|
||||
int mdb_sql_yyinput(char *buf, int need)
|
||||
{
|
||||
int cplen, have;
|
||||
|
||||
have = strlen(g_input_ptr);
|
||||
cplen = need > have ? have : need;
|
||||
|
||||
if (cplen>0) {
|
||||
memcpy(buf, g_input_ptr, cplen);
|
||||
g_input_ptr += cplen;
|
||||
}
|
||||
return cplen;
|
||||
}
|
||||
MdbSQL *mdb_sql_init()
|
||||
{
|
||||
MdbSQL *sql;
|
||||
@@ -112,14 +97,9 @@ mdb_sql_run_query (MdbSQL* sql, const gchar* querystr) {
|
||||
g_return_val_if_fail (sql, NULL);
|
||||
g_return_val_if_fail (querystr, NULL);
|
||||
|
||||
g_input_ptr = (gchar*) querystr;
|
||||
|
||||
/* calls to yyparse should be serialized for thread safety */
|
||||
|
||||
/* begin unsafe */
|
||||
_mdb_sql (sql);
|
||||
sql->error_msg[0]='\0';
|
||||
if (yyparse()) {
|
||||
|
||||
if (parse_sql (sql, querystr)) {
|
||||
/* end unsafe */
|
||||
mdb_sql_error (sql, _("Could not parse '%s' command"), querystr);
|
||||
mdb_sql_reset (sql);
|
||||
|
@@ -1,4 +1,3 @@
|
||||
%{
|
||||
/* MDB Tools - A library for reading MS Access database files
|
||||
* Copyright (C) 2000 Brian Bruns
|
||||
*
|
||||
@@ -16,30 +15,50 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
%{
|
||||
#include "mdbsql.h"
|
||||
|
||||
int yylex(void);
|
||||
int yyerror(char *);
|
||||
struct sql_context;
|
||||
#include "parser.h"
|
||||
|
||||
MdbSQL *_mdb_sql(MdbSQL *sql)
|
||||
typedef void *yyscan_t;
|
||||
typedef struct yy_buffer_state* YY_BUFFER_STATE;
|
||||
extern int yylex_init(yyscan_t* scanner);
|
||||
extern int yylex_destroy(yyscan_t scanner);
|
||||
extern int yylex(YYSTYPE* yylval_param, YYLTYPE* yyloc, yyscan_t yyscanner);
|
||||
extern YY_BUFFER_STATE yy_scan_string(const char* buffer, yyscan_t scanner);
|
||||
|
||||
/** error handler for bison */
|
||||
void yyerror(YYLTYPE* yyloc, struct sql_context* sql_ctx, char const* msg);
|
||||
|
||||
typedef struct sql_context
|
||||
{
|
||||
static MdbSQL *g_sql;
|
||||
// lexer context
|
||||
yyscan_t flex_scanner;
|
||||
|
||||
if (sql) {
|
||||
g_sql = sql;
|
||||
}
|
||||
return g_sql;
|
||||
}
|
||||
MdbSQL *mdb;
|
||||
} sql_context;
|
||||
|
||||
#define scanner parser_ctx->flex_scanner
|
||||
|
||||
// we want verbose error messages
|
||||
#define YYERROR_VERBOSE 1
|
||||
%}
|
||||
|
||||
// make the parser reentrant
|
||||
%locations
|
||||
%define api.pure
|
||||
%lex-param {void * scanner}
|
||||
%parse-param {struct sql_context* parser_ctx}
|
||||
|
||||
%union {
|
||||
char *name;
|
||||
double dval;
|
||||
int ival;
|
||||
}
|
||||
|
||||
|
||||
%start stmt
|
||||
|
||||
%token <name> IDENT NAME PATH STRING NUMBER
|
||||
%token SELECT FROM WHERE CONNECT DISCONNECT TO LIST TABLES AND OR NOT LIMIT COUNT STRPTIME
|
||||
@@ -56,24 +75,24 @@ static MdbSQL *g_sql;
|
||||
|
||||
stmt:
|
||||
query
|
||||
| error { yyclearin; mdb_sql_reset(_mdb_sql(NULL)); }
|
||||
| error { yyclearin; mdb_sql_reset(parser_ctx->mdb); }
|
||||
;
|
||||
|
||||
query:
|
||||
SELECT column_list FROM table where_clause limit_clause {
|
||||
mdb_sql_select(_mdb_sql(NULL));
|
||||
mdb_sql_select(parser_ctx->mdb);
|
||||
}
|
||||
| CONNECT TO database {
|
||||
mdb_sql_open(_mdb_sql(NULL), $3); free($3);
|
||||
mdb_sql_open(parser_ctx->mdb, $3); free($3);
|
||||
}
|
||||
| DISCONNECT {
|
||||
mdb_sql_close(_mdb_sql(NULL));
|
||||
mdb_sql_close(parser_ctx->mdb);
|
||||
}
|
||||
| DESCRIBE TABLE table {
|
||||
mdb_sql_describe_table(_mdb_sql(NULL));
|
||||
mdb_sql_describe_table(parser_ctx->mdb);
|
||||
}
|
||||
| LIST TABLES {
|
||||
mdb_sql_listtables(_mdb_sql(NULL));
|
||||
mdb_sql_listtables(parser_ctx->mdb);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -84,35 +103,35 @@ where_clause:
|
||||
|
||||
limit_clause:
|
||||
/* empty */
|
||||
| LIMIT NUMBER { mdb_sql_add_limit(_mdb_sql(NULL), $2); free($2); }
|
||||
| LIMIT NUMBER { mdb_sql_add_limit(parser_ctx->mdb, $2); free($2); }
|
||||
;
|
||||
|
||||
sarg_list:
|
||||
sarg
|
||||
| '(' sarg_list ')'
|
||||
| NOT sarg_list { mdb_sql_add_not(_mdb_sql(NULL)); }
|
||||
| sarg_list OR sarg_list { mdb_sql_add_or(_mdb_sql(NULL)); }
|
||||
| sarg_list AND sarg_list { mdb_sql_add_and(_mdb_sql(NULL)); }
|
||||
| NOT sarg_list { mdb_sql_add_not(parser_ctx->mdb); }
|
||||
| sarg_list OR sarg_list { mdb_sql_add_or(parser_ctx->mdb); }
|
||||
| sarg_list AND sarg_list { mdb_sql_add_and(parser_ctx->mdb); }
|
||||
;
|
||||
|
||||
sarg:
|
||||
identifier operator constant {
|
||||
mdb_sql_add_sarg(_mdb_sql(NULL), $1, $2, $3);
|
||||
mdb_sql_add_sarg(parser_ctx->mdb, $1, $2, $3);
|
||||
free($1);
|
||||
free($3);
|
||||
}
|
||||
| constant operator identifier {
|
||||
mdb_sql_add_sarg(_mdb_sql(NULL), $3, $2, $1);
|
||||
mdb_sql_add_sarg(parser_ctx->mdb, $3, $2, $1);
|
||||
free($1);
|
||||
free($3);
|
||||
}
|
||||
| constant operator constant {
|
||||
mdb_sql_eval_expr(_mdb_sql(NULL), $1, $2, $3);
|
||||
mdb_sql_eval_expr(parser_ctx->mdb, $1, $2, $3);
|
||||
free($1);
|
||||
free($3);
|
||||
}
|
||||
| identifier nulloperator {
|
||||
mdb_sql_add_sarg(_mdb_sql(NULL), $1, $2, NULL);
|
||||
mdb_sql_add_sarg(parser_ctx->mdb, $1, $2, NULL);
|
||||
free($1);
|
||||
}
|
||||
;
|
||||
@@ -138,7 +157,7 @@ nulloperator:
|
||||
|
||||
constant:
|
||||
STRPTIME '(' constant ',' constant ')' {
|
||||
$$ = mdb_sql_strptime(_mdb_sql(NULL), $3, $5);
|
||||
$$ = mdb_sql_strptime(parser_ctx->mdb, $3, $5);
|
||||
free($3);
|
||||
free($5);
|
||||
}
|
||||
@@ -152,19 +171,39 @@ database:
|
||||
;
|
||||
|
||||
table:
|
||||
identifier { mdb_sql_add_table(_mdb_sql(NULL), $1); free($1); }
|
||||
identifier { mdb_sql_add_table(parser_ctx->mdb, $1); free($1); }
|
||||
;
|
||||
|
||||
column_list:
|
||||
COUNT '(' '*' ')' { mdb_sql_sel_count(_mdb_sql(NULL)); }
|
||||
| '*' { mdb_sql_all_columns(_mdb_sql(NULL)); }
|
||||
COUNT '(' '*' ')' { mdb_sql_sel_count(parser_ctx->mdb); }
|
||||
| '*' { mdb_sql_all_columns(parser_ctx->mdb); }
|
||||
| column
|
||||
| column ',' column_list
|
||||
;
|
||||
|
||||
|
||||
column:
|
||||
identifier { mdb_sql_add_column(_mdb_sql(NULL), $1); free($1); }
|
||||
identifier { mdb_sql_add_column(parser_ctx->mdb, $1); free($1); }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
|
||||
int parse_sql( MdbSQL * mdb, const gchar *str)
|
||||
{
|
||||
sql_context ctx;
|
||||
ctx.mdb = mdb;
|
||||
|
||||
yylex_init(&ctx.flex_scanner);
|
||||
yy_scan_string(str, ctx.flex_scanner);
|
||||
int res = yyparse(&ctx);
|
||||
yylex_destroy(ctx.flex_scanner);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void yyerror(YYLTYPE* yyloc,sql_context* sql_ctx, char const * msg)
|
||||
{
|
||||
fprintf(stderr,"Error at Line : %s\n", msg);
|
||||
mdb_sql_error(sql_ctx->mdb, "%s", msg);
|
||||
}
|
||||
|
Reference in New Issue
Block a user