almost have both query types working
This commit is contained in:
parent
e1c98c5fda
commit
4964fb1374
|
@ -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;
|
||||||
|
|
||||||
|
|
24
src/db-sql.c
24
src/db-sql.c
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue