Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"sticker find" support for sort and window parameter and handling for integer values #1895

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ver 0.24 (not yet released)
- new commands "stickernames" and "playlistlength"
- new "search"/"find" filter "added-since"
- allow range in listplaylist and listplaylistinfo
- "sticker find" supports sort and window parameter and new sticker compare operators "eq", "lt" and "gt"
* database
- attribute "added" shows when each song was added to the database
- proxy: require MPD 0.21 or later
Expand Down
8 changes: 5 additions & 3 deletions doc/protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1486,19 +1486,21 @@ 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``","``value`` or "``value_int``" (casts the sticker value to an integer). [#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:
"``<``", "``>``"
"``<``", "``>``" for strings and "``eq``", "``lt``", "``gt``" to cast the value to an integer.

Examples:

Expand Down
48 changes: 44 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,37 @@ 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") ||
StringIsEqual(s, "value_int")
) {
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 @@ -399,12 +433,18 @@ handle_sticker(Client &client, Request args, Response &r)
op = StickerOperator::LESS_THAN;
else if (StringIsEqual(op_s, ">"))
op = StickerOperator::GREATER_THAN;
else if (StringIsEqual(op_s, "eq"))
op = StickerOperator::EQUALS_INT;
else if (StringIsEqual(op_s, "lt"))
op = StickerOperator::LESS_THAN_INT;
else if (StringIsEqual(op_s, "gt"))
op = StickerOperator::GREATER_THAN_INT;
else {
r.FmtError(ACK_ERROR_ARG, "bad operator \"{}\"", op_s);
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
128 changes: 98 additions & 30 deletions src/sticker/Database.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,34 @@
#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_EQ_INT,
STICKER_SQL_FIND_LT_INT,
STICKER_SQL_FIND_GT_INT,

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 +47,29 @@ 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>?",

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

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

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

static constexpr auto sticker_sql = std::array {
//[STICKER_SQL_GET] =
"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?",
Expand All @@ -50,17 +83,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 +319,79 @@ 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 = StringIsEmpty(sort)
? std::string()
: StringIsEqual(sort, "value_int")
? fmt::format("ORDER BY CAST(value AS INT) {}", descending ? "desc" : "asc")
: 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;

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

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

case StickerOperator::GREATER_THAN_INT:
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_GT_INT], 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 +401,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
22 changes: 17 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,19 @@ class StickerDatabase {
SQL_COUNT
};

enum SQL_FIND {
SQL_FIND,
SQL_FIND_VALUE,
SQL_FIND_LT,
SQL_FIND_GT,

SQL_FIND_EQ_INT,
SQL_FIND_LT_INT,
SQL_FIND_GT_INT,

SQL_FIND_COUNT
};

std::string path;

Sqlite::Database db;
Expand Down Expand Up @@ -143,6 +153,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 +189,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
Loading
Loading