aboutsummaryrefslogtreecommitdiffstats
path: root/src/StickerDatabase.cxx
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2013-01-02 22:25:17 +0100
committerMax Kellermann <max@duempel.org>2013-01-02 22:25:17 +0100
commit9ceb8a717ae940972904ef83722f71c3ee124715 (patch)
tree31fc9fed3ca5934581455ddf603a3e3f78fc66d3 /src/StickerDatabase.cxx
parent8331de424a67b137cd83ce817da0fceec647dc2f (diff)
downloadmpd-9ceb8a717ae940972904ef83722f71c3ee124715.tar.gz
mpd-9ceb8a717ae940972904ef83722f71c3ee124715.tar.xz
mpd-9ceb8a717ae940972904ef83722f71c3ee124715.zip
sticker: convert to C++
Diffstat (limited to 'src/StickerDatabase.cxx')
-rw-r--r--src/StickerDatabase.cxx663
1 files changed, 663 insertions, 0 deletions
diff --git a/src/StickerDatabase.cxx b/src/StickerDatabase.cxx
new file mode 100644
index 000000000..9577ff734
--- /dev/null
+++ b/src/StickerDatabase.cxx
@@ -0,0 +1,663 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "StickerDatabase.hxx"
+
+extern "C" {
+#include "idle.h"
+}
+
+#include <glib.h>
+#include <sqlite3.h>
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "sticker"
+
+#if SQLITE_VERSION_NUMBER < 3003009
+#define sqlite3_prepare_v2 sqlite3_prepare
+#endif
+
+struct sticker {
+ GHashTable *table;
+};
+
+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,
+};
+
+static const char *const sticker_sql[] = {
+ //[STICKER_SQL_GET] =
+ "SELECT value FROM sticker WHERE type=? AND uri=? AND name=?",
+ //[STICKER_SQL_LIST] =
+ "SELECT name,value FROM sticker WHERE type=? AND uri=?",
+ //[STICKER_SQL_UPDATE] =
+ "UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?",
+ //[STICKER_SQL_INSERT] =
+ "INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)",
+ //[STICKER_SQL_DELETE] =
+ "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=?",
+};
+
+static const char sticker_sql_create[] =
+ "CREATE TABLE IF NOT EXISTS sticker("
+ " type VARCHAR NOT NULL, "
+ " uri VARCHAR NOT NULL, "
+ " name VARCHAR NOT NULL, "
+ " value VARCHAR NOT NULL"
+ ");"
+ "CREATE UNIQUE INDEX IF NOT EXISTS"
+ " sticker_value ON sticker(type, uri, name);"
+ "";
+
+static sqlite3 *sticker_db;
+static sqlite3_stmt *sticker_stmt[G_N_ELEMENTS(sticker_sql)];
+
+static GQuark
+sticker_quark(void)
+{
+ return g_quark_from_static_string("sticker");
+}
+
+static sqlite3_stmt *
+sticker_prepare(const char *sql, GError **error_r)
+{
+ int ret;
+ sqlite3_stmt *stmt;
+
+ ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ g_set_error(error_r, sticker_quark(), ret,
+ "sqlite3_prepare_v2() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return NULL;
+ }
+
+ return stmt;
+}
+
+bool
+sticker_global_init(const char *path, GError **error_r)
+{
+ int ret;
+
+ if (path == NULL)
+ /* not configured */
+ return true;
+
+ /* open/create the sqlite database */
+
+ ret = sqlite3_open(path, &sticker_db);
+ if (ret != SQLITE_OK) {
+ g_set_error(error_r, sticker_quark(), ret,
+ "Failed to open sqlite database '%s': %s",
+ path, sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ /* create the table and index */
+
+ ret = sqlite3_exec(sticker_db, sticker_sql_create, NULL, NULL, NULL);
+ if (ret != SQLITE_OK) {
+ g_set_error(error_r, sticker_quark(), ret,
+ "Failed to create sticker table: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ /* prepare the statements we're going to use */
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(sticker_sql); ++i) {
+ assert(sticker_sql[i] != NULL);
+
+ sticker_stmt[i] = sticker_prepare(sticker_sql[i], error_r);
+ if (sticker_stmt[i] == NULL)
+ return false;
+ }
+
+ return true;
+}
+
+void
+sticker_global_finish(void)
+{
+ if (sticker_db == NULL)
+ /* not configured */
+ return;
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(sticker_stmt); ++i) {
+ assert(sticker_stmt[i] != NULL);
+
+ sqlite3_finalize(sticker_stmt[i]);
+ }
+
+ sqlite3_close(sticker_db);
+}
+
+bool
+sticker_enabled(void)
+{
+ return sticker_db != NULL;
+}
+
+char *
+sticker_load_value(const char *type, const char *uri, const char *name)
+{
+ sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_GET];
+ int ret;
+ char *value;
+
+ assert(sticker_enabled());
+ assert(type != NULL);
+ assert(uri != NULL);
+ assert(name != NULL);
+
+ if (*name == 0)
+ return NULL;
+
+ sqlite3_reset(stmt);
+
+ ret = sqlite3_bind_text(stmt, 1, type, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return NULL;
+ }
+
+ ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return NULL;
+ }
+
+ ret = sqlite3_bind_text(stmt, 3, name, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return NULL;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ } while (ret == SQLITE_BUSY);
+
+ if (ret == SQLITE_ROW) {
+ /* record found */
+ value = g_strdup((const char*)sqlite3_column_text(stmt, 0));
+ } else if (ret == SQLITE_DONE) {
+ /* no record found */
+ value = NULL;
+ } else {
+ /* error */
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return NULL;
+ }
+
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+
+ return value;
+}
+
+static bool
+sticker_list_values(GHashTable *hash, const char *type, const char *uri)
+{
+ sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST];
+ int ret;
+ char *name, *value;
+
+ assert(hash != NULL);
+ assert(type != NULL);
+ assert(uri != NULL);
+ assert(sticker_enabled());
+
+ sqlite3_reset(stmt);
+
+ ret = sqlite3_bind_text(stmt, 1, type, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ switch (ret) {
+ case SQLITE_ROW:
+ name = g_strdup((const char*)sqlite3_column_text(stmt, 0));
+ value = g_strdup((const char*)sqlite3_column_text(stmt, 1));
+ g_hash_table_insert(hash, name, value);
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_BUSY:
+ /* no op */
+ break;
+ default:
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+ } while (ret != SQLITE_DONE);
+
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+
+ return true;
+}
+
+static bool
+sticker_update_value(const char *type, const char *uri,
+ const char *name, const char *value)
+{
+ sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_UPDATE];
+ int ret;
+
+ assert(type != NULL);
+ assert(uri != NULL);
+ assert(name != NULL);
+ assert(*name != 0);
+ assert(value != NULL);
+
+ assert(sticker_enabled());
+
+ sqlite3_reset(stmt);
+
+ ret = sqlite3_bind_text(stmt, 1, value, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 2, type, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 3, uri, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 4, name, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ } while (ret == SQLITE_BUSY);
+
+ if (ret != SQLITE_DONE) {
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_changes(sticker_db);
+
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+
+ idle_add(IDLE_STICKER);
+ return ret > 0;
+}
+
+static bool
+sticker_insert_value(const char *type, const char *uri,
+ const char *name, const char *value)
+{
+ sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_INSERT];
+ int ret;
+
+ assert(type != NULL);
+ assert(uri != NULL);
+ assert(name != NULL);
+ assert(*name != 0);
+ assert(value != NULL);
+
+ assert(sticker_enabled());
+
+ sqlite3_reset(stmt);
+
+ ret = sqlite3_bind_text(stmt, 1, type, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 3, name, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 4, value, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ } while (ret == SQLITE_BUSY);
+
+ if (ret != SQLITE_DONE) {
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+
+
+ idle_add(IDLE_STICKER);
+ return true;
+}
+
+bool
+sticker_store_value(const char *type, const char *uri,
+ const char *name, const char *value)
+{
+ assert(sticker_enabled());
+ assert(type != NULL);
+ assert(uri != NULL);
+ assert(name != NULL);
+ assert(value != NULL);
+
+ if (*name == 0)
+ return false;
+
+ return sticker_update_value(type, uri, name, value) ||
+ sticker_insert_value(type, uri, name, value);
+}
+
+bool
+sticker_delete(const char *type, const char *uri)
+{
+ sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE];
+ int ret;
+
+ assert(sticker_enabled());
+ assert(type != NULL);
+ assert(uri != NULL);
+
+ sqlite3_reset(stmt);
+
+ ret = sqlite3_bind_text(stmt, 1, type, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ } while (ret == SQLITE_BUSY);
+
+ if (ret != SQLITE_DONE) {
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+
+ idle_add(IDLE_STICKER);
+ return true;
+}
+
+bool
+sticker_delete_value(const char *type, const char *uri, const char *name)
+{
+ sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE_VALUE];
+ int ret;
+
+ assert(sticker_enabled());
+ assert(type != NULL);
+ assert(uri != NULL);
+
+ sqlite3_reset(stmt);
+
+ ret = sqlite3_bind_text(stmt, 1, type, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 2, uri, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 3, name, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ } while (ret == SQLITE_BUSY);
+
+ if (ret != SQLITE_DONE) {
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_changes(sticker_db);
+
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+
+ idle_add(IDLE_STICKER);
+ return ret > 0;
+}
+
+static struct sticker *
+sticker_new(void)
+{
+ struct sticker *sticker = g_new(struct sticker, 1);
+
+ sticker->table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, g_free);
+ return sticker;
+}
+
+void
+sticker_free(struct sticker *sticker)
+{
+ assert(sticker != NULL);
+ assert(sticker->table != NULL);
+
+ g_hash_table_destroy(sticker->table);
+ g_free(sticker);
+}
+
+const char *
+sticker_get_value(const struct sticker *sticker, const char *name)
+{
+ return (const char *)g_hash_table_lookup(sticker->table, name);
+}
+
+struct sticker_foreach_data {
+ void (*func)(const char *name, const char *value,
+ gpointer user_data);
+ gpointer user_data;
+};
+
+static void
+sticker_foreach_func(gpointer key, gpointer value, gpointer user_data)
+{
+ struct sticker_foreach_data *data =
+ (struct sticker_foreach_data *)user_data;
+
+ data->func((const char *)key, (const char *)value, data->user_data);
+}
+
+void
+sticker_foreach(const struct sticker *sticker,
+ void (*func)(const char *name, const char *value,
+ gpointer user_data),
+ gpointer user_data)
+{
+ struct sticker_foreach_data data;
+ data.func = func;
+ data.user_data = user_data;
+
+ g_hash_table_foreach(sticker->table, sticker_foreach_func, &data);
+}
+
+struct sticker *
+sticker_load(const char *type, const char *uri)
+{
+ struct sticker *sticker = sticker_new();
+ bool success;
+
+ success = sticker_list_values(sticker->table, type, uri);
+ if (!success) {
+ sticker_free(sticker);
+ return NULL;
+ }
+
+ if (g_hash_table_size(sticker->table) == 0) {
+ /* don't return empty sticker objects */
+ sticker_free(sticker);
+ return NULL;
+ }
+
+ return sticker;
+}
+
+bool
+sticker_find(const char *type, const char *base_uri, const char *name,
+ void (*func)(const char *uri, const char *value,
+ gpointer user_data),
+ gpointer user_data)
+{
+ sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_FIND];
+ int ret;
+
+ assert(type != NULL);
+ assert(name != NULL);
+ assert(func != NULL);
+ assert(sticker_enabled());
+
+ sqlite3_reset(stmt);
+
+ ret = sqlite3_bind_text(stmt, 1, type, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ if (base_uri == NULL)
+ base_uri = "";
+
+ ret = sqlite3_bind_text(stmt, 2, base_uri, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ ret = sqlite3_bind_text(stmt, 3, name, -1, NULL);
+ if (ret != SQLITE_OK) {
+ g_warning("sqlite3_bind_text() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ switch (ret) {
+ case SQLITE_ROW:
+ func((const char*)sqlite3_column_text(stmt, 0),
+ (const char*)sqlite3_column_text(stmt, 1),
+ user_data);
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_BUSY:
+ /* no op */
+ break;
+ default:
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+ } while (ret != SQLITE_DONE);
+
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+
+ return true;
+}