Skip to content

Commit

Permalink
Add sort and window parameter for "sticker find"
Browse files Browse the repository at this point in the history
  • Loading branch information
jcorporation committed Nov 2, 2023
1 parent 68b19ae commit ec9784f
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 41 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ver 0.24 (not yet released)
- apply Unicode normalization to case-insensitive filter expressions
- stickers on playlists and some tag types
- new command "stickernames"
- "sticker find" supports sort and window parameter
* database
- attribute "added" shows when each song was added to the database
- proxy: require MPD 0.21 or later
Expand Down
6 changes: 4 additions & 2 deletions doc/protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1462,15 +1462,17 @@ the database for songs).

.. _command_sticker_find:

:command:`sticker find {TYPE} {URI} {NAME}`
:command:`sticker find {TYPE} {URI} {NAME} [sort {SORTTYPE}] [window {START:END}]`
Searches the sticker database for stickers with the
specified name, below the specified directory (URI).
For each matching song, it prints the URI and that one
sticker's value.

``sort`` sorts the result by "``uri``" or "``value``". [#since_0_24]_

.. _command_sticker_find_value:

:command:`sticker find {TYPE} {URI} {NAME} = {VALUE}`
:command:`sticker find {TYPE} {URI} {NAME} = {VALUE} [sort {SORTTYPE}] [window {START:END}]`
Searches for stickers with the given value.

Other supported operators are:
Expand Down
39 changes: 35 additions & 4 deletions src/command/StickerCommands.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ class DomainHandler {
return CommandResult::OK;
}

virtual CommandResult Find(const char *uri, const char *name, StickerOperator op, const char *value) {
virtual CommandResult Find(const char *uri, const char *name, StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window) {
auto data = CallbackContext{
.name = name,
.sticker_type = sticker_type,
Expand All @@ -97,6 +98,7 @@ class DomainHandler {
uri,
name,
op, value,
sort, descending, window,
callback, &data);

return CommandResult::OK;
Expand Down Expand Up @@ -172,14 +174,16 @@ class SongHandler final : public DomainHandler {
database.ReturnSong(song);
}

CommandResult Find(const char *uri, const char *name, StickerOperator op, const char *value) override {
CommandResult Find(const char *uri, const char *name, StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window) override {
struct sticker_song_find_data data = {
response,
name,
};

sticker_song_find(sticker_database, database, uri, data.name,
op, value,
sort, descending, window,
sticker_song_find_print_cb, &data);

return CommandResult::OK;
Expand Down Expand Up @@ -386,7 +390,34 @@ handle_sticker(Client &client, Request args, Response &r)
return handler->Delete(uri, sticker_name);

/* find */
if ((args.size() == 4 || args.size() == 6) && StringIsEqual(cmd, "find")) {
if (args.size() >= 4 && StringIsEqual(cmd, "find")) {
RangeArg window = RangeArg::All();
if (args.size() >= 6 && StringIsEqual(args[args.size() - 2], "window")) {
window = args.ParseRange(args.size() - 1);
args.pop_back();
args.pop_back();
}

auto sort = "";
bool descending = false;
if (args.size() >= 6 && StringIsEqual(args[args.size() - 2], "sort")) {
const char *s = args.back();
if (*s == '-') {
descending = true;
++s;
}
if (StringIsEqual(s, "uri") || StringIsEqual(s, "value")) {
sort = s;
}
else {
r.FmtError(ACK_ERROR_ARG, "Unknown sort tag \"{}\"", s);
return CommandResult::ERROR;
}

args.pop_back();
args.pop_back();
}

bool has_op = args.size() > 4;
auto value = has_op ? args[5] : nullptr;
StickerOperator op = StickerOperator::EXISTS;
Expand All @@ -404,7 +435,7 @@ handle_sticker(Client &client, Request args, Response &r)
return CommandResult::ERROR;
}
}
return handler->Find(uri, sticker_name, op, value);
return handler->Find(uri, sticker_name, op, value, sort, descending, window);
}

r.Error(ACK_ERROR_ARG, "bad request");
Expand Down
92 changes: 62 additions & 30 deletions src/sticker/Database.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,30 @@
#include "util/StringCompare.hxx"
#include "util/ScopeExit.hxx"

#include <fmt/format.h>
#include <cassert>
#include <iterator>
#include <array>
#include <stdexcept>

using namespace Sqlite;

enum sticker_sql_find {
STICKER_SQL_FIND,
STICKER_SQL_FIND_VALUE,
STICKER_SQL_FIND_LT,
STICKER_SQL_FIND_GT,

STICKER_SQL_FIND_COUNT
};

enum sticker_sql {
STICKER_SQL_GET,
STICKER_SQL_LIST,
STICKER_SQL_UPDATE,
STICKER_SQL_INSERT,
STICKER_SQL_DELETE,
STICKER_SQL_DELETE_VALUE,
STICKER_SQL_FIND,
STICKER_SQL_FIND_VALUE,
STICKER_SQL_FIND_LT,
STICKER_SQL_FIND_GT,
STICKER_SQL_DISTINCT_TYPE_URI,
STICKER_SQL_TRANSACTION_BEGIN,
STICKER_SQL_TRANSACTION_COMMIT,
Expand All @@ -37,6 +43,20 @@ enum sticker_sql {
STICKER_SQL_COUNT
};

static constexpr auto sticker_sql_find = std::array {
//[STICKER_SQL_FIND] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",

//[STICKER_SQL_FIND_VALUE] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?",

//[STICKER_SQL_FIND_LT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value<?",

//[STICKER_SQL_FIND_GT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value>?",
};

static constexpr auto sticker_sql = std::array {
//[STICKER_SQL_GET] =
"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?",
Expand All @@ -50,17 +70,6 @@ static constexpr auto sticker_sql = std::array {
"DELETE FROM sticker WHERE type=? AND uri=?",
//[STICKER_SQL_DELETE_VALUE] =
"DELETE FROM sticker WHERE type=? AND uri=? AND name=?",
//[STICKER_SQL_FIND] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",

//[STICKER_SQL_FIND_VALUE] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?",

//[STICKER_SQL_FIND_LT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value<?",

//[STICKER_SQL_FIND_GT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value>?",

//[STICKER_SQL_DISTINCT_TYPE_URI] =
"SELECT DISTINCT type,uri FROM sticker",
Expand Down Expand Up @@ -297,33 +306,56 @@ StickerDatabase::Load(const char *type, const char *uri)
sqlite3_stmt *
StickerDatabase::BindFind(const char *type, const char *base_uri,
const char *name,
StickerOperator op, const char *value)
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window)
{
assert(type != nullptr);
assert(name != nullptr);

if (base_uri == nullptr)
base_uri = "";

auto order_by = sort[0] == '\0'
? std::string()
: fmt::format("order by {} {}", sort, descending ? "desc" : "asc");

auto offset = window.IsAll()
? std::string()
: window.IsOpenEnded()
? fmt::format("limit -1 offset {}", window.start)
: fmt::format("limit {} offset {}", window.Count(), window.start);

std::string sql_str;
sqlite3_stmt *sql;

switch (op) {
case StickerOperator::EXISTS:
BindAll(stmt[STICKER_SQL_FIND], type, base_uri, name);
return stmt[STICKER_SQL_FIND];
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name);
return sql;

case StickerOperator::EQUALS:
BindAll(stmt[STICKER_SQL_FIND_VALUE],
type, base_uri, name, value);
return stmt[STICKER_SQL_FIND_VALUE];
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_VALUE], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;

case StickerOperator::LESS_THAN:
BindAll(stmt[STICKER_SQL_FIND_LT],
type, base_uri, name, value);
return stmt[STICKER_SQL_FIND_LT];
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_LT], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;

case StickerOperator::GREATER_THAN:
BindAll(stmt[STICKER_SQL_FIND_GT],
type, base_uri, name, value);
return stmt[STICKER_SQL_FIND_GT];
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_GT], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;
}

assert(false);
Expand All @@ -333,18 +365,18 @@ StickerDatabase::BindFind(const char *type, const char *base_uri,
void
StickerDatabase::Find(const char *type, const char *base_uri, const char *name,
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window,
void (*func)(const char *uri, const char *value,
void *user_data),
void *user_data)
{
assert(func != nullptr);

sqlite3_stmt *const s = BindFind(type, base_uri, name, op, value);
sqlite3_stmt *const s = BindFind(type, base_uri, name, op, value, sort, descending, window);
assert(s != nullptr);

AtScopeExit(s) {
sqlite3_reset(s);
sqlite3_clear_bindings(s);
sqlite3_finalize(s);
};

ExecuteForEach(s, [s, func, user_data](){
Expand Down
18 changes: 13 additions & 5 deletions src/sticker/Database.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "Match.hxx"
#include "lib/sqlite/Database.hxx"
#include "protocol/RangeArg.hxx"

#include <sqlite3.h>

Expand All @@ -46,10 +47,6 @@ class StickerDatabase {
SQL_INSERT,
SQL_DELETE,
SQL_DELETE_VALUE,
SQL_FIND,
SQL_FIND_VALUE,
SQL_FIND_LT,
SQL_FIND_GT,
SQL_DISTINCT_TYPE_URI,
SQL_TRANSACTION_BEGIN,
SQL_TRANSACTION_COMMIT,
Expand All @@ -59,6 +56,15 @@ class StickerDatabase {
SQL_COUNT
};

enum SQL_FIND {
SQL_FIND,
SQL_FIND_VALUE,
SQL_FIND_LT,
SQL_FIND_GT,

SQL_FIND_COUNT
};

std::string path;

Sqlite::Database db;
Expand Down Expand Up @@ -143,6 +149,7 @@ public:
*/
void Find(const char *type, const char *base_uri, const char *name,
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window,
void (*func)(const char *uri, const char *value,
void *user_data),
void *user_data);
Expand Down Expand Up @@ -178,7 +185,8 @@ private:

sqlite3_stmt *BindFind(const char *type, const char *base_uri,
const char *name,
StickerOperator op, const char *value);
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window);
};

#endif
2 changes: 2 additions & 0 deletions src/sticker/SongSticker.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ void
sticker_song_find(StickerDatabase &sticker_database, const Database &db,
const char *base_uri, const char *name,
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window,
void (*func)(const LightSong &song, const char *value,
void *user_data),
void *user_data)
Expand All @@ -114,5 +115,6 @@ sticker_song_find(StickerDatabase &sticker_database, const Database &db,
data.base_uri_length = strlen(data.base_uri);

sticker_database.Find("song", data.base_uri, name, op, value,
sort, descending, window,
sticker_song_find_cb, &data);
}
2 changes: 2 additions & 0 deletions src/sticker/SongSticker.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#define MPD_SONG_STICKER_HXX

#include "Match.hxx"
#include "protocol/RangeArg.hxx"

#include <string>

Expand Down Expand Up @@ -80,6 +81,7 @@ void
sticker_song_find(StickerDatabase &sticker_database, const Database &db,
const char *base_uri, const char *name,
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window,
void (*func)(const LightSong &song, const char *value,
void *user_data),
void *user_data);
Expand Down

0 comments on commit ec9784f

Please sign in to comment.