mirror of
https://github.com/owntone/owntone-server.git
synced 2025-04-23 20:15:41 -04:00
add query/filter support
This commit is contained in:
parent
352f627471
commit
8e6c0d2c6d
@ -45,7 +45,7 @@ CREATE TABLE config (
|
|||||||
|
|
||||||
CREATE TABLE playlists (
|
CREATE TABLE playlists (
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
name VARCHAR(255) NOT NULL,
|
title VARCHAR(255) NOT NULL,
|
||||||
smart INTEGER NOT NULL,
|
smart INTEGER NOT NULL,
|
||||||
items INTEGER NOT NULL,
|
items INTEGER NOT NULL,
|
||||||
query VARCHAR(1024)
|
query VARCHAR(1024)
|
||||||
|
@ -38,7 +38,7 @@ typedef enum {
|
|||||||
// song meta data
|
// song meta data
|
||||||
metaSongAlbum = firstTypeSpecificMetaId,
|
metaSongAlbum = firstTypeSpecificMetaId,
|
||||||
metaSongArtist,
|
metaSongArtist,
|
||||||
metaSongBPM, /* beats per minute */
|
metaSongBPM,
|
||||||
metaSongBitRate,
|
metaSongBitRate,
|
||||||
metaSongComment,
|
metaSongComment,
|
||||||
metaSongCompilation,
|
metaSongCompilation,
|
||||||
@ -105,8 +105,7 @@ typedef struct tag_dbqueryinfo {
|
|||||||
char *whereclause;
|
char *whereclause;
|
||||||
} DBQUERYINFO;
|
} DBQUERYINFO;
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
const char* tag;
|
const char* tag;
|
||||||
MetaFieldName_t bit;
|
MetaFieldName_t bit;
|
||||||
} METAMAP;
|
} METAMAP;
|
||||||
|
@ -488,6 +488,8 @@ int db_sqlite_enum_start(DBQUERYINFO *pinfo) {
|
|||||||
if(pinfo->whereclause) {
|
if(pinfo->whereclause) {
|
||||||
if(have_clause)
|
if(have_clause)
|
||||||
strcat(query_rest," AND ");
|
strcat(query_rest," AND ");
|
||||||
|
else
|
||||||
|
strcpy(query_rest," WHERE ");
|
||||||
|
|
||||||
strcat(query_rest,"(");
|
strcat(query_rest,"(");
|
||||||
strcat(query_rest,pinfo->whereclause);
|
strcat(query_rest,pinfo->whereclause);
|
||||||
@ -628,7 +630,6 @@ int db_sqlite_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap) {
|
|||||||
if(!presult)
|
if(!presult)
|
||||||
return 0;
|
return 0;
|
||||||
db_sqlite_build_dmap(pinfo,(char**)valarray,presult,result_size);
|
db_sqlite_build_dmap(pinfo,(char**)valarray,presult,result_size);
|
||||||
DPRINTF(E_DBG,L_DB,"Building response for %s (size %d)\n",valarray[3],result_size);
|
|
||||||
*pdmap = presult;
|
*pdmap = presult;
|
||||||
return result_size;
|
return result_size;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "dynamic-art.h"
|
#include "dynamic-art.h"
|
||||||
#include "restart.h"
|
#include "restart.h"
|
||||||
#include "daapd.h"
|
#include "daapd.h"
|
||||||
|
#include "query.h"
|
||||||
|
|
||||||
/* Forwards */
|
/* Forwards */
|
||||||
static void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
|
static void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi);
|
||||||
@ -87,6 +88,7 @@ int daap_auth(char *username, char *password) {
|
|||||||
void daap_handler(WS_CONNINFO *pwsc) {
|
void daap_handler(WS_CONNINFO *pwsc) {
|
||||||
DBQUERYINFO *pqi;
|
DBQUERYINFO *pqi;
|
||||||
char *token, *string, *save;
|
char *token, *string, *save;
|
||||||
|
char *query;
|
||||||
|
|
||||||
pqi=(DBQUERYINFO*)malloc(sizeof(DBQUERYINFO));
|
pqi=(DBQUERYINFO*)malloc(sizeof(DBQUERYINFO));
|
||||||
if(!pqi) {
|
if(!pqi) {
|
||||||
@ -96,6 +98,13 @@ void daap_handler(WS_CONNINFO *pwsc) {
|
|||||||
|
|
||||||
memset(pqi,0x00,sizeof(DBQUERYINFO));
|
memset(pqi,0x00,sizeof(DBQUERYINFO));
|
||||||
|
|
||||||
|
query=ws_getvar(pwsc,"query");
|
||||||
|
if(!query) query=ws_getvar(pwsc,"filter");
|
||||||
|
if(query) {
|
||||||
|
DPRINTF(E_DBG,L_DAAP,"Getting sql clause for %s\n",query);
|
||||||
|
pqi->whereclause = query_build_sql(query);
|
||||||
|
}
|
||||||
|
|
||||||
/* Add some default headers */
|
/* Add some default headers */
|
||||||
ws_addresponseheader(pwsc,"Accept-Ranges","bytes");
|
ws_addresponseheader(pwsc,"Accept-Ranges","bytes");
|
||||||
ws_addresponseheader(pwsc,"DAAP-Server","mt-daapd/" VERSION);
|
ws_addresponseheader(pwsc,"DAAP-Server","mt-daapd/" VERSION);
|
||||||
|
407
src/query.c
407
src/query.c
@ -4,55 +4,109 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "db-generic.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
#include "query.h"
|
#include "query.h"
|
||||||
|
|
||||||
static const query_field_t* find_field(const char* name,
|
static query_node_t* query_build(const char* query);
|
||||||
const query_field_t* fields);
|
static void query_free(query_node_t* query);
|
||||||
static int arith_query(query_node_t* query, void* target);
|
static int query_build_clause(query_node_t *query, char **current, int *size);
|
||||||
static int string_query(query_node_t* query, void* target);
|
|
||||||
|
|
||||||
static query_node_t* match_specifier(const char* query,
|
static const query_field_t *find_field(const char* name,
|
||||||
|
const query_field_t* fields);
|
||||||
|
// static int arith_query(query_node_t* query, void* target);
|
||||||
|
// static int string_query(query_node_t* query, void* target);
|
||||||
|
|
||||||
|
static query_node_t *match_specifier(const char* query,
|
||||||
const char** cursor,
|
const char** cursor,
|
||||||
const query_field_t* fields);
|
const query_field_t* fields);
|
||||||
static query_node_t* group_match(const char* query,
|
|
||||||
|
static query_node_t *group_match(const char* query,
|
||||||
const char** cursor,
|
const char** cursor,
|
||||||
const query_field_t* fields);
|
const query_field_t* fields);
|
||||||
static query_node_t* single_match(const char* query,
|
|
||||||
|
static query_node_t *single_match(const char* query,
|
||||||
const char** cursor,
|
const char** cursor,
|
||||||
const query_field_t* fields);
|
const query_field_t* fields);
|
||||||
|
|
||||||
static int get_field_name(const char** pcursor,
|
static int get_field_name(const char** pcursor,
|
||||||
const char* query,
|
const char* query,
|
||||||
char* name,
|
char* name,
|
||||||
int len);
|
int len);
|
||||||
/*
|
|
||||||
static int get_opcode(const char** pcursor,
|
|
||||||
const char* query,
|
|
||||||
char* name,
|
|
||||||
int len);
|
|
||||||
*/
|
|
||||||
static query_node_t* match_number(const query_field_t* field,
|
|
||||||
char not, char opcode,
|
|
||||||
const char** pcursor,
|
|
||||||
const char* query);
|
|
||||||
static query_node_t* match_string(const query_field_t* field,
|
|
||||||
char not, char opcode,
|
|
||||||
const char** pcursor,
|
|
||||||
const char* query);
|
|
||||||
char* query_unescape(const char* query);
|
|
||||||
|
|
||||||
query_node_t* query_build(const char* query, const query_field_t* fields)
|
static query_node_t *match_number(const query_field_t* field,
|
||||||
{
|
char not, char opcode,
|
||||||
|
const char** pcursor,
|
||||||
|
const char* query);
|
||||||
|
|
||||||
|
static query_node_t *match_string(const query_field_t* field,
|
||||||
|
char not, char opcode,
|
||||||
|
const char** pcursor,
|
||||||
|
const char* query);
|
||||||
|
|
||||||
|
char *query_unescape(const char* query);
|
||||||
|
|
||||||
|
static char *query_lookup_name(char *name);
|
||||||
|
|
||||||
|
|
||||||
|
static query_field_t song_fields[] = {
|
||||||
|
{ qft_string, "dmap.itemname", "title" },
|
||||||
|
{ qft_i32, "dmap.itemid", "id" },
|
||||||
|
{ qft_string, "daap.songalbum", "album" },
|
||||||
|
{ qft_string, "daap.songartist", "artist" },
|
||||||
|
{ qft_i32, "daap.songbitrate", "bitrate" },
|
||||||
|
{ qft_string, "daap.songcomment", "comment" },
|
||||||
|
{ qft_i32, "daap.songcompilation", "compilation" },
|
||||||
|
{ qft_string, "daap.songcomposer", "composer" },
|
||||||
|
{ qft_i32, "daap.songdatakind", "data_kind" },
|
||||||
|
{ qft_string, "daap.songdataurl", "url" },
|
||||||
|
{ qft_i32, "daap.songdateadded", "time_added" },
|
||||||
|
{ qft_i32, "daap.songdatemodified","time_modified" },
|
||||||
|
{ qft_string, "daap.songdescription", "description" },
|
||||||
|
{ qft_i32, "daap.songdisccount", "total_discs" },
|
||||||
|
{ qft_i32, "daap.songdiscnumber", "disc" },
|
||||||
|
{ qft_string, "daap.songformat", "type" },
|
||||||
|
{ qft_string, "daap.songgenre", "genre" },
|
||||||
|
{ qft_i32, "daap.songsamplerate", "samplerate" },
|
||||||
|
{ qft_i32, "daap.songsize", "file_size" },
|
||||||
|
// { qft_i32_const, "daap.songstarttime", 0 },
|
||||||
|
{ qft_i32, "daap.songstoptime", "song_length" },
|
||||||
|
{ qft_i32, "daap.songtime", "song_length" },
|
||||||
|
{ qft_i32, "daap.songtrackcount", "total_tracks" },
|
||||||
|
{ qft_i32, "daap.songtracknumber", "track" },
|
||||||
|
{ qft_i32, "daap.songyear", "year" },
|
||||||
|
{ 0, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
char *query_build_sql(char *query) {
|
||||||
|
query_node_t *pquery;
|
||||||
|
char sql[2048];
|
||||||
|
char *sqlptr=sql;
|
||||||
|
int size=sizeof(sql);
|
||||||
|
|
||||||
|
pquery=query_build(query);
|
||||||
|
if(!query_build_clause(pquery,&sqlptr,&size)) {
|
||||||
|
query_free(pquery);
|
||||||
|
return strdup(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
query_free(pquery);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
query_node_t* query_build(const char* query) {
|
||||||
query_node_t* left = 0;
|
query_node_t* left = 0;
|
||||||
char* raw = query_unescape(query);
|
char* raw = query_unescape(query);
|
||||||
const char* cursor = raw;
|
const char* cursor = raw;
|
||||||
query_node_t* right = 0;
|
query_node_t* right = 0;
|
||||||
query_type_t join;
|
query_type_t join;
|
||||||
|
|
||||||
if(0 == (left = match_specifier(query, &cursor, fields)))
|
if(0 == (left = match_specifier(query, &cursor, song_fields)))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
while(*cursor)
|
while(*cursor)
|
||||||
@ -72,7 +126,7 @@ query_node_t* query_build(const char* query, const query_field_t* fields)
|
|||||||
|
|
||||||
cursor++;
|
cursor++;
|
||||||
|
|
||||||
if(0 == (right = match_specifier(raw, &cursor, fields)))
|
if(0 == (right = match_specifier(raw, &cursor, song_fields)))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
con = (query_node_t*) calloc(1, sizeof(*con));
|
con = (query_node_t*) calloc(1, sizeof(*con));
|
||||||
@ -99,12 +153,12 @@ query_node_t* query_build(const char* query, const query_field_t* fields)
|
|||||||
|
|
||||||
static query_node_t* match_specifier(const char* query,
|
static query_node_t* match_specifier(const char* query,
|
||||||
const char** cursor,
|
const char** cursor,
|
||||||
const query_field_t* fields)
|
const query_field_t* fields) {
|
||||||
{
|
switch(**cursor) {
|
||||||
switch(**cursor)
|
case '\'':
|
||||||
{
|
return single_match(query, cursor, fields);
|
||||||
case '\'': return single_match(query, cursor, fields);
|
case '(':
|
||||||
case '(': return group_match(query, cursor, fields);
|
return group_match(query, cursor, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(E_LOG,L_QRY,"Illegal character '%c' (0%o) at index %d: %s\n",
|
DPRINTF(E_LOG,L_QRY,"Illegal character '%c' (0%o) at index %d: %s\n",
|
||||||
@ -246,17 +300,14 @@ static query_node_t* single_match(const char* query,
|
|||||||
static int get_field_name(const char** pcursor,
|
static int get_field_name(const char** pcursor,
|
||||||
const char* query,
|
const char* query,
|
||||||
char* name,
|
char* name,
|
||||||
int len)
|
int len) {
|
||||||
{
|
|
||||||
const char* cursor = *pcursor;
|
const char* cursor = *pcursor;
|
||||||
|
|
||||||
if(!isalpha(*cursor))
|
if(!isalpha(*cursor))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
while(isalpha(*cursor) || *cursor == '.')
|
while(isalpha(*cursor) || *cursor == '.') {
|
||||||
{
|
if(--len <= 0) {
|
||||||
if(--len <= 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG,L_QRY,"token length exceeded at offset %d: %s\n",
|
DPRINTF(E_LOG,L_QRY,"token length exceeded at offset %d: %s\n",
|
||||||
cursor - query, query);
|
cursor - query, query);
|
||||||
return 0;
|
return 0;
|
||||||
@ -331,32 +382,26 @@ static query_node_t* match_string(const query_field_t* field,
|
|||||||
query_type_t op = qot_is;
|
query_type_t op = qot_is;
|
||||||
query_node_t* node;
|
query_node_t* node;
|
||||||
|
|
||||||
if(opcode != ':')
|
if(opcode != ':') {
|
||||||
{
|
|
||||||
DPRINTF(E_LOG,L_QRY,"Illegal operation on string: %c at index %d: %s\n",
|
DPRINTF(E_LOG,L_QRY,"Illegal operation on string: %c at index %d: %s\n",
|
||||||
opcode, cursor - query - 1);
|
opcode, cursor - query - 1);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*cursor == '*')
|
if(*cursor == '*') {
|
||||||
{
|
|
||||||
op = qot_ends;
|
op = qot_ends;
|
||||||
cursor++;
|
cursor++;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(*cursor && *cursor != '\'')
|
while(*cursor && *cursor != '\'') {
|
||||||
{
|
if(--left == 0) {
|
||||||
if(--left == 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG,L_QRY,"string too long at index %d: %s\n",
|
DPRINTF(E_LOG,L_QRY,"string too long at index %d: %s\n",
|
||||||
cursor - query, query);
|
cursor - query, query);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*cursor == '\\')
|
if(*cursor == '\\') {
|
||||||
{
|
switch(*++cursor) {
|
||||||
switch(*++cursor)
|
|
||||||
{
|
|
||||||
case '*':
|
case '*':
|
||||||
case '\'':
|
case '\'':
|
||||||
case '\\':
|
case '\\':
|
||||||
@ -367,13 +412,12 @@ static query_node_t* match_string(const query_field_t* field,
|
|||||||
*cursor, *cursor, cursor - query, query);
|
*cursor, *cursor, cursor - query, query);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
*dst++ = *cursor++;
|
*dst++ = *cursor++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(dst[-1] == '*')
|
if(dst[-1] == '*') {
|
||||||
{
|
|
||||||
op = (op == qot_is) ? qot_begins : qot_contains;
|
op = (op == qot_is) ? qot_begins : qot_contains;
|
||||||
dst--;
|
dst--;
|
||||||
}
|
}
|
||||||
@ -390,70 +434,25 @@ static query_node_t* match_string(const query_field_t* field,
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
int query_test(query_node_t* query, void* target)
|
|
||||||
{
|
|
||||||
switch(query->type)
|
|
||||||
{
|
|
||||||
/* conjunction */
|
|
||||||
case qot_and:
|
|
||||||
return (query_test(query->left.node, target) &&
|
|
||||||
query_test(query->right.node, target));
|
|
||||||
|
|
||||||
case qot_or:
|
void query_free(query_node_t* query) {
|
||||||
return (query_test(query->left.node, target) ||
|
|
||||||
query_test(query->right.node, target));
|
|
||||||
|
|
||||||
/* negation */
|
|
||||||
case qot_not:
|
|
||||||
return !query_test(query->left.node, target);
|
|
||||||
|
|
||||||
/* arithmetic */
|
|
||||||
case qot_eq:
|
|
||||||
case qot_ne:
|
|
||||||
case qot_le:
|
|
||||||
case qot_lt:
|
|
||||||
case qot_ge:
|
|
||||||
case qot_gt:
|
|
||||||
return arith_query(query, target);
|
|
||||||
|
|
||||||
/* string */
|
|
||||||
case qot_is:
|
|
||||||
case qot_begins:
|
|
||||||
case qot_ends:
|
|
||||||
case qot_contains:
|
|
||||||
return string_query(query, target);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* constants */
|
|
||||||
case qot_const:
|
|
||||||
return query->left.constant;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* should not happen */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void query_free(query_node_t* query)
|
|
||||||
{
|
|
||||||
if(0 != query)
|
if(0 != query)
|
||||||
{
|
{
|
||||||
switch(query->type)
|
switch(query->type)
|
||||||
{
|
{
|
||||||
/* conjunction */
|
// conjunction
|
||||||
case qot_and:
|
case qot_and:
|
||||||
case qot_or:
|
case qot_or:
|
||||||
query_free(query->left.node);
|
query_free(query->left.node);
|
||||||
query_free(query->right.node);
|
query_free(query->right.node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* negation */
|
// negation
|
||||||
case qot_not:
|
case qot_not:
|
||||||
query_free(query->left.node);
|
query_free(query->left.node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* arithmetic */
|
// arithmetic
|
||||||
case qot_eq:
|
case qot_eq:
|
||||||
case qot_ne:
|
case qot_ne:
|
||||||
case qot_le:
|
case qot_le:
|
||||||
@ -462,7 +461,7 @@ void query_free(query_node_t* query)
|
|||||||
case qot_gt:
|
case qot_gt:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* string */
|
// string
|
||||||
case qot_is:
|
case qot_is:
|
||||||
case qot_begins:
|
case qot_begins:
|
||||||
case qot_ends:
|
case qot_ends:
|
||||||
@ -470,7 +469,7 @@ void query_free(query_node_t* query)
|
|||||||
free(query->right.str);
|
free(query->right.str);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* constants */
|
// constants
|
||||||
case qot_const:
|
case qot_const:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -483,13 +482,11 @@ void query_free(query_node_t* query)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const query_field_t* find_field(const char* name, const query_field_t* fields)
|
static const query_field_t* find_field(const char* name, const query_field_t* fields) {
|
||||||
{
|
|
||||||
while(fields->name && strcasecmp(fields->name, name))
|
while(fields->name && strcasecmp(fields->name, name))
|
||||||
fields++;
|
fields++;
|
||||||
|
|
||||||
if(fields->name == 0)
|
if(fields->name == 0) {
|
||||||
{
|
|
||||||
DPRINTF(E_LOG,L_QRY,"Illegal query field: %s\n", name);
|
DPRINTF(E_LOG,L_QRY,"Illegal query field: %s\n", name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -497,186 +494,98 @@ static const query_field_t* find_field(const char* name, const query_field_t* fi
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int arith_query(query_node_t* query, void* target)
|
int query_add_string(char **current, int *size, char *fmt, ...) {
|
||||||
{
|
va_list ap;
|
||||||
const query_field_t* field = query->left.field;
|
int write_size;
|
||||||
|
|
||||||
switch(field->type)
|
va_start(ap, fmt);
|
||||||
{
|
write_size=vsnprintf(*current, *size, fmt, ap);
|
||||||
case qft_i32:
|
va_end(ap);
|
||||||
{
|
|
||||||
int tv = * (int*) ((size_t) target + field->offset);
|
|
||||||
|
|
||||||
tv -= query->right.i32;
|
|
||||||
|
|
||||||
switch(query->type)
|
|
||||||
{
|
|
||||||
case qot_eq: return tv == 0;
|
|
||||||
case qot_ne: return tv != 0;
|
|
||||||
case qot_le: return tv <= 0;
|
|
||||||
case qot_lt: return tv < 0;
|
|
||||||
case qot_ge: return tv >= 0;
|
|
||||||
case qot_gt: return tv > 0;
|
|
||||||
default:
|
|
||||||
DPRINTF(E_LOG,L_QRY,"illegal query type: %d\n", query->type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case qft_i64:
|
|
||||||
{
|
|
||||||
long long tv = * (long long*) ((size_t) target + field->offset);
|
|
||||||
|
|
||||||
tv -= query->right.i32;
|
|
||||||
|
|
||||||
switch(query->type)
|
|
||||||
{
|
|
||||||
case qot_eq: return tv == 0;
|
|
||||||
case qot_ne: return tv != 0;
|
|
||||||
case qot_le: return tv <= 0;
|
|
||||||
case qot_lt: return tv < 0;
|
|
||||||
case qot_ge: return tv >= 0;
|
|
||||||
case qot_gt: return tv > 0;
|
|
||||||
default:
|
|
||||||
DPRINTF(E_LOG,L_QRY,"illegal query type: %d\n", query->type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DPRINTF(E_LOG,L_QRY,"illegal field type: %d\n", field->type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(write_size > *size) {
|
||||||
|
*size=0;
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*size = *size - write_size;
|
||||||
|
return write_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int string_query(query_node_t* query, void* target)
|
int query_build_clause(query_node_t *query, char **current, int *size) {
|
||||||
{
|
char* labels[] = {
|
||||||
const query_field_t* field = query->left.field;
|
|
||||||
const char* ts;
|
|
||||||
|
|
||||||
if(field->type != qft_string)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG,L_QRY,"illegal field type: %d\n", field->type);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ts = * (const char**) ((size_t) target + field->offset);
|
|
||||||
|
|
||||||
if(0 == ts)
|
|
||||||
return strlen(query->right.str) == 0;
|
|
||||||
|
|
||||||
switch(query->type)
|
|
||||||
{
|
|
||||||
case qot_is:
|
|
||||||
return !strcasecmp(query->right.str, ts);
|
|
||||||
|
|
||||||
case qot_begins:
|
|
||||||
return !strncasecmp(query->right.str, ts, strlen(query->right.str));
|
|
||||||
|
|
||||||
case qot_ends:
|
|
||||||
{
|
|
||||||
int start = strlen(ts) - strlen(query->right.str);
|
|
||||||
|
|
||||||
if(start < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return !strcasecmp(query->right.str, ts + start);
|
|
||||||
}
|
|
||||||
|
|
||||||
case qot_contains:
|
|
||||||
return (int) strcasestr(ts, query->right.str); /* returns null if not found */
|
|
||||||
|
|
||||||
default:
|
|
||||||
DPRINTF(E_LOG,L_QRY,"Illegal query type: %d\n", query->type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void query_dump(FILE* fp, query_node_t* query, int depth)
|
|
||||||
{
|
|
||||||
static const char* labels[] = {
|
|
||||||
"NOP",
|
"NOP",
|
||||||
"and",
|
"AND",
|
||||||
"or",
|
"OR",
|
||||||
"not",
|
"NOT",
|
||||||
"==",
|
"=",
|
||||||
"!=",
|
"<>",
|
||||||
"<=",
|
"<=",
|
||||||
"<",
|
"<",
|
||||||
">=",
|
">=",
|
||||||
">",
|
">",
|
||||||
"eq",
|
"=",
|
||||||
"beginswith",
|
" (%s LIKE '%s\%) ",
|
||||||
"endwith",
|
" (%s LIKE '\%%s') ",
|
||||||
"contains",
|
" (%s LIKE '\%%s\%') ",
|
||||||
"constant"
|
"constant"
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef DEBUG
|
switch(query->type) {
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
switch(query->type)
|
|
||||||
{
|
|
||||||
case qot_and:
|
case qot_and:
|
||||||
case qot_or:
|
case qot_or:
|
||||||
fprintf(fp, "%*s(%s\n", depth, "", labels[query->type]);
|
if(*size) (*current) += query_add_string(current,size," (");
|
||||||
query_dump(fp, query->left.node, depth + 4);
|
if(query_build_clause(query->left.node,current,size)) return 1;
|
||||||
query_dump(fp, query->right.node, depth + 4);
|
if(*size) (*current) += query_add_string(current,size," %s ", labels[query->type]);
|
||||||
fprintf(fp, "%*s)\n", depth, "");
|
if(query_build_clause(query->right.node,current,size)) return 1;
|
||||||
|
if(*size) (*current) += query_add_string(current,size,") ");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case qot_not:
|
case qot_not:
|
||||||
fprintf(fp, "%*s(not\n", depth, "");
|
if(*size) (*current) += query_add_string(current,size," (NOT ");
|
||||||
query_dump(fp, query->left.node, depth + 4);
|
if(query_build_clause(query->left.node,current,size)) return 1;
|
||||||
fprintf(fp, "%*s)\n", depth, "");
|
if(*size) (*current) += query_add_string(current,size,") ");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* arithmetic */
|
|
||||||
case qot_eq:
|
case qot_eq:
|
||||||
case qot_ne:
|
case qot_ne:
|
||||||
case qot_le:
|
case qot_le:
|
||||||
case qot_lt:
|
case qot_lt:
|
||||||
case qot_ge:
|
case qot_ge:
|
||||||
case qot_gt:
|
case qot_gt:
|
||||||
if(query->left.field->type == qft_i32)
|
if(*size) (*current) += query_add_string(current,size," (%s %s ",
|
||||||
fprintf(fp, "%*s(%s %s %d)\n",
|
query->left.field->fieldname,
|
||||||
depth, "", labels[query->type],
|
labels[query->type]);
|
||||||
query->left.field->name, query->right.i32);
|
if(query->left.field->type == qft_i32) {
|
||||||
else
|
if(*size) (*current) += query_add_string(current,size," %d) ",query->right.i32);
|
||||||
fprintf(fp, "%*s(%s %s %ll)\n",
|
} else {
|
||||||
depth, "", labels[query->type],
|
if(*size) (*current) += query_add_string(current,size," %ll) ",query->right.i64);
|
||||||
query->left.field->name, query->right.i64);
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* string */
|
|
||||||
case qot_is:
|
case qot_is:
|
||||||
|
if(*size)(*current) += query_add_string(current,size," (%s='%s') ",
|
||||||
|
query->left.field->fieldname,
|
||||||
|
query->right.str);
|
||||||
|
break;
|
||||||
case qot_begins:
|
case qot_begins:
|
||||||
case qot_ends:
|
case qot_ends:
|
||||||
case qot_contains:
|
case qot_contains:
|
||||||
fprintf(fp, "%*s(%s %s \"%s\")\n",
|
if(*size)(*current) += query_add_string(current,size,labels[query->type],
|
||||||
depth, "", labels[query->type],
|
query->left.field->fieldname,
|
||||||
query->left.field->name, query->right.str);
|
query->right.str);
|
||||||
break;
|
break;
|
||||||
|
case qot_const: /* Not sure what this would be for */
|
||||||
/* constants */
|
|
||||||
case qot_const:
|
|
||||||
fprintf(fp, "%*s(%s)\n", depth, "", query->left.constant ? "true" : "false");
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* query_unescape(const char* src)
|
char* query_unescape(const char* src) {
|
||||||
{
|
|
||||||
char* copy = malloc(strlen(src) + 1);
|
char* copy = malloc(strlen(src) + 1);
|
||||||
char* dst = copy;
|
char* dst = copy;
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ struct query_field_
|
|||||||
{
|
{
|
||||||
query_type_t type;
|
query_type_t type;
|
||||||
const char* name;
|
const char* name;
|
||||||
int offset;
|
const char* fieldname;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct query_node_ query_node_t;
|
typedef struct query_node_ query_node_t;
|
||||||
@ -62,10 +62,6 @@ struct query_node_
|
|||||||
} right;
|
} right;
|
||||||
};
|
};
|
||||||
|
|
||||||
query_node_t* query_build(const char* query,
|
extern char *query_build_sql(char *query);
|
||||||
const query_field_t* fields);
|
|
||||||
int query_test(query_node_t* query, void* target);
|
|
||||||
void query_free(query_node_t* query);
|
|
||||||
void query_dump(FILE* fp, query_node_t* query, int depth);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user