sticker/Database: wrap in class StickerDatabase

This commit is contained in:
Max Kellermann 2019-04-24 15:18:01 +02:00
parent c88d5616f7
commit 77c9081f78
9 changed files with 273 additions and 243 deletions

View File

@ -107,9 +107,9 @@ Instance::OnDatabaseSongRemoved(const char *uri) noexcept
#ifdef ENABLE_SQLITE #ifdef ENABLE_SQLITE
/* if the song has a sticker, remove it */ /* if the song has a sticker, remove it */
if (sticker_enabled()) { if (HasStickerDatabase()) {
try { try {
sticker_song_delete(uri); sticker_song_delete(*sticker_database, uri);
} catch (...) { } catch (...) {
} }
} }

View File

@ -53,6 +53,7 @@ class ClientList;
struct Partition; struct Partition;
class StateFile; class StateFile;
class RemoteTagCache; class RemoteTagCache;
class StickerDatabase;
/** /**
* A utility class which, when used as the first base class, ensures * A utility class which, when used as the first base class, ensures
@ -125,6 +126,10 @@ struct Instance final
StateFile *state_file = nullptr; StateFile *state_file = nullptr;
#ifdef ENABLE_SQLITE
std::unique_ptr<StickerDatabase> sticker_database;
#endif
Instance(); Instance();
~Instance() noexcept; ~Instance() noexcept;
@ -166,6 +171,12 @@ struct Instance final
const Database &GetDatabaseOrThrow() const; const Database &GetDatabaseOrThrow() const;
#endif #endif
#ifdef ENABLE_SQLITE
bool HasStickerDatabase() noexcept {
return sticker_database != nullptr;
}
#endif
void BeginShutdownUpdate() noexcept; void BeginShutdownUpdate() noexcept;
#ifdef ENABLE_CURL #ifdef ENABLE_CURL

View File

@ -237,23 +237,23 @@ InitDatabaseAndStorage(const ConfigData &config)
#endif #endif
#ifdef ENABLE_SQLITE
/** /**
* Configure and initialize the sticker subsystem. * Configure and initialize the sticker subsystem.
*/ */
static void static std::unique_ptr<StickerDatabase>
glue_sticker_init(const ConfigData &config) LoadStickerDatabase(const ConfigData &config)
{ {
#ifdef ENABLE_SQLITE
auto sticker_file = config.GetPath(ConfigOption::STICKER_FILE); auto sticker_file = config.GetPath(ConfigOption::STICKER_FILE);
if (sticker_file.IsNull()) if (sticker_file.IsNull())
return; return nullptr;
sticker_global_init(std::move(sticker_file)); return std::make_unique<StickerDatabase>(std::move(sticker_file));
#else
(void)config;
#endif
} }
#endif
static void static void
glue_state_file_init(const ConfigData &raw_config) 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); const bool create_db = InitDatabaseAndStorage(raw_config);
#endif #endif
glue_sticker_init(raw_config); #ifdef ENABLE_SQLITE
instance->sticker_database = LoadStickerDatabase(raw_config);
#endif
command_init(); command_init();
@ -625,10 +627,6 @@ mpd_main_after_fork(const ConfigData &raw_config, const Config &config)
} }
#endif #endif
#ifdef ENABLE_SQLITE
sticker_global_finish();
#endif
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -38,6 +38,7 @@
#include "Permission.hxx" #include "Permission.hxx"
#include "tag/Type.h" #include "tag/Type.h"
#include "Partition.hxx" #include "Partition.hxx"
#include "Instance.hxx"
#include "client/Client.hxx" #include "client/Client.hxx"
#include "client/Response.hxx" #include "client/Response.hxx"
#include "util/Macros.hxx" #include "util/Macros.hxx"
@ -216,7 +217,7 @@ command_available(gcc_unused const Partition &partition,
{ {
#ifdef ENABLE_SQLITE #ifdef ENABLE_SQLITE
if (StringIsEqual(cmd->cmd, "sticker")) if (StringIsEqual(cmd->cmd, "sticker"))
return sticker_enabled(); return partition.instance.HasStickerDatabase();
#endif #endif
#ifdef ENABLE_NEIGHBOR_PLUGINS #ifdef ENABLE_NEIGHBOR_PLUGINS

View File

@ -28,6 +28,7 @@
#include "client/Client.hxx" #include "client/Client.hxx"
#include "client/Response.hxx" #include "client/Response.hxx"
#include "Partition.hxx" #include "Partition.hxx"
#include "Instance.hxx"
#include "util/StringAPI.hxx" #include "util/StringAPI.hxx"
#include "util/ScopeExit.hxx" #include "util/ScopeExit.hxx"
@ -50,7 +51,9 @@ sticker_song_find_print_cb(const LightSong &song, const char *value,
} }
static CommandResult 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(); const Database &db = partition.GetDatabaseOrThrow();
@ -62,7 +65,8 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
assert(song != nullptr); assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); }; 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()) { if (value.empty()) {
r.Error(ACK_ERROR_NO_EXIST, "no such sticker"); r.Error(ACK_ERROR_NO_EXIST, "no such sticker");
return CommandResult::ERROR; return CommandResult::ERROR;
@ -77,7 +81,7 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
assert(song != nullptr); assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); }; 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); sticker_print(r, sticker);
return CommandResult::OK; return CommandResult::OK;
@ -87,7 +91,8 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
assert(song != nullptr); assert(song != nullptr);
AtScopeExit(&db, song) { db.ReturnSong(song); }; 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; return CommandResult::OK;
/* delete song song_id [key] */ /* delete song song_id [key] */
} else if ((args.size == 3 || args.size == 4) && } 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); }; AtScopeExit(&db, song) { db.ReturnSong(song); };
bool ret = args.size == 3 bool ret = args.size == 3
? sticker_song_delete(*song) ? sticker_song_delete(sticker_database, *song)
: sticker_song_delete_value(*song, args[3]); : sticker_song_delete_value(sticker_database, *song,
args[3]);
if (!ret) { if (!ret) {
r.Error(ACK_ERROR_NO_EXIST, "no such sticker"); r.Error(ACK_ERROR_NO_EXIST, "no such sticker");
return CommandResult::ERROR; return CommandResult::ERROR;
@ -138,7 +144,7 @@ handle_sticker_song(Response &r, Partition &partition, Request args)
args[3], args[3],
}; };
sticker_song_find(db, base_uri, data.name, sticker_song_find(sticker_database, db, base_uri, data.name,
op, value, op, value,
sticker_song_find_print_cb, &data); sticker_song_find_print_cb, &data);
@ -154,13 +160,18 @@ handle_sticker(Client &client, Request args, Response &r)
{ {
assert(args.size >= 3); assert(args.size >= 3);
if (!sticker_enabled()) { auto &instance = client.GetInstance();
if (!instance.HasStickerDatabase()) {
r.Error(ACK_ERROR_UNKNOWN, "sticker database is disabled"); r.Error(ACK_ERROR_UNKNOWN, "sticker database is disabled");
return CommandResult::ERROR; return CommandResult::ERROR;
} }
auto &sticker_database = *instance.sticker_database;
if (StringIsEqual(args[1], "song")) if (StringIsEqual(args[1], "song"))
return handle_sticker_song(r, client.GetPartition(), args); return handle_sticker_song(r, client.GetPartition(),
sticker_database,
args);
else { else {
r.Error(ACK_ERROR_ARG, "unknown sticker domain"); r.Error(ACK_ERROR_ARG, "unknown sticker domain");
return CommandResult::ERROR; return CommandResult::ERROR;

View File

@ -41,6 +41,7 @@ enum sticker_sql {
STICKER_SQL_FIND_VALUE, STICKER_SQL_FIND_VALUE,
STICKER_SQL_FIND_LT, STICKER_SQL_FIND_LT,
STICKER_SQL_FIND_GT, STICKER_SQL_FIND_GT,
STICKER_SQL_COUNT
}; };
static const char *const sticker_sql[] = { static const char *const sticker_sql[] = {
@ -80,11 +81,7 @@ static const char sticker_sql_create[] =
" sticker_value ON sticker(type, uri, name);" " sticker_value ON sticker(type, uri, name);"
""; "";
static sqlite3 *sticker_db; StickerDatabase::StickerDatabase(Path path)
static sqlite3_stmt *sticker_stmt[ARRAY_SIZE(sticker_sql)];
void
sticker_global_init(Path path)
{ {
assert(!path.IsNull()); assert(!path.IsNull());
@ -92,20 +89,20 @@ sticker_global_init(Path path)
/* open/create the sqlite database */ /* open/create the sqlite database */
ret = sqlite3_open(path.c_str(), &sticker_db); ret = sqlite3_open(path.c_str(), &db);
if (ret != SQLITE_OK) { if (ret != SQLITE_OK) {
const std::string utf8 = path.ToUTF8(); const std::string utf8 = path.ToUTF8();
throw SqliteError(sticker_db, ret, throw SqliteError(db, ret,
("Failed to open sqlite database '" + ("Failed to open sqlite database '" +
utf8 + "'").c_str()); utf8 + "'").c_str());
} }
/* create the table and index */ /* create the table and index */
ret = sqlite3_exec(sticker_db, sticker_sql_create, ret = sqlite3_exec(db, sticker_sql_create,
nullptr, nullptr, nullptr); nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) if (ret != SQLITE_OK)
throw SqliteError(sticker_db, ret, throw SqliteError(db, ret,
"Failed to create sticker table"); "Failed to create sticker table");
/* prepare the statements we're going to use */ /* prepare the statements we're going to use */
@ -113,38 +110,28 @@ sticker_global_init(Path path)
for (unsigned i = 0; i < ARRAY_SIZE(sticker_sql); ++i) { for (unsigned i = 0; i < ARRAY_SIZE(sticker_sql); ++i) {
assert(sticker_sql[i] != nullptr); assert(sticker_sql[i] != nullptr);
sticker_stmt[i] = Prepare(sticker_db, sticker_sql[i]); stmt[i] = Prepare(db, sticker_sql[i]);
} }
} }
void StickerDatabase::~StickerDatabase() noexcept
sticker_global_finish() noexcept
{ {
if (sticker_db == nullptr) assert(db != nullptr);
/* not configured */
return;
for (unsigned i = 0; i < ARRAY_SIZE(sticker_stmt); ++i) { for (unsigned i = 0; i < ARRAY_SIZE(stmt); ++i) {
assert(sticker_stmt[i] != nullptr); assert(stmt[i] != nullptr);
sqlite3_finalize(sticker_stmt[i]); sqlite3_finalize(stmt[i]);
} }
sqlite3_close(sticker_db); sqlite3_close(db);
}
bool
sticker_enabled() noexcept
{
return sticker_db != nullptr;
} }
std::string std::string
sticker_load_value(const char *type, const char *uri, const char *name) StickerDatabase::LoadValue(const char *type, const char *uri, const char *name)
{ {
sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_GET]; sqlite3_stmt *const s = stmt[STICKER_SQL_GET];
assert(sticker_enabled());
assert(type != nullptr); assert(type != nullptr);
assert(uri != nullptr); assert(uri != nullptr);
assert(name != nullptr); assert(name != nullptr);
@ -152,49 +139,48 @@ sticker_load_value(const char *type, const char *uri, const char *name)
if (StringIsEmpty(name)) if (StringIsEmpty(name))
return std::string(); return std::string();
BindAll(stmt, type, uri, name); BindAll(s, type, uri, name);
AtScopeExit(stmt) { AtScopeExit(s) {
sqlite3_reset(stmt); sqlite3_reset(s);
sqlite3_clear_bindings(stmt); sqlite3_clear_bindings(s);
}; };
std::string value; std::string value;
if (ExecuteRow(stmt)) if (ExecuteRow(s))
value = (const char*)sqlite3_column_text(stmt, 0); value = (const char*)sqlite3_column_text(s, 0);
return value; return value;
} }
static void void
sticker_list_values(std::map<std::string, std::string> &table, StickerDatabase::ListValues(std::map<std::string, std::string> &table,
const char *type, const char *uri) const char *type, const char *uri)
{ {
sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST]; sqlite3_stmt *const s = stmt[STICKER_SQL_LIST];
assert(type != nullptr); assert(type != nullptr);
assert(uri != nullptr); assert(uri != nullptr);
assert(sticker_enabled());
BindAll(stmt, type, uri); BindAll(s, type, uri);
AtScopeExit(stmt) { AtScopeExit(s) {
sqlite3_reset(stmt); sqlite3_reset(s);
sqlite3_clear_bindings(stmt); sqlite3_clear_bindings(s);
}; };
ExecuteForEach(stmt, [stmt, &table](){ ExecuteForEach(s, [s, &table](){
const char *name = (const char *)sqlite3_column_text(stmt, 0); const char *name = (const char *)sqlite3_column_text(s, 0);
const char *value = (const char *)sqlite3_column_text(stmt, 1); const char *value = (const char *)sqlite3_column_text(s, 1);
table.insert(std::make_pair(name, value)); table.insert(std::make_pair(name, value));
}); });
} }
static bool bool
sticker_update_value(const char *type, const char *uri, StickerDatabase::UpdateValue(const char *type, const char *uri,
const char *name, const char *value) const char *name, const char *value)
{ {
sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_UPDATE]; sqlite3_stmt *const s = stmt[STICKER_SQL_UPDATE];
assert(type != nullptr); assert(type != nullptr);
assert(uri != nullptr); assert(uri != nullptr);
@ -202,27 +188,25 @@ sticker_update_value(const char *type, const char *uri,
assert(*name != 0); assert(*name != 0);
assert(value != nullptr); assert(value != nullptr);
assert(sticker_enabled()); BindAll(s, value, type, uri, name);
BindAll(stmt, value, type, uri, name); AtScopeExit(s) {
sqlite3_reset(s);
AtScopeExit(stmt) { sqlite3_clear_bindings(s);
sqlite3_reset(stmt);
sqlite3_clear_bindings(stmt);
}; };
bool modified = ExecuteModified(stmt); bool modified = ExecuteModified(s);
if (modified) if (modified)
idle_add(IDLE_STICKER); idle_add(IDLE_STICKER);
return modified; return modified;
} }
static void void
sticker_insert_value(const char *type, const char *uri, StickerDatabase::InsertValue(const char *type, const char *uri,
const char *name, const char *value) const char *name, const char *value)
{ {
sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_INSERT]; sqlite3_stmt *const s = stmt[STICKER_SQL_INSERT];
assert(type != nullptr); assert(type != nullptr);
assert(uri != nullptr); assert(uri != nullptr);
@ -230,24 +214,21 @@ sticker_insert_value(const char *type, const char *uri,
assert(*name != 0); assert(*name != 0);
assert(value != nullptr); assert(value != nullptr);
assert(sticker_enabled()); BindAll(s, type, uri, name, value);
BindAll(stmt, type, uri, name, value); AtScopeExit(s) {
sqlite3_reset(s);
AtScopeExit(stmt) { sqlite3_clear_bindings(s);
sqlite3_reset(stmt);
sqlite3_clear_bindings(stmt);
}; };
ExecuteCommand(stmt); ExecuteCommand(s);
idle_add(IDLE_STICKER); idle_add(IDLE_STICKER);
} }
void void
sticker_store_value(const char *type, const char *uri, StickerDatabase::StoreValue(const char *type, const char *uri,
const char *name, const char *value) const char *name, const char *value)
{ {
assert(sticker_enabled());
assert(type != nullptr); assert(type != nullptr);
assert(uri != nullptr); assert(uri != nullptr);
assert(name != nullptr); assert(name != nullptr);
@ -256,67 +237,67 @@ sticker_store_value(const char *type, const char *uri,
if (StringIsEmpty(name)) if (StringIsEmpty(name))
return; return;
if (!sticker_update_value(type, uri, name, value)) if (!UpdateValue(type, uri, name, value))
sticker_insert_value(type, uri, name, value); InsertValue(type, uri, name, value);
} }
bool bool
sticker_delete(const char *type, const char *uri) StickerDatabase::Delete(const char *type, const char *uri)
{ {
sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE]; sqlite3_stmt *const s = stmt[STICKER_SQL_DELETE];
assert(sticker_enabled());
assert(type != nullptr); assert(type != nullptr);
assert(uri != nullptr); assert(uri != nullptr);
BindAll(stmt, type, uri); BindAll(s, type, uri);
AtScopeExit(stmt) { AtScopeExit(s) {
sqlite3_reset(stmt); sqlite3_reset(s);
sqlite3_clear_bindings(stmt); sqlite3_clear_bindings(s);
}; };
bool modified = ExecuteModified(stmt); bool modified = ExecuteModified(s);
if (modified) if (modified)
idle_add(IDLE_STICKER); idle_add(IDLE_STICKER);
return modified; return modified;
} }
bool bool
sticker_delete_value(const char *type, const char *uri, const char *name) StickerDatabase::DeleteValue(const char *type, const char *uri,
const char *name)
{ {
sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE_VALUE]; sqlite3_stmt *const s = stmt[STICKER_SQL_DELETE_VALUE];
assert(sticker_enabled());
assert(type != nullptr); assert(type != nullptr);
assert(uri != nullptr); assert(uri != nullptr);
BindAll(stmt, type, uri, name); BindAll(s, type, uri, name);
AtScopeExit(stmt) { AtScopeExit(s) {
sqlite3_reset(stmt); sqlite3_reset(s);
sqlite3_clear_bindings(stmt); sqlite3_clear_bindings(s);
}; };
bool modified = ExecuteModified(stmt); bool modified = ExecuteModified(s);
if (modified) if (modified)
idle_add(IDLE_STICKER); idle_add(IDLE_STICKER);
return modified; return modified;
} }
Sticker Sticker
sticker_load(const char *type, const char *uri) StickerDatabase::Load(const char *type, const char *uri)
{ {
Sticker s; Sticker s;
sticker_list_values(s.table, type, uri); ListValues(s.table, type, uri);
return s; return s;
} }
static sqlite3_stmt * sqlite3_stmt *
BindFind(const char *type, const char *base_uri, const char *name, StickerDatabase::BindFind(const char *type, const char *base_uri,
StickerOperator op, const char *value) const char *name,
StickerOperator op, const char *value)
{ {
assert(type != nullptr); assert(type != nullptr);
assert(name != nullptr); assert(name != nullptr);
@ -326,23 +307,23 @@ BindFind(const char *type, const char *base_uri, const char *name,
switch (op) { switch (op) {
case StickerOperator::EXISTS: case StickerOperator::EXISTS:
BindAll(sticker_stmt[STICKER_SQL_FIND], type, base_uri, name); BindAll(stmt[STICKER_SQL_FIND], type, base_uri, name);
return sticker_stmt[STICKER_SQL_FIND]; return stmt[STICKER_SQL_FIND];
case StickerOperator::EQUALS: case StickerOperator::EQUALS:
BindAll(sticker_stmt[STICKER_SQL_FIND_VALUE], BindAll(stmt[STICKER_SQL_FIND_VALUE],
type, base_uri, name, value); type, base_uri, name, value);
return sticker_stmt[STICKER_SQL_FIND_VALUE]; return stmt[STICKER_SQL_FIND_VALUE];
case StickerOperator::LESS_THAN: case StickerOperator::LESS_THAN:
BindAll(sticker_stmt[STICKER_SQL_FIND_LT], BindAll(stmt[STICKER_SQL_FIND_LT],
type, base_uri, name, value); type, base_uri, name, value);
return sticker_stmt[STICKER_SQL_FIND_LT]; return stmt[STICKER_SQL_FIND_LT];
case StickerOperator::GREATER_THAN: case StickerOperator::GREATER_THAN:
BindAll(sticker_stmt[STICKER_SQL_FIND_GT], BindAll(stmt[STICKER_SQL_FIND_GT],
type, base_uri, name, value); type, base_uri, name, value);
return sticker_stmt[STICKER_SQL_FIND_GT]; return stmt[STICKER_SQL_FIND_GT];
} }
assert(false); assert(false);
@ -350,26 +331,25 @@ BindFind(const char *type, const char *base_uri, const char *name,
} }
void void
sticker_find(const char *type, const char *base_uri, const char *name, StickerDatabase::Find(const char *type, const char *base_uri, const char *name,
StickerOperator op, const char *value, StickerOperator op, const char *value,
void (*func)(const char *uri, const char *value, void (*func)(const char *uri, const char *value,
void *user_data), void *user_data),
void *user_data) void *user_data)
{ {
assert(func != nullptr); assert(func != nullptr);
assert(sticker_enabled());
sqlite3_stmt *const stmt = BindFind(type, base_uri, name, op, value); sqlite3_stmt *const s = BindFind(type, base_uri, name, op, value);
assert(stmt != nullptr); assert(s != nullptr);
AtScopeExit(stmt) { AtScopeExit(s) {
sqlite3_reset(stmt); sqlite3_reset(s);
sqlite3_clear_bindings(stmt); sqlite3_clear_bindings(s);
}; };
ExecuteForEach(stmt, [stmt, func, user_data](){ ExecuteForEach(s, [s, func, user_data](){
func((const char*)sqlite3_column_text(stmt, 0), func((const char*)sqlite3_column_text(s, 0),
(const char*)sqlite3_column_text(stmt, 1), (const char*)sqlite3_column_text(s, 1),
user_data); user_data);
}); });
} }

View File

@ -45,96 +45,116 @@
#include "Match.hxx" #include "Match.hxx"
#include "util/Compiler.h" #include "util/Compiler.h"
#include <sqlite3.h>
#include <map>
#include <string> #include <string>
class Path; class Path;
struct Sticker; struct Sticker;
/** class StickerDatabase {
* Opens the sticker database. enum SQL {
* SQL_GET,
* Throws std::runtime_error on error. SQL_LIST,
*/ SQL_UPDATE,
void SQL_INSERT,
sticker_global_init(Path path); SQL_DELETE,
SQL_DELETE_VALUE,
SQL_FIND,
SQL_FIND_VALUE,
SQL_FIND_LT,
SQL_FIND_GT,
/** SQL_COUNT
* Close the sticker database. };
*/
void
sticker_global_finish() noexcept;
/** sqlite3 *db;
* Returns true if the sticker database is configured and available. sqlite3_stmt *stmt[SQL_COUNT];
*/
gcc_const
bool
sticker_enabled() noexcept;
/** public:
* Returns one value from an object's sticker record. Returns an /**
* empty string if the value doesn't exist. * Opens the sticker database.
* *
* Throws #SqliteError on error. * Throws on error.
*/ */
std::string StickerDatabase(Path path);
sticker_load_value(const char *type, const char *uri, const char *name); ~StickerDatabase() noexcept;
/** /**
* Sets a sticker value in the specified object. Overwrites existing * Returns one value from an object's sticker record. Returns an
* values. * empty string if the value doesn't exist.
* *
* Throws #SqliteError on error. * Throws #SqliteError on error.
*/ */
void std::string LoadValue(const char *type, const char *uri,
sticker_store_value(const char *type, const char *uri, const char *name);
const char *name, const char *value);
/** /**
* Deletes a sticker from the database. All sticker values of the * Sets a sticker value in the specified object. Overwrites existing
* specified object are deleted. * values.
* *
* Throws #SqliteError on error. * Throws #SqliteError on error.
*/ */
bool void StoreValue(const char *type, const char *uri,
sticker_delete(const char *type, const char *uri); const char *name, const char *value);
/** /**
* Deletes a sticker value. Fails if no sticker with this name * Deletes a sticker from the database. All sticker values of the
* exists. * specified object are deleted.
* *
* Throws #SqliteError on error. * Throws #SqliteError on error.
*/ */
bool bool Delete(const char *type, const char *uri);
sticker_delete_value(const char *type, const char *uri, const char *name);
/** /**
* Loads the sticker for the specified resource. * Deletes a sticker value. Fails if no sticker with this name
* * exists.
* Throws #SqliteError on error. *
* * Throws #SqliteError on error.
* @param type the resource type, e.g. "song" */
* @param uri the URI of the resource, e.g. the song path bool DeleteValue(const char *type, const char *uri, const char *name);
* @return a sticker object
*/
Sticker
sticker_load(const char *type, const char *uri);
/** /**
* Finds stickers with the specified name below the specified URI. * Loads the sticker for the specified resource.
* *
* @param type the resource type, e.g. "song" * Throws #SqliteError on error.
* @param base_uri the URI prefix of the resources, or nullptr if all *
* resources should be searched * @param type the resource type, e.g. "song"
* @param name the name of the sticker * @param uri the URI of the resource, e.g. the song path
* @param op the comparison operator * @return a sticker object
* @param value the operand */
*/ Sticker Load(const char *type, const char *uri);
void
sticker_find(const char *type, const char *base_uri, const char *name, /**
StickerOperator op, const char *value, * Finds stickers with the specified name below the specified URI.
void (*func)(const char *uri, const char *value, *
void *user_data), * @param type the resource type, e.g. "song"
void *user_data); * @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 #endif

View File

@ -29,44 +29,47 @@
#include <stdlib.h> #include <stdlib.h>
std::string 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(); const auto uri = song.GetURI();
return sticker_load_value("song", uri.c_str(), name); return db.LoadValue("song", uri.c_str(), name);
} }
void void
sticker_song_set_value(const LightSong &song, sticker_song_set_value(StickerDatabase &db,
const LightSong &song,
const char *name, const char *value) const char *name, const char *value)
{ {
const auto uri = song.GetURI(); const auto uri = song.GetURI();
sticker_store_value("song", uri.c_str(), name, value); db.StoreValue("song", uri.c_str(), name, value);
} }
bool 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 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 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(); const auto uri = song.GetURI();
return sticker_delete_value("song", uri.c_str(), name); return db.DeleteValue("song", uri.c_str(), name);
} }
Sticker Sticker
sticker_song_get(const LightSong &song) sticker_song_get(StickerDatabase &db, const LightSong &song)
{ {
const auto uri = song.GetURI(); const auto uri = song.GetURI();
return sticker_load("song", uri.c_str()); return db.Load("song", uri.c_str());
} }
namespace { namespace {
@ -101,7 +104,8 @@ sticker_song_find_cb(const char *uri, const char *value, void *user_data)
} }
void 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, StickerOperator op, const char *value,
void (*func)(const LightSong &song, const char *value, void (*func)(const LightSong &song, const char *value,
void *user_data), 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); data.base_uri_length = strlen(data.base_uri);
sticker_find("song", data.base_uri, name, op, value, sticker_database.Find("song", data.base_uri, name, op, value,
sticker_song_find_cb, &data); sticker_song_find_cb, &data);
} }

View File

@ -27,6 +27,7 @@
struct LightSong; struct LightSong;
struct Sticker; struct Sticker;
class Database; class Database;
class StickerDatabase;
/** /**
* Returns one value from a song's sticker record. * Returns one value from a song's sticker record.
@ -34,7 +35,8 @@ class Database;
* Throws #SqliteError on error. * Throws #SqliteError on error.
*/ */
std::string 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 * 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. * Throws #SqliteError on error.
*/ */
void void
sticker_song_set_value(const LightSong &song, sticker_song_set_value(StickerDatabase &db,
const LightSong &song,
const char *name, const char *value); const char *name, const char *value);
/** /**
@ -52,10 +55,10 @@ sticker_song_set_value(const LightSong &song,
* Throws #SqliteError on error. * Throws #SqliteError on error.
*/ */
bool bool
sticker_song_delete(const char *uri); sticker_song_delete(StickerDatabase &db, const char *uri);
bool 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 * 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. * Throws #SqliteError on error.
*/ */
bool 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. * 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 * @return a sticker object
*/ */
Sticker Sticker
sticker_song_get(const LightSong &song); sticker_song_get(StickerDatabase &db, const LightSong &song);
/** /**
* Finds stickers with the specified name below the specified * 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 * @param name the name of the sticker
*/ */
void 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, StickerOperator op, const char *value,
void (*func)(const LightSong &song, const char *value, void (*func)(const LightSong &song, const char *value,
void *user_data), void *user_data),