From 1078c1c1bf61f1b7830297e28f31b85694a75639 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 11 Dec 2024 21:41:20 +0100 Subject: [PATCH] Add sticker sub-commands inc and dec Let sqlite do the work for incrementing or decrementing a sticker value. This sub-commands are usefull to track playcounts with sticker values and saves us one roundtrip. --- NEWS | 1 + doc/protocol.rst | 14 ++++++++ src/command/StickerCommands.cxx | 26 +++++++++++++++ src/sticker/Database.cxx | 58 +++++++++++++++++++++++++++++++++ src/sticker/Database.hxx | 20 ++++++++++++ src/sticker/SongSticker.cxx | 18 ++++++++++ src/sticker/SongSticker.hxx | 22 +++++++++++++ 7 files changed, 159 insertions(+) diff --git a/NEWS b/NEWS index 80f4f0b92b..e9f22deda3 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,7 @@ ver 0.24 (not yet released) - volume command is no longer deprecated - new "available" and "reset" subcommands for tagtypes - searching stored playlists respond now with song position + - new sticker subcommand "inc" and "dec" * database - attribute "added" shows when each song was added to the database - fix integer overflows with 64-bit inode numbers diff --git a/doc/protocol.rst b/doc/protocol.rst index 3c7007f417..3062549be4 100644 --- a/doc/protocol.rst +++ b/doc/protocol.rst @@ -1518,6 +1518,20 @@ the database for songs). sticker item with that name already exists, it is replaced. +.. _command_sticker_inc: + +:command:`sticker inc {TYPE} {URI} {NAME} {VALUE}` + Adds a sticker value to the specified object. If a + sticker item with that name already exists, it is + incremented by supplied value. + +.. _command_sticker_dec: + +:command:`sticker dec {TYPE} {URI} {NAME} {VALUE}` + Adds a sticker value to the specified object. If a + sticker item with that name already exists, it is + decremented by supplied value. + .. _command_sticker_delete: :command:`sticker delete {TYPE} {URI} [NAME]` diff --git a/src/command/StickerCommands.cxx b/src/command/StickerCommands.cxx index 009756e983..79be2e98d6 100644 --- a/src/command/StickerCommands.cxx +++ b/src/command/StickerCommands.cxx @@ -58,6 +58,24 @@ class DomainHandler { return CommandResult::OK; } + virtual CommandResult Inc(const char *uri, const char *name, const char *value) { + sticker_database.IncValue(sticker_type, + ValidateUri(uri).c_str(), + name, + value); + + return CommandResult::OK; + } + + virtual CommandResult Dec(const char *uri, const char *name, const char *value) { + sticker_database.DecValue(sticker_type, + ValidateUri(uri).c_str(), + name, + value); + + return CommandResult::OK; + } + virtual CommandResult Delete(const char *uri, const char *name) { std::string validated_uri = ValidateUri(uri); uri = validated_uri.c_str(); @@ -442,6 +460,14 @@ handle_sticker(Client &client, Request args, Response &r) /* set */ if (args.size() == 5 && StringIsEqual(cmd, "set")) return handler->Set(uri, sticker_name, args[4]); + + /* inc */ + if (args.size() == 5 && StringIsEqual(cmd, "inc")) + return handler->Inc(uri, sticker_name, args[4]); + + /* dec */ + if (args.size() == 5 && StringIsEqual(cmd, "dec")) + return handler->Dec(uri, sticker_name, args[4]); /* delete */ if ((args.size() == 3 || args.size() == 4) && StringIsEqual(cmd, "delete")) diff --git a/src/sticker/Database.cxx b/src/sticker/Database.cxx index 66612a58ec..95dcad221f 100644 --- a/src/sticker/Database.cxx +++ b/src/sticker/Database.cxx @@ -48,6 +48,8 @@ enum sticker_sql { STICKER_SQL_NAMES, STICKER_SQL_NAMES_TYPES, STICKER_SQL_NAMES_TYPES_BY_TYPE, + STICKER_SQL_INC, + STICKER_SQL_DEC, STICKER_SQL_COUNT }; @@ -115,6 +117,16 @@ static constexpr auto sticker_sql = std::array { //[STICKER_SQL_NAMES_TYPES_BY_TYPE] "SELECT name,type FROM sticker WHERE type = ? GROUP BY name,type ORDER BY name", + + //[STICKER_SQL_INC] = + "INSERT INTO sticker (type, uri, name, value) VALUES (?, ?, ?, ?) " + "ON CONFLICT(type, uri, name) DO " + "UPDATE set value = value + ?", + + //[STICKER_SQL_DEC] = + "INSERT INTO sticker (type, uri, name, value) VALUES (?, ?, ?, ?) " + "ON CONFLICT(type, uri, name) DO " + "UPDATE set value = value - ?", }; static constexpr const char sticker_sql_create[] = @@ -280,6 +292,52 @@ StickerDatabase::StoreValue(const char *type, const char *uri, InsertValue(type, uri, name, value); } +void +StickerDatabase::IncValue(const char *type, const char *uri, + const char *name, const char *value) +{ + sqlite3_stmt *const s = stmt[STICKER_SQL_INC]; + + assert(type != nullptr); + assert(uri != nullptr); + assert(name != nullptr); + assert(*name != 0); + assert(value != nullptr); + + BindAll(s, type, uri, name, value, value); + + AtScopeExit(s) { + sqlite3_reset(s); + sqlite3_clear_bindings(s); + }; + + ExecuteCommand(s); + idle_add(IDLE_STICKER); +} + +void +StickerDatabase::DecValue(const char *type, const char *uri, + const char *name, const char *value) +{ + sqlite3_stmt *const s = stmt[STICKER_SQL_DEC]; + + assert(type != nullptr); + assert(uri != nullptr); + assert(name != nullptr); + assert(*name != 0); + assert(value != nullptr); + + BindAll(s, type, uri, name, value, value); + + AtScopeExit(s) { + sqlite3_reset(s); + sqlite3_clear_bindings(s); + }; + + ExecuteCommand(s); + idle_add(IDLE_STICKER); +} + bool StickerDatabase::Delete(const char *type, const char *uri) { diff --git a/src/sticker/Database.hxx b/src/sticker/Database.hxx index cf0958dcae..068def8e91 100644 --- a/src/sticker/Database.hxx +++ b/src/sticker/Database.hxx @@ -54,6 +54,8 @@ class StickerDatabase { SQL_NAMES, SQL_NAMES_TYPES, SQL_NAMES_TYPES_BY_TYPE, + STICKER_SQL_INC, + STICKER_SQL_DEC, SQL_COUNT }; @@ -118,6 +120,24 @@ public: */ void StoreValue(const char *type, const char *uri, const char *name, const char *value); + + /** + * Increments a sticker by value in the specified object. Inserts + * the value if object does not exist. + * + * Throws #SqliteError on error. + */ + void IncValue(const char *type, const char *uri, + const char *name, const char *value); + + /** + * Decrements a sticker by value in the specified object. Inserts + * the value if object does not exist. + * + * Throws #SqliteError on error. + */ + void DecValue(const char *type, const char *uri, + const char *name, const char *value); /** * Deletes a sticker from the database. All sticker values of the diff --git a/src/sticker/SongSticker.cxx b/src/sticker/SongSticker.cxx index 401ad81cc8..b7b695d8c4 100644 --- a/src/sticker/SongSticker.cxx +++ b/src/sticker/SongSticker.cxx @@ -30,6 +30,24 @@ sticker_song_set_value(StickerDatabase &db, db.StoreValue("song", uri.c_str(), name, value); } +void +sticker_song_inc_value(StickerDatabase &db, + const LightSong &song, + const char *name, const char *value) +{ + const auto uri = song.GetURI(); + db.IncValue("song", uri.c_str(), name, value); +} + +void +sticker_song_dec_value(StickerDatabase &db, + const LightSong &song, + const char *name, const char *value) +{ + const auto uri = song.GetURI(); + db.DecValue("song", uri.c_str(), name, value); +} + bool sticker_song_delete(StickerDatabase &db, const char *uri) { diff --git a/src/sticker/SongSticker.hxx b/src/sticker/SongSticker.hxx index 2d6587bb31..92fbe52c69 100644 --- a/src/sticker/SongSticker.hxx +++ b/src/sticker/SongSticker.hxx @@ -34,6 +34,28 @@ sticker_song_set_value(StickerDatabase &db, const LightSong &song, const char *name, const char *value); +/** + * Increments a sticker by value in the specified object. Inserts + * the value if object does not exist. + * + * Throws #SqliteError on error. + */ +void +sticker_song_inc_value(StickerDatabase &db, + const LightSong &song, + const char *name, const char *value); + +/** + * Decrements a sticker by value in the specified object. Inserts + * the value if object does not exist. + * + * Throws #SqliteError on error. + */ +void +sticker_song_dec_value(StickerDatabase &db, + const LightSong &song, + const char *name, const char *value); + /** * Deletes a sticker from the database. All values are deleted. *