diff options
author | Max Kellermann <max@duempel.org> | 2009-01-19 18:51:57 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2009-01-19 18:51:57 +0100 |
commit | 145ab84d51f262983412143fbb29487a8647adb3 (patch) | |
tree | 8fc58be92be2178c86484caca17fae23535e8fad /src/sticker.c | |
parent | fbed96dcea00db7c2f5044929caebfc06684ea15 (diff) | |
download | mpd-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.c | 361 |
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; +} |