Make SQL parser/lexer reentrant

Provides thread safety for mdbtools ODBC driver
This commit is contained in:
Nyall Dawson
2020-08-18 12:09:11 +10:00
parent 6171472411
commit e92f14f9c4
4 changed files with 114 additions and 83 deletions

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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);
}