aboutsummaryrefslogtreecommitdiffstats
path: root/src/sticker.c
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2009-01-19 18:51:57 +0100
committerMax Kellermann <max@duempel.org>2009-01-19 18:51:57 +0100
commit145ab84d51f262983412143fbb29487a8647adb3 (patch)
tree8fc58be92be2178c86484caca17fae23535e8fad /src/sticker.c
parentfbed96dcea00db7c2f5044929caebfc06684ea15 (diff)
downloadmpd-145ab84d51f262983412143fbb29487a8647adb3.tar.gz
mpd-145ab84d51f262983412143fbb29487a8647adb3.tar.xz
mpd-145ab84d51f262983412143fbb29487a8647adb3.zip
sticker: new library for storing dynamic information about songs
"Stickers" are pieces of information attached to existing MPD objects (e.g. song files, directories, albums). Clients can create arbitrary name/value pairs. MPD itself does not assume any special meaning in them.
Diffstat (limited to 'src/sticker.c')
-rw-r--r--src/sticker.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/src/sticker.c b/src/sticker.c
new file mode 100644
index 000000000..e596fd97c
--- /dev/null
+++ b/src/sticker.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sticker.h"
+
+#include <glib.h>
+#include <sqlite3.h>
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "sticker"
+
+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 const char sticker_sql_get[] =
+ "SELECT value FROM sticker WHERE type=? AND uri=? AND name=?";
+
+static const char sticker_sql_update[] =
+ "UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?";
+
+static const char sticker_sql_insert[] =
+ "INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)";
+
+static const char sticker_sql_delete[] =
+ "DELETE FROM sticker WHERE type=? AND uri=?";
+
+static sqlite3 *sticker_db;
+static sqlite3_stmt *sticker_stmt_get, *sticker_stmt_update,
+ *sticker_stmt_insert, *sticker_stmt_delete;
+
+static sqlite3_stmt *
+sticker_prepare(const char *sql)
+{
+ int ret;
+ sqlite3_stmt *stmt;
+
+ ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, NULL);
+ if (ret != SQLITE_OK)
+ g_error("sqlite3_prepare_v2() failed: %s",
+ sqlite3_errmsg(sticker_db));
+
+ return stmt;
+}
+
+void
+sticker_global_init(const char *path)
+{
+ int ret;
+
+ if (path == NULL)
+ /* not configured */
+ return;
+
+ /* open/create the sqlite database */
+
+ ret = sqlite3_open(path, &sticker_db);
+ if (ret != SQLITE_OK)
+ g_error("Failed to open sqlite database '%s': %s",
+ path, sqlite3_errmsg(sticker_db));
+
+ /* create the table and index */
+
+ ret = sqlite3_exec(sticker_db, sticker_sql_create, NULL, NULL, NULL);
+ if (ret != SQLITE_OK)
+ g_error("Failed to create sticker table: %s",
+ sqlite3_errmsg(sticker_db));
+
+ /* prepare the statements we're going to use */
+
+ sticker_stmt_get = sticker_prepare(sticker_sql_get);
+ sticker_stmt_update = sticker_prepare(sticker_sql_update);
+ sticker_stmt_insert = sticker_prepare(sticker_sql_insert);
+ sticker_stmt_delete = sticker_prepare(sticker_sql_delete);
+
+ if (sticker_stmt_get == NULL || sticker_stmt_update == NULL ||
+ sticker_stmt_insert == NULL || sticker_stmt_delete == NULL)
+ g_error("Failed to prepare sqlite statements");
+}
+
+void
+sticker_global_finish(void)
+{
+ if (sticker_db == NULL)
+ /* not configured */
+ return;
+
+ sqlite3_finalize(sticker_stmt_delete);
+ sqlite3_finalize(sticker_stmt_update);
+ sqlite3_finalize(sticker_stmt_insert);
+ 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)
+{
+ int ret;
+ char *value;
+
+ assert(sticker_enabled());
+ assert(type != NULL);
+ assert(uri != NULL);
+ assert(name != NULL);
+
+ if (*name == 0)
+ return NULL;
+
+ sqlite3_reset(sticker_stmt_get);
+
+ ret = sqlite3_bind_text(sticker_stmt_get, 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(sticker_stmt_get, 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(sticker_stmt_get, 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(sticker_stmt_get);
+ } while (ret == SQLITE_BUSY);
+
+ if (ret == SQLITE_ROW) {
+ /* record found */
+ value = g_strdup((const char*)sqlite3_column_text(sticker_stmt_get, 0));
+ } else if (ret == SQLITE_DONE) {
+ /* no record found */
+ value = NULL;
+ } else {
+ /* error */
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ sqlite3_reset(sticker_stmt_get);
+ sqlite3_clear_bindings(sticker_stmt_get);
+
+ return value;
+}
+
+static bool
+sticker_update_value(const char *type, const char *uri,
+ const char *name, const char *value)
+{
+ int ret;
+
+ assert(type != NULL);
+ assert(uri != NULL);
+ assert(name != NULL);
+ assert(*name != 0);
+ assert(value != NULL);
+
+ assert(sticker_enabled());
+
+ sqlite3_reset(sticker_stmt_update);
+
+ ret = sqlite3_bind_text(sticker_stmt_update, 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(sticker_stmt_update, 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(sticker_stmt_update, 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(sticker_stmt_update, 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(sticker_stmt_update);
+ } 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(sticker_stmt_update);
+ sqlite3_clear_bindings(sticker_stmt_update);
+
+ return ret > 0;
+}
+
+static bool
+sticker_insert_value(const char *type, const char *uri,
+ const char *name, const char *value)
+{
+ int ret;
+
+ assert(type != NULL);
+ assert(uri != NULL);
+ assert(name != NULL);
+ assert(*name != 0);
+ assert(value != NULL);
+
+ assert(sticker_enabled());
+
+ sqlite3_reset(sticker_stmt_insert);
+
+ ret = sqlite3_bind_text(sticker_stmt_insert, 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(sticker_stmt_insert, 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(sticker_stmt_insert, 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(sticker_stmt_insert, 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(sticker_stmt_insert);
+ } while (ret == SQLITE_BUSY);
+
+ if (ret != SQLITE_DONE) {
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ sqlite3_reset(sticker_stmt_insert);
+ sqlite3_clear_bindings(sticker_stmt_insert);
+
+ 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)
+{
+ int ret;
+
+ assert(sticker_enabled());
+ assert(type != NULL);
+ assert(uri != NULL);
+
+ sqlite3_reset(sticker_stmt_delete);
+
+ ret = sqlite3_bind_text(sticker_stmt_delete, 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(sticker_stmt_delete, 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(sticker_stmt_delete);
+ } while (ret == SQLITE_BUSY);
+
+ if (ret != SQLITE_DONE) {
+ g_warning("sqlite3_step() failed: %s",
+ sqlite3_errmsg(sticker_db));
+ return false;
+ }
+
+ sqlite3_reset(sticker_stmt_delete);
+ sqlite3_clear_bindings(sticker_stmt_delete);
+
+ return true;
+}