almost have both query types working

This commit is contained in:
Ron Pedde 2006-03-10 23:41:13 +00:00
parent e1c98c5fda
commit 4964fb1374
6 changed files with 182 additions and 86 deletions

View File

@ -23,6 +23,7 @@
#define _DB_GENERIC_H_ #define _DB_GENERIC_H_
#include "mp3-scanner.h" /** for MP3FILE */ #include "mp3-scanner.h" /** for MP3FILE */
#include "smart-parser.h" /** for PARSETREE */
typedef enum { typedef enum {
// generic meta data // generic meta data
@ -125,7 +126,7 @@ typedef struct tag_dbqueryinfo {
int session_id; int session_id;
int uri_count; int uri_count;
char *uri_sections[10]; char *uri_sections[10];
char *whereclause; PARSETREE pt;
void *output_info; void *output_info;
} DBQUERYINFO; } DBQUERYINFO;

View File

@ -240,7 +240,7 @@ int db_sql_parse_smart(char **pe, char **clause, char *phrase) {
return FALSE; return FALSE;
} }
if(!sp_parse(pt,phrase)) { if(!sp_parse(pt,phrase,SP_TYPE_PLAYLIST)) {
if(pe) *pe = strdup(sp_get_error(pt)); if(pe) *pe = strdup(sp_get_error(pt));
DPRINTF(E_LOG,L_DB,"Error parsing playlist: %s\n",sp_get_error(pt)); DPRINTF(E_LOG,L_DB,"Error parsing playlist: %s\n",sp_get_error(pt));
@ -931,6 +931,7 @@ int db_sql_enum_start(char **pe, DBQUERYINFO *pinfo) {
char query_count[255]; char query_count[255];
char query_rest[4096]; char query_rest[4096];
char *where_clause; char *where_clause;
char *filter;
int is_smart; int is_smart;
int have_clause=0; int have_clause=0;
@ -1048,17 +1049,24 @@ int db_sql_enum_start(char **pe, DBQUERYINFO *pinfo) {
} }
/* Apply the query/filter */ /* Apply the query/filter */
if(pinfo->whereclause) { if(pinfo->pt) {
if(have_clause) filter = sp_sql_clause(pinfo->pt);
if(filter) {
if(have_clause) {
strcat(query_rest," and "); strcat(query_rest," and ");
else } else {
strcpy(query_rest," where "); strcpy(query_rest," where ");
strcat(query_rest,"(");
strcat(query_rest,pinfo->whereclause);
strcat(query_rest,")");
} }
strcat(query_rest,"(");
strcat(query_rest,filter);
strcat(query_rest,")");
free(filter);
} else {
DPRINTF(E_LOG,L_DB,"Error getting sql for parse tree\n");
}
}
if(pinfo->index_type == indexTypeLast) { if(pinfo->index_type == indexTypeLast) {
/* We don't really care how many items unless we are /* We don't really care how many items unless we are

View File

@ -152,12 +152,19 @@ void daap_handler(WS_CONNINFO *pwsc) {
memset(pqi,0x00,sizeof(DBQUERYINFO)); memset(pqi,0x00,sizeof(DBQUERYINFO));
/* we could really pre-parse this to make sure it works */
query=ws_getvar(pwsc,"query"); query=ws_getvar(pwsc,"query");
if(!query) query=ws_getvar(pwsc,"filter"); if(!query) query=ws_getvar(pwsc,"filter");
if(query) { if(query) {
DPRINTF(E_DBG,L_DAAP,"Getting sql clause for %s\n",query); pqi->pt = sp_init();
pqi->whereclause = query_build_sql(query); if(!sp_parse(&pqi->pt,query,SP_TYPE_QUERY)) {
DPRINTF(E_LOG,L_DAAP,"Ignoring bad query/filter (%s): %s\n",
query,sp_get_error(pqi->pt));
sp_dispose(pqi->pt);
pqi->pt = NULL;
} }
}
/* Add some default headers */ /* Add some default headers */
ws_addresponseheader(pwsc,"Accept-Ranges","bytes"); ws_addresponseheader(pwsc,"Accept-Ranges","bytes");

View File

@ -11,19 +11,23 @@
#include "smart-parser.h" #include "smart-parser.h"
void usage(void) { void usage(void) {
printf("Usage:\n\n parser [-d <debug level>] \"phrase\"\n\n"); printf("Usage:\n\n parser [-t <type (0/1)>] [-d <debug level>] \"phrase\"\n\n");
exit(0); exit(0);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int option; int option;
int type=0;
PARSETREE pt; PARSETREE pt;
while((option = getopt(argc, argv, "d:")) != -1) { while((option = getopt(argc, argv, "d:t:")) != -1) {
switch(option) { switch(option) {
case 'd': case 'd':
err_setlevel(atoi(optarg)); err_setlevel(atoi(optarg));
break; break;
case 't':
type = atoi(optarg);
break;
default: default:
fprintf(stderr,"Error: unknown option (%c)\n\n",option); fprintf(stderr,"Error: unknown option (%c)\n\n",option);
usage(); usage();
@ -34,7 +38,7 @@ int main(int argc, char *argv[]) {
printf("Parsing %s\n",argv[optind]); printf("Parsing %s\n",argv[optind]);
pt=sp_init(); pt=sp_init();
if(!sp_parse(pt,argv[optind])) { if(!sp_parse(pt,argv[optind],type)) {
printf("%s\n",sp_get_error(pt)); printf("%s\n",sp_get_error(pt));
} else { } else {
printf("SQL: %s\n",sp_sql_clause(pt)); printf("SQL: %s\n",sp_sql_clause(pt));

View File

@ -147,6 +147,10 @@ typedef struct tag_sp_node {
#define T_ENDSWITH 0x001d #define T_ENDSWITH 0x001d
#define T_LAST 0x001e #define T_LAST 0x001e
/* specific to query extensions */
#define T_GREATERAND 0x001f
#define T_EXPRQUOTE 0x0020
#define T_EOF 0x00fd #define T_EOF 0x00fd
#define T_BOF 0x00fe #define T_BOF 0x00fe
#define T_ERROR 0x00ff #define T_ERROR 0x00ff
@ -211,6 +215,15 @@ FIELDLOOKUP sp_symbols_0[] = {
}; };
FIELDLOOKUP sp_symbols_1[] = { FIELDLOOKUP sp_symbols_1[] = {
{ T_OPENPAREN, "(", NULL },
{ T_CLOSEPAREN, ")", NULL },
{ T_EXPRQUOTE, "'", NULL },
{ T_GREATERAND, "+", NULL },
{ T_GREATERAND, " ", NULL },
{ T_LESS,"-", NULL },
{ T_OR, ",", NULL },
{ T_EQUAL, ":", NULL },
{ T_NOT, "!", NULL },
{ 0, NULL, NULL } { 0, NULL, NULL }
}; };
@ -341,6 +354,7 @@ typedef struct tag_parsetree {
#define SP_E_BEFOREAFTER 0x0a #define SP_E_BEFOREAFTER 0x0a
#define SP_E_TIMEINTERVAL 0x0b #define SP_E_TIMEINTERVAL 0x0b
#define SP_E_DATE 0x0c #define SP_E_DATE 0x0c
#define SP_E_EXPRQUOTE 0x0d
char *sp_errorstrings[] = { char *sp_errorstrings[] = {
"Success", "Success",
@ -355,7 +369,8 @@ char *sp_errorstrings[] = {
"Expecting date comparison operator (<,<=,>,>=)", "Expecting date comparison operator (<,<=,>,>=)",
"Expecting interval comparison (before, after)", "Expecting interval comparison (before, after)",
"Expecting time interval (days, weeks, months, years)", "Expecting time interval (days, weeks, months, years)",
"Expecting date" "Expecting date",
"Expecting ' (single quote)\n"
}; };
/* Forwards */ /* Forwards */
@ -431,7 +446,10 @@ time_t sp_isdate(char *string) {
} }
/** /**
* scan the input, returning the next available token. * scan the input, returning the next available token. This is
* kind of a mess, and looking at it with new eyes would probably
* yield a better way of tokenizing the stream, but this seems to
* work.
* *
* @param tree current working parse tree. * @param tree current working parse tree.
* @returns next token (token, not the value) * @returns next token (token, not the value)
@ -446,6 +464,7 @@ int sp_scan(PARSETREE tree, int hint) {
int is_qstr; int is_qstr;
time_t tval; time_t tval;
char *qstr; char *qstr;
char *token_string;
if(tree->token.token_id & 0x2000) { if(tree->token.token_id & 0x2000) {
if(tree->token.data.cvalue) if(tree->token.data.cvalue)
@ -501,9 +520,13 @@ int sp_scan(PARSETREE tree, int hint) {
qstr = sp_terminators[tree->token_list][3]; qstr = sp_terminators[tree->token_list][3];
is_qstr = (strchr(qstr,*(tree->current)) != NULL); is_qstr = (strchr(qstr,*(tree->current)) != NULL);
DPRINTF(E_SPAM,L_PARSE,"qstr: %s -- is_quoted: %d\n",qstr,is_qstr);
if(strlen(qstr)) { /* strings ARE quoted */ if(strlen(qstr)) { /* strings ARE quoted */
if(hint == SP_HINT_STRING) { /* MUST be a quote */ if(hint == SP_HINT_STRING) { /* MUST be a quote */
if(!is_qstr) { if(!is_qstr) {
tree->token.token_id = T_ERROR;
return T_ERROR; return T_ERROR;
} }
} else { } else {
@ -552,12 +575,20 @@ int sp_scan(PARSETREE tree, int hint) {
} }
if(tree->token.token_id & 0x2000) { if(tree->token.token_id & 0x2000) {
token_string=tree->current;
if(found) {
if(pfield->xlat) {
len = strlen(pfield->xlat);
token_string = pfield->xlat;
}
}
tree->token.data.cvalue = malloc(len + 1); tree->token.data.cvalue = malloc(len + 1);
if(!tree->token.data.cvalue) { if(!tree->token.data.cvalue) {
/* fail on malloc error */ /* fail on malloc error */
DPRINTF(E_FATAL,L_PARSE,"Malloc error.\n"); DPRINTF(E_FATAL,L_PARSE,"Malloc error.\n");
} }
strncpy(tree->token.data.cvalue,tree->current,len); strncpy(tree->token.data.cvalue,token_string,len);
tree->token.data.cvalue[len] = '\x0'; tree->token.data.cvalue[len] = '\x0';
} }
@ -652,10 +683,11 @@ PARSETREE sp_init(void) {
* @param term term or phrase to parse * @param term term or phrase to parse
* @returns 1 if successful, 0 if not * @returns 1 if successful, 0 if not
*/ */
int sp_parse(PARSETREE tree, char *term) { int sp_parse(PARSETREE tree, char *term, int type) {
tree->term = strdup(term); /* will be destroyed by parsing */ tree->term = strdup(term); /* will be destroyed by parsing */
tree->current=tree->term; tree->current=tree->term;
tree->token.token_id=T_BOF; tree->token.token_id=T_BOF;
tree->token_list = type;
if(tree->tree) if(tree->tree)
sp_free_node(tree->tree); sp_free_node(tree->tree);
@ -716,7 +748,8 @@ SP_NODE *sp_parse_aexpr(PARSETREE tree) {
expr = sp_parse_expr(tree); expr = sp_parse_expr(tree);
while(expr && (tree->token.token_id == T_AND)) { while(expr && ((tree->token.token_id == T_AND) ||
(tree->token.token_id == T_GREATERAND))) {
pnew = (SP_NODE*)malloc(sizeof(SP_NODE)); pnew = (SP_NODE*)malloc(sizeof(SP_NODE));
if(!pnew) { if(!pnew) {
DPRINTF(E_FATAL,L_PARSE,"Malloc error\n"); DPRINTF(E_FATAL,L_PARSE,"Malloc error\n");
@ -829,6 +862,15 @@ SP_NODE *sp_parse_criterion(PARSETREE tree) {
sp_enter_exit(tree,"sp_parse_criterion",1,expr); sp_enter_exit(tree,"sp_parse_criterion",1,expr);
if(tree->token_list == 1) {
if(tree->token.token_id != T_EXPRQUOTE) {
sp_set_error(tree,SP_E_EXPRQUOTE);
return NULL;
} else {
sp_scan(tree,SP_HINT_NONE);
}
}
switch(tree->token.token_id) { switch(tree->token.token_id) {
case T_STRING_FIELD: case T_STRING_FIELD:
expr = sp_parse_string_criterion(tree); expr = sp_parse_string_criterion(tree);
@ -849,6 +891,16 @@ SP_NODE *sp_parse_criterion(PARSETREE tree) {
break; break;
} }
if(tree->token_list == 1) {
if(tree->token.token_id != T_EXPRQUOTE) {
sp_set_error(tree,SP_E_EXPRQUOTE);
sp_free_node(expr);
return NULL;
} else {
sp_scan(tree,SP_HINT_NONE);
}
}
sp_enter_exit(tree,"sp_parse_criterion",0,expr); sp_enter_exit(tree,"sp_parse_criterion",0,expr);
return expr; return expr;
} }
@ -895,11 +947,22 @@ SP_NODE *sp_parse_criterion(PARSETREE tree) {
} }
if(result) { if(result) {
sp_scan(tree,SP_HINT_NONE); sp_scan(tree,SP_HINT_STRING);
/* should be sitting on string literal */ /* should be sitting on string literal */
if(tree->token.token_id == T_STRING) { if(tree->token.token_id == T_STRING) {
result = 1; result = 1;
pnew->right.cvalue=strdup(tree->token.data.cvalue); pnew->right.cvalue=strdup(tree->token.data.cvalue);
if(tree->token_list == 1) {
if(pnew->right.cvalue[0]=='*') {
pnew->op = T_ENDSWITH;
memcpy(pnew->right.cvalue,&pnew->right.cvalue[1],
(int)strlen(pnew->right.cvalue)); /* with zt */
}
if(pnew->right.cvalue[strlen(pnew->right.cvalue)-1] == '*') {
pnew->op = (pnew->op==T_ENDSWITH)?T_INCLUDES:T_STARTSWITH;
pnew->right.cvalue[strlen(pnew->right.cvalue)-1] = '\0';
}
}
sp_scan(tree,SP_HINT_NONE); sp_scan(tree,SP_HINT_NONE);
} else { } else {
sp_set_error(tree,SP_E_OPENQUOTE); sp_set_error(tree,SP_E_OPENQUOTE);
@ -954,6 +1017,11 @@ SP_NODE *sp_parse_criterion(PARSETREE tree) {
pnew->op=tree->token.token_id; pnew->op=tree->token.token_id;
pnew->op_type = SP_OPTYPE_INT; pnew->op_type = SP_OPTYPE_INT;
break; break;
case T_GREATERAND:
result = 1;
pnew->op = T_GREATER;
pnew->op_type = SP_OPTYPE_INT;
break;
default: default:
/* Error: expecting legal int comparison operator */ /* Error: expecting legal int comparison operator */
sp_set_error(tree,SP_E_INTCMP); sp_set_error(tree,SP_E_INTCMP);
@ -963,7 +1031,7 @@ SP_NODE *sp_parse_criterion(PARSETREE tree) {
} }
if(result) { if(result) {
sp_scan(tree,SP_HINT_NONE); sp_scan(tree,SP_HINT_INT);
/* should be sitting on a literal string */ /* should be sitting on a literal string */
if(tree->token.token_id == T_NUMBER) { if(tree->token.token_id == T_NUMBER) {
result = 1; result = 1;
@ -1023,6 +1091,11 @@ SP_NODE *sp_parse_date_criterion(PARSETREE tree) {
pnew->op=tree->token.token_id; pnew->op=tree->token.token_id;
pnew->op_type = SP_OPTYPE_DATE; pnew->op_type = SP_OPTYPE_DATE;
break; break;
case T_GREATERAND:
result = 1;
pnew->op=T_GREATER;
pnew->op_type = SP_OPTYPE_DATE;
break;
case T_BEFORE: case T_BEFORE:
result = 1; result = 1;
pnew->op_type = SP_OPTYPE_DATE; pnew->op_type = SP_OPTYPE_DATE;
@ -1042,7 +1115,7 @@ SP_NODE *sp_parse_date_criterion(PARSETREE tree) {
} }
if(result) { if(result) {
sp_scan(tree,SP_HINT_NONE); sp_scan(tree,SP_HINT_DATE);
/* should be sitting on a date */ /* should be sitting on a date */
if((pnew->right.tvalue = sp_parse_date(tree))) { if((pnew->right.tvalue = sp_parse_date(tree))) {
result=1; result=1;

View File

@ -8,10 +8,13 @@
typedef void* PARSETREE; typedef void* PARSETREE;
extern PARSETREE sp_init(void); extern PARSETREE sp_init(void);
extern int sp_parse(PARSETREE *tree, char *term); extern int sp_parse(PARSETREE *tree, char *term, int type);
extern int sp_dispose(PARSETREE tree); extern int sp_dispose(PARSETREE tree);
extern char *sp_get_error(PARSETREE tree); extern char *sp_get_error(PARSETREE tree);
extern char *sp_sql_clause(PARSETREE tree); extern char *sp_sql_clause(PARSETREE tree);
#define SP_TYPE_PLAYLIST 0
#define SP_TYPE_QUERY 1
#endif /* _SMART_PARSER_H_ */ #endif /* _SMART_PARSER_H_ */