Commit 77c9081f authored by Max Kellermann's avatar Max Kellermann

sticker/Database: wrap in class StickerDatabase

parent c88d5616
......@@ -107,9 +107,9 @@ Instance::OnDatabaseSongRemoved(const char *uri) noexcept
#ifdef ENABLE_SQLITE
/* if the song has a sticker, remove it */
if (sticker_enabled()) {
if (HasStickerDatabase()) {
try {
sticker_song_delete(uri);
sticker_song_delete(*sticker_database, uri);
} catch (...) {
}
}
......
......@@ -53,6 +53,7 @@ class ClientList;
struct Partition;
class StateFile;
class RemoteTagCache;
class StickerDatabase;
/**
* A utility class which, when used as the first base class, ensures
......@@ -125,6 +126,10 @@ struct Instance final
StateFile *state_file = nullptr;
#ifdef ENABLE_SQLITE
std::unique_ptr<StickerDatabase> sticker_database;
#endif
Instance();
~Instance() noexcept;
......@@ -166,6 +171,12 @@ struct Instance final
const Database &GetDatabaseOrThrow() const;
#endif
#ifdef ENABLE_SQLITE
bool HasStickerDatabase() noexcept {
return sticker_database != nullptr;
}
#endif
void BeginShutdownUpdate() noexcept;
#ifdef ENABLE_CURL
......
......@@ -237,23 +237,23 @@ InitDatabaseAndStorage(const ConfigData &config)
#endif
#ifdef ENABLE_SQLITE
/**
* Configure and initialize the sticker subsystem.
*/
static void
glue_sticker_init(const ConfigData &config)
static std::unique_ptr<StickerDatabase>
LoadStickerDatabase(const ConfigData &config)
{
#ifdef ENABLE_SQLITE
auto sticker_file = config.GetPath(ConfigOption::STICKER_FILE);
if (sticker_file.IsNull())
return;
return nullptr;
sticker_global_init(std::move(sticker_file));
#else
(void)config;
#endif
return std::make_unique<StickerDatabase>(std::move(sticker_file));
}
#endif
static void
glue_state_file_init(const ConfigData &raw_config)
{
......@@ -513,7 +513,9 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
const bool create_db = InitDatabaseAndStorage(raw_config);
#endif
glue_sticker_init(raw_config);
#ifdef ENABLE_SQLITE
instance->sticker_database = LoadStickerDatabase(raw_config);
#endif
command_init();
......@@ -625,10 +627,6 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
}
#endif
#ifdef ENABLE_SQLITE
sticker_global_finish();
#endif
return EXIT_SUCCESS;
}
......
......@@ -38,6 +38,7 @@
#include "Permission.hxx"
#include "tag/Type.h"
#include "Partition.hxx"
#include "Instance.hxx"
#include "client/Client.hxx"
#include "client/Response.hxx"
#include "util/Macros.hxx"
......@@ -216,7 +217,7 @@ command_available(gcc_unused const Partition &partition,
{
#ifdef ENABLE_SQLITE
if (StringIsEqual(cmd->cmd, "sticker"))
return sticker_enabled();
return partition.instance.HasStickerDatabase();
#endif
#ifdef ENABLE_NEIGHBOR_PLUGINS
......
......@@ -28,6 +28,7 @@
#include "client/Client.hxx"
#include "client/Response.hxx"
#include "Partition.hxx"
#include "Instance.hxx"
#include "util/StringAPI.hxx"
#include "util/ScopeExit.hxx"
......@@ -50,7 +51,9 @@ sticker_song_find_print_cb(const LightSong &song, const char *value,
}
static CommandResult
handle_sticker_song(Response &r, Partition &partition, Request args)
handle_sticker_song(Response &r, Partition &partition,
StickerDatabase &sticker_database,
Request args)
{
const Database &db = partition.GetDatabaseOrThrow();
......@@ -62,7 +65,8 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); };
const auto value = sticker_song_get_value(*song, args[3]);
const auto value = sticker_song_get_value(sticker_database,
*song, args[3]);
if (value.empty()) {
r.Error(ACK_ERROR_NO_EXIST, "no such sticker");
return CommandResult::ERROR;
......@@ -77,7 +81,7 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); };
const auto sticker = sticker_song_get(*song);
const auto sticker = sticker_song_get(sticker_database, *song);
sticker_print(r, sticker);
return CommandResult::OK;
......@@ -87,7 +91,8 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); };
sticker_song_set_value(*song, args[3], args[4]);
sticker_song_set_value(sticker_database, *song,
args[3], args[4]);
return CommandResult::OK;
/* delete song song_id [key] */
} else if ((args.size == 3 || args.size == 4) &&
......@@ -97,8 +102,9 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
AtScopeExit(&db, song) { db.ReturnSong(song); };
bool ret = args.size == 3
? sticker_song_delete(*song)
: sticker_song_delete_value(*song, args[3]);
? sticker_song_delete(sticker_database, *song)
: sticker_song_delete_value(sticker_database, *song,
args[3]);
if (!ret) {
r.Error(ACK_ERROR_NO_EXIST, "no such sticker");
return CommandResult::ERROR;
......@@ -138,7 +144,7 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
args[3],
};
sticker_song_find(db, base_uri, data.name,
sticker_song_find(sticker_database, db, base_uri, data.name,
op, value,
sticker_song_find_print_cb, &data);
......@@ -154,13 +160,18 @@ handle_sticker(Client &client, Request args, Response &r)
{
assert(args.size >= 3);
if (!sticker_enabled()) {
auto &instance = client.GetInstance();
if (!instance.HasStickerDatabase()) {
r.Error(ACK_ERROR_UNKNOWN, "sticker database is disabled");
return CommandResult::ERROR;
}
auto &sticker_database = *instance.sticker_database;
if (StringIsEqual(args[1], "song"))
return handle_sticker_song(r, client.GetPartition(), args);
return handle_sticker_song(r, client.GetPartition(),
sticker_database,
args);
else {
r.Error(ACK_ERROR_ARG, "unknown sticker domain");
return CommandResult::ERROR;
......
......@@ -45,96 +45,116 @@
#include "Match.hxx"
#include "util/Compiler.h"
#include <sqlite3.h>
#include <map>
#include <string>
class Path;
struct Sticker;
/**
* Opens the sticker database.
*
* Throws std::runtime_error on error.
*/
void
sticker_global_init(Path path);
class StickerDatabase {
enum SQL {
SQL_GET,
SQL_LIST,
SQL_UPDATE,
SQL_INSERT,
SQL_DELETE,
SQL_DELETE_VALUE,
SQL_FIND,
SQL_FIND_VALUE,
SQL_FIND_LT,
SQL_FIND_GT,
/**
* Close the sticker database.
*/
void
sticker_global_finish() noexcept;
SQL_COUNT
};
/**
* Returns true if the sticker database is configured and available.
*/
gcc_const
bool
sticker_enabled() noexcept;
sqlite3 *db;
sqlite3_stmt *stmt[SQL_COUNT];
/**
* Returns one value from an object's sticker record. Returns an
* empty string if the value doesn't exist.
*
* Throws #SqliteError on error.
*/
std::string
sticker_load_value(const char *type, const char *uri, const char *name);
public:
/**
* Opens the sticker database.
*
* Throws on error.
*/
StickerDatabase(Path path);
~StickerDatabase() noexcept;
/**
* Sets a sticker value in the specified object. Overwrites existing
* values.
*
* Throws #SqliteError on error.
*/
void
sticker_store_value(const char *type, const char *uri,
const char *name, const char *value);
/**
* Returns one value from an object's sticker record. Returns an
* empty string if the value doesn't exist.
*
* Throws #SqliteError on error.
*/
std::string LoadValue(const char *type, const char *uri,
const char *name);
/**
* Deletes a sticker from the database. All sticker values of the
* specified object are deleted.
*
* Throws #SqliteError on error.
*/
bool
sticker_delete(const char *type, const char *uri);
/**
* Sets a sticker value in the specified object. Overwrites existing
* values.
*
* Throws #SqliteError on error.
*/
void StoreValue(const char *type, const char *uri,
const char *name, const char *value);
/**
* Deletes a sticker value. Fails if no sticker with this name
* exists.
*
* Throws #SqliteError on error.
*/
bool
sticker_delete_value(const char *type, const char *uri, const char *name);
/**
* Deletes a sticker from the database. All sticker values of the
* specified object are deleted.
*
* Throws #SqliteError on error.
*/
bool Delete(const char *type, const char *uri);
/**
* Loads the sticker for the specified resource.
*
* Throws #SqliteError on error.
*
* @param type the resource type, e.g. "song"
* @param uri the URI of the resource, e.g. the song path
* @return a sticker object
*/
Sticker
sticker_load(const char *type, const char *uri);
/**
* Deletes a sticker value. Fails if no sticker with this name
* exists.
*
* Throws #SqliteError on error.
*/
bool DeleteValue(const char *type, const char *uri, const char *name);
/**
* Finds stickers with the specified name below the specified URI.
*
* @param type the resource type, e.g. "song"
* @param base_uri the URI prefix of the resources, or nullptr if all
* resources should be searched
* @param name the name of the sticker
* @param op the comparison operator
* @param value the operand
*/
void
sticker_find(const char *type, const char *base_uri, const char *name,
StickerOperator op, const char *value,
void (*func)(const char *uri, const char *value,
void *user_data),
void *user_data);
/**
* Loads the sticker for the specified resource.
*
* Throws #SqliteError on error.
*
* @param type the resource type, e.g. "song"
* @param uri the URI of the resource, e.g. the song path
* @return a sticker object
*/
Sticker Load(const char *type, const char *uri);
/**
* Finds stickers with the specified name below the specified URI.
*
* @param type the resource type, e.g. "song"
* @param base_uri the URI prefix of the resources, or nullptr if all
* resources should be searched
* @param name the name of the sticker
* @param op the comparison operator
* @param value the operand
*/
void Find(const char *type, const char *base_uri, const char *name,
StickerOperator op, const char *value,
void (*func)(const char *uri, const char *value,
void *user_data),
void *user_data);
private:
void ListValues(std::map<std::string, std::string> &table,
const char *type, const char *uri);
bool UpdateValue(const char *type, const char *uri,
const char *name, const char *value);
void InsertValue(const char *type, const char *uri,
const char *name, const char *value);
sqlite3_stmt *BindFind(const char *type, const char *base_uri,
const char *name,
StickerOperator op, const char *value);
};
#endif
......@@ -29,44 +29,47 @@
#include <stdlib.h>
std::string
sticker_song_get_value(const LightSong &song, const char *name)
sticker_song_get_value(StickerDatabase &db,
const LightSong &song, const char *name)
{
const auto uri = song.GetURI();
return sticker_load_value("song", uri.c_str(), name);
return db.LoadValue("song", uri.c_str(), name);
}
void
sticker_song_set_value(const LightSong &song,
sticker_song_set_value(StickerDatabase &db,
const LightSong &song,
const char *name, const char *value)
{
const auto uri = song.GetURI();
sticker_store_value("song", uri.c_str(), name, value);
db.StoreValue("song", uri.c_str(), name, value);
}
bool
sticker_song_delete(const char *uri)
sticker_song_delete(StickerDatabase &db, const char *uri)
{
return sticker_delete("song", uri);
return db.Delete("song", uri);
}
bool
sticker_song_delete(const LightSong &song)
sticker_song_delete(StickerDatabase &db, const LightSong &song)
{
return sticker_song_delete(song.GetURI().c_str());
return sticker_song_delete(db, song.GetURI().c_str());
}
bool
sticker_song_delete_value(const LightSong &song, const char *name)
sticker_song_delete_value(StickerDatabase &db,
const LightSong &song, const char *name)
{
const auto uri = song.GetURI();
return sticker_delete_value("song", uri.c_str(), name);
return db.DeleteValue("song", uri.c_str(), name);
}
Sticker
sticker_song_get(const LightSong &song)
sticker_song_get(StickerDatabase &db, const LightSong &song)
{
const auto uri = song.GetURI();
return sticker_load("song", uri.c_str());
return db.Load("song", uri.c_str());
}
namespace {
......@@ -101,7 +104,8 @@ sticker_song_find_cb(const char *uri, const char *value, void *user_data)
}
void
sticker_song_find(const Database &db, const char *base_uri, const char *name,
sticker_song_find(StickerDatabase &sticker_database, const Database &db,
const char *base_uri, const char *name,
StickerOperator op, const char *value,
void (*func)(const LightSong &song, const char *value,
void *user_data),
......@@ -126,6 +130,6 @@ sticker_song_find(const Database &db, const char *base_uri, const char *name,
data.base_uri_length = strlen(data.base_uri);
sticker_find("song", data.base_uri, name, op, value,
sticker_song_find_cb, &data);
sticker_database.Find("song", data.base_uri, name, op, value,
sticker_song_find_cb, &data);
}
......@@ -27,6 +27,7 @@
struct LightSong;
struct Sticker;
class Database;
class StickerDatabase;
/**
* Returns one value from a song's sticker record.
......@@ -34,7 +35,8 @@ class Database;
* Throws #SqliteError on error.
*/
std::string
sticker_song_get_value(const LightSong &song, const char *name);
sticker_song_get_value(StickerDatabase &db,
const LightSong &song, const char *name);
/**
* Sets a sticker value in the specified song. Overwrites existing
......@@ -43,7 +45,8 @@ sticker_song_get_value(const LightSong &song, const char *name);
* Throws #SqliteError on error.
*/
void
sticker_song_set_value(const LightSong &song,
sticker_song_set_value(StickerDatabase &db,
const LightSong &song,
const char *name, const char *value);
/**
......@@ -52,10 +55,10 @@ sticker_song_set_value(const LightSong &song,
* Throws #SqliteError on error.
*/
bool
sticker_song_delete(const char *uri);
sticker_song_delete(StickerDatabase &db, const char *uri);
bool
sticker_song_delete(const LightSong &song);
sticker_song_delete(StickerDatabase &db, const LightSong &song);
/**
* Deletes a sticker value. Does nothing if the sticker did not
......@@ -64,7 +67,8 @@ sticker_song_delete(const LightSong &song);
* Throws #SqliteError on error.
*/
bool
sticker_song_delete_value(const LightSong &song, const char *name);
sticker_song_delete_value(StickerDatabase &db,
const LightSong &song, const char *name);
/**
* Loads the sticker for the specified song.
......@@ -75,7 +79,7 @@ sticker_song_delete_value(const LightSong &song, const char *name);
* @return a sticker object
*/
Sticker
sticker_song_get(const LightSong &song);
sticker_song_get(StickerDatabase &db, const LightSong &song);
/**
* Finds stickers with the specified name below the specified
......@@ -89,7 +93,8 @@ sticker_song_get(const LightSong &song);
* @param name the name of the sticker
*/
void
sticker_song_find(const Database &db, const char *base_uri, const char *name,
sticker_song_find(StickerDatabase &sticker_database, const Database &db,
const char *base_uri, const char *name,
StickerOperator op, const char *value,
void (*func)(const LightSong &song, const char *value,
void *user_data),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment