diff --git a/contrib/sqlite.sql b/contrib/sqlite.sql index 6574151e..e160751f 100644 --- a/contrib/sqlite.sql +++ b/contrib/sqlite.sql @@ -45,7 +45,7 @@ CREATE TABLE config ( CREATE TABLE playlists ( id INTEGER PRIMARY KEY NOT NULL, - name VARCHAR(255) NOT NULL, + title VARCHAR(255) NOT NULL, smart INTEGER NOT NULL, items INTEGER NOT NULL, query VARCHAR(1024) diff --git a/src/db-generic.c b/src/db-generic.c index e5519917..a853708c 100644 --- a/src/db-generic.c +++ b/src/db-generic.c @@ -87,101 +87,101 @@ DB_FUNCTIONS db_functions[] = { }; DAAP_ITEMS taglist[] = { - { 0x05, "miid", "dmap.itemid" }, - { 0x09, "minm", "dmap.itemname" }, - { 0x01, "mikd", "dmap.itemkind" }, - { 0x07, "mper", "dmap.persistentid" }, - { 0x0C, "mcon", "dmap.container" }, - { 0x05, "mcti", "dmap.containeritemid" }, - { 0x05, "mpco", "dmap.parentcontainerid" }, - { 0x05, "mstt", "dmap.status" }, - { 0x09, "msts", "dmap.statusstring" }, - { 0x05, "mimc", "dmap.itemcount" }, - { 0x05, "mctc", "dmap.containercount" }, - { 0x05, "mrco", "dmap.returnedcount" }, - { 0x05, "mtco", "dmap.specifiedtotalcount" }, - { 0x0C, "mlcl", "dmap.listing" }, - { 0x0C, "mlit", "dmap.listingitem" }, - { 0x0C, "mbcl", "dmap.bag" }, - { 0x0C, "mdcl", "dmap.dictionary" }, - { 0x0C, "msrv", "dmap.serverinforesponse" }, - { 0x01, "msau", "dmap.authenticationmethod" }, - { 0x01, "mslr", "dmap.loginrequired" }, - { 0x0B, "mpro", "dmap.protocolversion" }, - { 0x01, "msal", "dmap.supportsautologout" }, - { 0x01, "msup", "dmap.supportsupdate" }, - { 0x01, "mspi", "dmap.supportspersistentids" }, - { 0x01, "msex", "dmap.supportsextensions" }, - { 0x01, "msbr", "dmap.supportsbrowse" }, - { 0x01, "msqy", "dmap.supportsquery" }, - { 0x01, "msix", "dmap.supportsindex" }, - { 0x01, "msrs", "dmap.supportsresolve" }, - { 0x05, "mstm", "dmap.timeoutinterval" }, - { 0x05, "msdc", "dmap.databasescount" }, - { 0x0C, "mlog", "dmap.loginresponse" }, - { 0x05, "mlid", "dmap.sessionid" }, - { 0x0C, "mupd", "dmap.updateresponse" }, - { 0x05, "musr", "dmap.serverrevision" }, - { 0x01, "muty", "dmap.updatetype" }, - { 0x0C, "mudl", "dmap.deletedidlisting" }, - { 0x0C, "mccr", "dmap.contentcodesresponse" }, - { 0x05, "mcnm", "dmap.contentcodesnumber" }, - { 0x09, "mcna", "dmap.contentcodesname" }, - { 0x03, "mcty", "dmap.contentcodestype" }, - { 0x0B, "apro", "daap.protocolversion" }, - { 0x0C, "avdb", "daap.serverdatabases" }, - { 0x0C, "abro", "daap.databasebrowse" }, - { 0x0C, "abal", "daap.browsealbumlisting" }, - { 0x0C, "abar", "daap.browseartistlisting" }, - { 0x0C, "abcp", "daap.browsecomposerlisting" }, - { 0x0C, "abgn", "daap.browsegenrelisting" }, - { 0x0C, "adbs", "daap.databasesongs" }, - { 0x09, "asal", "daap.songalbum" }, - { 0x09, "asar", "daap.songartist" }, - { 0x03, "asbt", "daap.songbeatsperminute" }, - { 0x03, "asbr", "daap.songbitrate" }, - { 0x09, "ascm", "daap.songcomment" }, - { 0x01, "asco", "daap.songcompilation" }, - { 0x09, "ascp", "daap.songcomposer" }, - { 0x0A, "asda", "daap.songdateadded" }, - { 0x0A, "asdm", "daap.songdatemodified" }, - { 0x03, "asdc", "daap.songdisccount" }, - { 0x03, "asdn", "daap.songdiscnumber" }, - { 0x01, "asdb", "daap.songdisabled" }, - { 0x09, "aseq", "daap.songeqpreset" }, - { 0x09, "asfm", "daap.songformat" }, - { 0x09, "asgn", "daap.songgenre" }, - { 0x09, "asdt", "daap.songdescription" }, - { 0x02, "asrv", "daap.songrelativevolume" }, - { 0x05, "assr", "daap.songsamplerate" }, - { 0x05, "assz", "daap.songsize" }, - { 0x05, "asst", "daap.songstarttime" }, - { 0x05, "assp", "daap.songstoptime" }, - { 0x05, "astm", "daap.songtime" }, - { 0x03, "astc", "daap.songtrackcount" }, - { 0x03, "astn", "daap.songtracknumber" }, - { 0x01, "asur", "daap.songuserrating" }, - { 0x03, "asyr", "daap.songyear" }, - { 0x01, "asdk", "daap.songdatakind" }, - { 0x09, "asul", "daap.songdataurl" }, - { 0x0C, "aply", "daap.databaseplaylists" }, - { 0x01, "abpl", "daap.baseplaylist" }, - { 0x0C, "apso", "daap.playlistsongs" }, - { 0x0C, "arsv", "daap.resolve" }, - { 0x0C, "arif", "daap.resolveinfo" }, - { 0x05, "aeNV", "com.apple.itunes.norm-volume" }, - { 0x01, "aeSP", "com.apple.itunes.smart-playlist" }, - /* iTunes 4.5+ */ - { 0x01, "msas", "dmap.authenticationschemes" }, - { 0x05, "ascd", "daap.songcodectype" }, - { 0x09, "agrp", "daap.songgrouping" }, - { 0x05, "aeSV", "com.apple.itunes.music-sharing-version" }, - { 0x05, "aePI", "com.apple.itunes.itms-playlistid" }, - { 0x05, "aeCI", "com.apple.iTunes.itms-composerid" }, - { 0x05, "aeGI", "com.apple.iTunes.itms-genreid" }, - { 0x05, "aeAI", "com.apple.iTunes.itms-artistid" }, - { 0x05, "aeSI", "com.apple.iTunes.itms-songid" }, - { 0x00, NULL, NULL } + { 0x05, "miid", "dmap.itemid" }, + { 0x09, "minm", "dmap.itemname" }, + { 0x01, "mikd", "dmap.itemkind" }, + { 0x07, "mper", "dmap.persistentid" }, + { 0x0C, "mcon", "dmap.container" }, + { 0x05, "mcti", "dmap.containeritemid" }, + { 0x05, "mpco", "dmap.parentcontainerid" }, + { 0x05, "mstt", "dmap.status" }, + { 0x09, "msts", "dmap.statusstring" }, + { 0x05, "mimc", "dmap.itemcount" }, + { 0x05, "mctc", "dmap.containercount" }, + { 0x05, "mrco", "dmap.returnedcount" }, + { 0x05, "mtco", "dmap.specifiedtotalcount" }, + { 0x0C, "mlcl", "dmap.listing" }, + { 0x0C, "mlit", "dmap.listingitem" }, + { 0x0C, "mbcl", "dmap.bag" }, + { 0x0C, "mdcl", "dmap.dictionary" }, + { 0x0C, "msrv", "dmap.serverinforesponse" }, + { 0x01, "msau", "dmap.authenticationmethod" }, + { 0x01, "mslr", "dmap.loginrequired" }, + { 0x0B, "mpro", "dmap.protocolversion" }, + { 0x01, "msal", "dmap.supportsautologout" }, + { 0x01, "msup", "dmap.supportsupdate" }, + { 0x01, "mspi", "dmap.supportspersistentids" }, + { 0x01, "msex", "dmap.supportsextensions" }, + { 0x01, "msbr", "dmap.supportsbrowse" }, + { 0x01, "msqy", "dmap.supportsquery" }, + { 0x01, "msix", "dmap.supportsindex" }, + { 0x01, "msrs", "dmap.supportsresolve" }, + { 0x05, "mstm", "dmap.timeoutinterval" }, + { 0x05, "msdc", "dmap.databasescount" }, + { 0x0C, "mlog", "dmap.loginresponse" }, + { 0x05, "mlid", "dmap.sessionid" }, + { 0x0C, "mupd", "dmap.updateresponse" }, + { 0x05, "musr", "dmap.serverrevision" }, + { 0x01, "muty", "dmap.updatetype" }, + { 0x0C, "mudl", "dmap.deletedidlisting" }, + { 0x0C, "mccr", "dmap.contentcodesresponse" }, + { 0x05, "mcnm", "dmap.contentcodesnumber" }, + { 0x09, "mcna", "dmap.contentcodesname" }, + { 0x03, "mcty", "dmap.contentcodestype" }, + { 0x0B, "apro", "daap.protocolversion" }, + { 0x0C, "avdb", "daap.serverdatabases" }, + { 0x0C, "abro", "daap.databasebrowse" }, + { 0x0C, "abal", "daap.browsealbumlisting" }, + { 0x0C, "abar", "daap.browseartistlisting" }, + { 0x0C, "abcp", "daap.browsecomposerlisting" }, + { 0x0C, "abgn", "daap.browsegenrelisting" }, + { 0x0C, "adbs", "daap.databasesongs" }, + { 0x09, "asal", "daap.songalbum" }, + { 0x09, "asar", "daap.songartist" }, + { 0x03, "asbt", "daap.songbeatsperminute" }, + { 0x03, "asbr", "daap.songbitrate" }, + { 0x09, "ascm", "daap.songcomment" }, + { 0x01, "asco", "daap.songcompilation" }, + { 0x09, "ascp", "daap.songcomposer" }, + { 0x0A, "asda", "daap.songdateadded" }, + { 0x0A, "asdm", "daap.songdatemodified" }, + { 0x03, "asdc", "daap.songdisccount" }, + { 0x03, "asdn", "daap.songdiscnumber" }, + { 0x01, "asdb", "daap.songdisabled" }, + { 0x09, "aseq", "daap.songeqpreset" }, + { 0x09, "asfm", "daap.songformat" }, + { 0x09, "asgn", "daap.songgenre" }, + { 0x09, "asdt", "daap.songdescription" }, + { 0x02, "asrv", "daap.songrelativevolume" }, + { 0x05, "assr", "daap.songsamplerate" }, + { 0x05, "assz", "daap.songsize" }, + { 0x05, "asst", "daap.songstarttime" }, + { 0x05, "assp", "daap.songstoptime" }, + { 0x05, "astm", "daap.songtime" }, + { 0x03, "astc", "daap.songtrackcount" }, + { 0x03, "astn", "daap.songtracknumber" }, + { 0x01, "asur", "daap.songuserrating" }, + { 0x03, "asyr", "daap.songyear" }, + { 0x01, "asdk", "daap.songdatakind" }, + { 0x09, "asul", "daap.songdataurl" }, + { 0x0C, "aply", "daap.databaseplaylists" }, + { 0x01, "abpl", "daap.baseplaylist" }, + { 0x0C, "apso", "daap.playlistsongs" }, + { 0x0C, "arsv", "daap.resolve" }, + { 0x0C, "arif", "daap.resolveinfo" }, + { 0x05, "aeNV", "com.apple.itunes.norm-volume" }, + { 0x01, "aeSP", "com.apple.itunes.smart-playlist" }, + /* iTunes 4.5+ */ + { 0x01, "msas", "dmap.authenticationschemes" }, + { 0x05, "ascd", "daap.songcodectype" }, + { 0x09, "agrp", "daap.songgrouping" }, + { 0x05, "aeSV", "com.apple.itunes.music-sharing-version" }, + { 0x05, "aePI", "com.apple.itunes.itms-playlistid" }, + { 0x05, "aeCI", "com.apple.iTunes.itms-composerid" }, + { 0x05, "aeGI", "com.apple.iTunes.itms-genreid" }, + { 0x05, "aeAI", "com.apple.iTunes.itms-artistid" }, + { 0x05, "aeSI", "com.apple.iTunes.itms-songid" }, + { 0x00, NULL, NULL } }; /** map the string names specified in the meta= tag to bit numbers */ diff --git a/src/db-generic.h b/src/db-generic.h index cd1dcbe7..d3fb90c7 100644 --- a/src/db-generic.h +++ b/src/db-generic.h @@ -38,7 +38,7 @@ typedef enum { // song meta data metaSongAlbum = firstTypeSpecificMetaId, metaSongArtist, - metaSongBPM, /* beats per minute */ + metaSongBPM, metaSongBitRate, metaSongComment, metaSongCompilation, @@ -105,10 +105,9 @@ typedef struct tag_dbqueryinfo { char *whereclause; } DBQUERYINFO; -typedef struct -{ - const char* tag; - MetaFieldName_t bit; +typedef struct { + const char* tag; + MetaFieldName_t bit; } METAMAP; typedef struct tag_daap_items { diff --git a/src/dbs-sqlite.c b/src/dbs-sqlite.c index 7917f3b4..a02d7801 100644 --- a/src/dbs-sqlite.c +++ b/src/dbs-sqlite.c @@ -488,6 +488,8 @@ int db_sqlite_enum_start(DBQUERYINFO *pinfo) { if(pinfo->whereclause) { if(have_clause) strcat(query_rest," AND "); + else + strcpy(query_rest," WHERE "); strcat(query_rest,"("); strcat(query_rest,pinfo->whereclause); @@ -628,7 +630,6 @@ int db_sqlite_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap) { if(!presult) return 0; 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; return result_size; } diff --git a/src/dispatch.c b/src/dispatch.c index 4a1ec4b9..17b7e8a6 100644 --- a/src/dispatch.c +++ b/src/dispatch.c @@ -40,6 +40,7 @@ #include "dynamic-art.h" #include "restart.h" #include "daapd.h" +#include "query.h" /* Forwards */ 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) { DBQUERYINFO *pqi; char *token, *string, *save; + char *query; pqi=(DBQUERYINFO*)malloc(sizeof(DBQUERYINFO)); if(!pqi) { @@ -96,6 +98,13 @@ void daap_handler(WS_CONNINFO *pwsc) { 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 */ ws_addresponseheader(pwsc,"Accept-Ranges","bytes"); ws_addresponseheader(pwsc,"DAAP-Server","mt-daapd/" VERSION); diff --git a/src/query.c b/src/query.c index 23705702..adba05f1 100644 --- a/src/query.c +++ b/src/query.c @@ -4,55 +4,109 @@ #include #include +#include #include #include +#include "db-generic.h" #include "err.h" #include "query.h" -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* query_build(const char* query); +static void query_free(query_node_t* query); +static int query_build_clause(query_node_t *query, char **current, int *size); -static query_node_t* match_specifier(const char* query, - const char** cursor, - const query_field_t* fields); -static query_node_t* group_match(const char* query, - const char** cursor, - const query_field_t* fields); -static query_node_t* single_match(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 query_field_t* fields); -static int get_field_name(const char** pcursor, - const char* query, - char* name, - 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 *group_match(const char* query, + const char** cursor, + const query_field_t* fields); + +static query_node_t *single_match(const char* query, + const char** cursor, + const query_field_t* fields); + +static int get_field_name(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); + +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; char* raw = query_unescape(query); const char* cursor = raw; query_node_t* right = 0; query_type_t join; - if(0 == (left = match_specifier(query, &cursor, fields))) + if(0 == (left = match_specifier(query, &cursor, song_fields))) goto error; while(*cursor) @@ -72,7 +126,7 @@ query_node_t* query_build(const char* query, const query_field_t* fields) cursor++; - if(0 == (right = match_specifier(raw, &cursor, fields))) + if(0 == (right = match_specifier(raw, &cursor, song_fields))) goto error; 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, const char** cursor, - const query_field_t* fields) -{ - switch(**cursor) - { - case '\'': return single_match(query, cursor, fields); - case '(': return group_match(query, cursor, fields); + const query_field_t* fields) { + switch(**cursor) { + case '\'': + return single_match(query, cursor, fields); + case '(': + return group_match(query, cursor, fields); } DPRINTF(E_LOG,L_QRY,"Illegal character '%c' (0%o) at index %d: %s\n", @@ -243,20 +297,17 @@ static query_node_t* single_match(const char* query, return node; } -static int get_field_name(const char** pcursor, - const char* query, - char* name, - int len) -{ +static int get_field_name(const char** pcursor, + const char* query, + char* name, + int len) { const char* cursor = *pcursor; if(!isalpha(*cursor)) return 0; - while(isalpha(*cursor) || *cursor == '.') - { - if(--len <= 0) - { + while(isalpha(*cursor) || *cursor == '.') { + if(--len <= 0) { DPRINTF(E_LOG,L_QRY,"token length exceeded at offset %d: %s\n", cursor - query, query); return 0; @@ -331,32 +382,26 @@ static query_node_t* match_string(const query_field_t* field, query_type_t op = qot_is; query_node_t* node; - if(opcode != ':') - { + if(opcode != ':') { DPRINTF(E_LOG,L_QRY,"Illegal operation on string: %c at index %d: %s\n", opcode, cursor - query - 1); return NULL; } - if(*cursor == '*') - { + if(*cursor == '*') { op = qot_ends; cursor++; } - while(*cursor && *cursor != '\'') - { - if(--left == 0) - { + while(*cursor && *cursor != '\'') { + if(--left == 0) { DPRINTF(E_LOG,L_QRY,"string too long at index %d: %s\n", cursor - query, query); return NULL; } - if(*cursor == '\\') - { - switch(*++cursor) - { + if(*cursor == '\\') { + switch(*++cursor) { case '*': case '\'': case '\\': @@ -367,13 +412,12 @@ static query_node_t* match_string(const query_field_t* field, *cursor, *cursor, cursor - query, query); return NULL; } - } - else + } else { *dst++ = *cursor++; + } } - if(dst[-1] == '*') - { + if(dst[-1] == '*') { op = (op == qot_is) ? qot_begins : qot_contains; dst--; } @@ -390,70 +434,25 @@ static query_node_t* match_string(const query_field_t* field, 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: - 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) -{ +void query_free(query_node_t* query) { if(0 != query) { switch(query->type) { - /* conjunction */ + // conjunction case qot_and: case qot_or: query_free(query->left.node); query_free(query->right.node); break; - /* negation */ + // negation case qot_not: query_free(query->left.node); break; - /* arithmetic */ + // arithmetic case qot_eq: case qot_ne: case qot_le: @@ -462,7 +461,7 @@ void query_free(query_node_t* query) case qot_gt: break; - /* string */ + // string case qot_is: case qot_begins: case qot_ends: @@ -470,7 +469,7 @@ void query_free(query_node_t* query) free(query->right.str); break; - /* constants */ + // constants case qot_const: 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)) fields++; - if(fields->name == 0) - { + if(fields->name == 0) { DPRINTF(E_LOG,L_QRY,"Illegal query field: %s\n", name); return NULL; } @@ -497,186 +494,98 @@ static const query_field_t* find_field(const char* name, const query_field_t* fi return fields; } -static int arith_query(query_node_t* query, void* target) -{ - const query_field_t* field = query->left.field; +int query_add_string(char **current, int *size, char *fmt, ...) { + va_list ap; + int write_size; - switch(field->type) - { - case qft_i32: - { - int tv = * (int*) ((size_t) target + field->offset); + va_start(ap, fmt); + write_size=vsnprintf(*current, *size, fmt, ap); + va_end(ap); - 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; - } - - return 0; -} - -static int string_query(query_node_t* query, void* target) -{ - 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); + if(write_size > *size) { + *size=0; 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; + *size = *size - write_size; + return write_size; } -void query_dump(FILE* fp, query_node_t* query, int depth) -{ - static const char* labels[] = { +int query_build_clause(query_node_t *query, char **current, int *size) { + char* labels[] = { "NOP", - "and", - "or", - "not", - "==", - "!=", + "AND", + "OR", + "NOT", + "=", + "<>", "<=", "<", ">=", ">", - "eq", - "beginswith", - "endwith", - "contains", + "=", + " (%s LIKE '%s\%) ", + " (%s LIKE '\%%s') ", + " (%s LIKE '\%%s\%') ", "constant" }; -#ifndef DEBUG - return; -#endif - - switch(query->type) - { + switch(query->type) { case qot_and: case qot_or: - fprintf(fp, "%*s(%s\n", depth, "", labels[query->type]); - query_dump(fp, query->left.node, depth + 4); - query_dump(fp, query->right.node, depth + 4); - fprintf(fp, "%*s)\n", depth, ""); + if(*size) (*current) += query_add_string(current,size," ("); + if(query_build_clause(query->left.node,current,size)) return 1; + if(*size) (*current) += query_add_string(current,size," %s ", labels[query->type]); + if(query_build_clause(query->right.node,current,size)) return 1; + if(*size) (*current) += query_add_string(current,size,") "); break; case qot_not: - fprintf(fp, "%*s(not\n", depth, ""); - query_dump(fp, query->left.node, depth + 4); - fprintf(fp, "%*s)\n", depth, ""); + if(*size) (*current) += query_add_string(current,size," (NOT "); + if(query_build_clause(query->left.node,current,size)) return 1; + if(*size) (*current) += query_add_string(current,size,") "); break; - - /* arithmetic */ + case qot_eq: case qot_ne: case qot_le: case qot_lt: case qot_ge: case qot_gt: - if(query->left.field->type == qft_i32) - fprintf(fp, "%*s(%s %s %d)\n", - depth, "", labels[query->type], - query->left.field->name, query->right.i32); - else - fprintf(fp, "%*s(%s %s %ll)\n", - depth, "", labels[query->type], - query->left.field->name, query->right.i64); + if(*size) (*current) += query_add_string(current,size," (%s %s ", + query->left.field->fieldname, + labels[query->type]); + if(query->left.field->type == qft_i32) { + if(*size) (*current) += query_add_string(current,size," %d) ",query->right.i32); + } else { + if(*size) (*current) += query_add_string(current,size," %ll) ",query->right.i64); + } break; - /* string */ + 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_ends: case qot_contains: - fprintf(fp, "%*s(%s %s \"%s\")\n", - depth, "", labels[query->type], - query->left.field->name, query->right.str); - break; - - /* constants */ - case qot_const: - fprintf(fp, "%*s(%s)\n", depth, "", query->left.constant ? "true" : "false"); + if(*size)(*current) += query_add_string(current,size,labels[query->type], + query->left.field->fieldname, + query->right.str); break; + case qot_const: /* Not sure what this would be for */ + break; default: break; } - + + return 0; } -char* query_unescape(const char* src) -{ +char* query_unescape(const char* src) { + char* copy = malloc(strlen(src) + 1); char* dst = copy; diff --git a/src/query.h b/src/query.h index 0d52bf6f..9b2ace6b 100644 --- a/src/query.h +++ b/src/query.h @@ -40,9 +40,9 @@ typedef enum typedef struct query_field_ query_field_t; struct query_field_ { - query_type_t type; - const char* name; - int offset; + query_type_t type; + const char* name; + const char* fieldname; }; typedef struct query_node_ query_node_t; @@ -62,10 +62,6 @@ struct query_node_ } right; }; -query_node_t* query_build(const 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); +extern char *query_build_sql(char *query); #endif