StickerDatabase.cxx 10.3 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13
 * 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.
14 15 16 17
 *
 * 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.
18 19
 */

20
#include "config.h"
Max Kellermann's avatar
Max Kellermann committed
21
#include "StickerDatabase.hxx"
22
#include "lib/sqlite/Domain.hxx"
23
#include "lib/sqlite/Util.hxx"
24
#include "fs/Path.hxx"
Max Kellermann's avatar
Max Kellermann committed
25
#include "Idle.hxx"
26
#include "util/Error.hxx"
27
#include "util/Macros.hxx"
28

29 30 31
#include <string>
#include <map>

32 33
#include <assert.h>

34
struct sticker {
35
	std::map<std::string, std::string> table;
36 37
};

38 39 40 41 42 43
enum sticker_sql {
	STICKER_SQL_GET,
	STICKER_SQL_LIST,
	STICKER_SQL_UPDATE,
	STICKER_SQL_INSERT,
	STICKER_SQL_DELETE,
44
	STICKER_SQL_DELETE_VALUE,
45
	STICKER_SQL_FIND,
46
	STICKER_SQL_FIND_VALUE,
47 48
	STICKER_SQL_FIND_LT,
	STICKER_SQL_FIND_GT,
49 50 51
};

static const char *const sticker_sql[] = {
Max Kellermann's avatar
Max Kellermann committed
52
	//[STICKER_SQL_GET] =
53
	"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?",
Max Kellermann's avatar
Max Kellermann committed
54
	//[STICKER_SQL_LIST] =
55
	"SELECT name,value FROM sticker WHERE type=? AND uri=?",
Max Kellermann's avatar
Max Kellermann committed
56
	//[STICKER_SQL_UPDATE] =
57
	"UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?",
Max Kellermann's avatar
Max Kellermann committed
58
	//[STICKER_SQL_INSERT] =
59
	"INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)",
Max Kellermann's avatar
Max Kellermann committed
60
	//[STICKER_SQL_DELETE] =
61
	"DELETE FROM sticker WHERE type=? AND uri=?",
Max Kellermann's avatar
Max Kellermann committed
62
	//[STICKER_SQL_DELETE_VALUE] =
63
	"DELETE FROM sticker WHERE type=? AND uri=? AND name=?",
Max Kellermann's avatar
Max Kellermann committed
64
	//[STICKER_SQL_FIND] =
65
	"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",
66 67 68

	//[STICKER_SQL_FIND_VALUE] =
	"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?",
69 70 71 72 73 74

	//[STICKER_SQL_FIND_LT] =
	"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value<?",

	//[STICKER_SQL_FIND_GT] =
	"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value>?",
75 76
};

77 78 79 80 81 82 83 84 85 86 87 88
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;
89
static sqlite3_stmt *sticker_stmt[ARRAY_SIZE(sticker_sql)];
90 91

static sqlite3_stmt *
92
sticker_prepare(const char *sql, Error &error)
93 94
{
	sqlite3_stmt *stmt;
95
	int ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, nullptr);
96
	if (ret != SQLITE_OK) {
97
		error.Format(sqlite_domain, ret,
98 99
			     "sqlite3_prepare_v2() failed: %s",
			     sqlite3_errmsg(sticker_db));
100
		return nullptr;
101
	}
102 103 104 105

	return stmt;
}

106
bool
107
sticker_global_init(Path path, Error &error)
108
{
109
	assert(!path.IsNull());
110

111
	int ret;
112 113 114

	/* open/create the sqlite database */

115
	ret = sqlite3_open(path.c_str(), &sticker_db);
116
	if (ret != SQLITE_OK) {
117
		const std::string utf8 = path.ToUTF8();
118 119 120
		error.Format(sqlite_domain, ret,
			     "Failed to open sqlite database '%s': %s",
			     utf8.c_str(), sqlite3_errmsg(sticker_db));
121 122
		return false;
	}
123 124 125

	/* create the table and index */

126 127
	ret = sqlite3_exec(sticker_db, sticker_sql_create,
			   nullptr, nullptr, nullptr);
128
	if (ret != SQLITE_OK) {
129
		error.Format(sqlite_domain, ret,
130 131
			     "Failed to create sticker table: %s",
			     sqlite3_errmsg(sticker_db));
132 133
		return false;
	}
134 135 136

	/* prepare the statements we're going to use */

137
	for (unsigned i = 0; i < ARRAY_SIZE(sticker_sql); ++i) {
138
		assert(sticker_sql[i] != nullptr);
139

140
		sticker_stmt[i] = sticker_prepare(sticker_sql[i], error);
141
		if (sticker_stmt[i] == nullptr)
142
			return false;
143
	}
144 145

	return true;
146 147 148
}

void
149
sticker_global_finish()
150
{
151
	if (sticker_db == nullptr)
152 153 154
		/* not configured */
		return;

155
	for (unsigned i = 0; i < ARRAY_SIZE(sticker_stmt); ++i) {
156
		assert(sticker_stmt[i] != nullptr);
157 158 159 160

		sqlite3_finalize(sticker_stmt[i]);
	}

161 162 163 164
	sqlite3_close(sticker_db);
}

bool
165
sticker_enabled()
166
{
167
	return sticker_db != nullptr;
168 169
}

170
std::string
171 172
sticker_load_value(const char *type, const char *uri, const char *name,
		   Error &error)
173
{
174
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_GET];
175 176

	assert(sticker_enabled());
177 178 179
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
180 181

	if (*name == 0)
182
		return std::string();
183

184
	if (!BindAll(error, stmt, type, uri, name))
185
		return std::string();
186

187
	std::string value;
188
	if (ExecuteRow(stmt, error))
189
		value = (const char*)sqlite3_column_text(stmt, 0);
190

191 192
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
193 194 195 196

	return value;
}

197
static bool
198
sticker_list_values(std::map<std::string, std::string> &table,
199 200
		    const char *type, const char *uri,
		    Error &error)
201
{
202
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST];
203

204 205
	assert(type != nullptr);
	assert(uri != nullptr);
206 207
	assert(sticker_enabled());

208
	if (!BindAll(error, stmt, type, uri))
209
		return false;
210

211
	const bool success = ExecuteForEach(stmt, error, [stmt, &table](){
212 213
			const char *name = (const char *)sqlite3_column_text(stmt, 0);
			const char *value = (const char *)sqlite3_column_text(stmt, 1);
214
			table.insert(std::make_pair(name, value));
215
		});
216

217 218
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
219

220
	return success;
221 222
}

223 224
static bool
sticker_update_value(const char *type, const char *uri,
225 226
		     const char *name, const char *value,
		     Error &error)
227
{
228
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_UPDATE];
229

230 231 232
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
233
	assert(*name != 0);
234
	assert(value != nullptr);
235 236 237

	assert(sticker_enabled());

238
	if (!BindAll(error, stmt, value, type, uri, name))
239 240
		return false;

241
	bool modified = ExecuteModified(stmt, error);
242

243 244
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
245

246 247 248
	if (modified)
		idle_add(IDLE_STICKER);
	return modified;
249 250 251 252
}

static bool
sticker_insert_value(const char *type, const char *uri,
253 254
		     const char *name, const char *value,
		     Error &error)
255
{
256
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_INSERT];
257

258 259 260
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
261
	assert(*name != 0);
262
	assert(value != nullptr);
263 264 265

	assert(sticker_enabled());

266
	if (!BindAll(error, stmt, type, uri, name, value))
267 268
		return false;

269
	bool success = ExecuteCommand(stmt, error);
270

271 272
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
273

274 275 276
	if (success)
		idle_add(IDLE_STICKER);
	return success;
277 278 279 280
}

bool
sticker_store_value(const char *type, const char *uri,
281 282
		    const char *name, const char *value,
		    Error &error)
283 284
{
	assert(sticker_enabled());
285 286 287 288
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
	assert(value != nullptr);
289 290 291 292

	if (*name == 0)
		return false;

293 294
	return sticker_update_value(type, uri, name, value, error) ||
		sticker_insert_value(type, uri, name, value, error);
295 296 297
}

bool
298
sticker_delete(const char *type, const char *uri, Error &error)
299
{
300
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE];
301 302

	assert(sticker_enabled());
303 304
	assert(type != nullptr);
	assert(uri != nullptr);
305

306
	if (!BindAll(error, stmt, type, uri))
307 308
		return false;

309
	bool modified = ExecuteModified(stmt, error);
310

311 312
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
313

314 315 316
	if (modified)
		idle_add(IDLE_STICKER);
	return modified;
317
}
318

319
bool
320 321
sticker_delete_value(const char *type, const char *uri, const char *name,
		     Error &error)
322 323 324 325
{
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE_VALUE];

	assert(sticker_enabled());
326 327
	assert(type != nullptr);
	assert(uri != nullptr);
328

329
	if (!BindAll(error, stmt, type, uri, name))
330 331
		return false;

332
	bool modified = ExecuteModified(stmt, error);
333 334 335 336

	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);

337 338 339
	if (modified)
		idle_add(IDLE_STICKER);
	return modified;
340 341
}

342 343 344
void
sticker_free(struct sticker *sticker)
{
345
	delete sticker;
346 347 348
}

const char *
349
sticker_get_value(const struct sticker &sticker, const char *name)
350
{
351 352
	auto i = sticker.table.find(name);
	if (i == sticker.table.end())
353
		return nullptr;
354

355
	return i->second.c_str();
356 357 358
}

void
359
sticker_foreach(const sticker &sticker,
360
		void (*func)(const char *name, const char *value,
361 362
			     void *user_data),
		void *user_data)
363
{
364
	for (const auto &i : sticker.table)
365
		func(i.first.c_str(), i.second.c_str(), user_data);
366 367 368
}

struct sticker *
369
sticker_load(const char *type, const char *uri, Error &error)
370
{
371
	sticker s;
372

373
	if (!sticker_list_values(s.table, type, uri, error))
374
		return nullptr;
375

376
	if (s.table.empty())
377
		/* don't return empty sticker objects */
378
		return nullptr;
379

380
	return new sticker(std::move(s));
381
}
382

383 384
static sqlite3_stmt *
BindFind(const char *type, const char *base_uri, const char *name,
385
	 StickerOperator op, const char *value,
386 387 388 389 390 391 392 393
	 Error &error)
{
	assert(type != nullptr);
	assert(name != nullptr);

	if (base_uri == nullptr)
		base_uri = "";

394 395 396 397 398
	switch (op) {
	case StickerOperator::EXISTS:
		return BindAllOrNull(error, sticker_stmt[STICKER_SQL_FIND],
				     type, base_uri, name);

399 400 401 402
	case StickerOperator::EQUALS:
		return BindAllOrNull(error,
				     sticker_stmt[STICKER_SQL_FIND_VALUE],
				     type, base_uri, name, value);
403 404 405 406 407 408 409 410 411 412

	case StickerOperator::LESS_THAN:
		return BindAllOrNull(error,
				     sticker_stmt[STICKER_SQL_FIND_LT],
				     type, base_uri, name, value);

	case StickerOperator::GREATER_THAN:
		return BindAllOrNull(error,
				     sticker_stmt[STICKER_SQL_FIND_GT],
				     type, base_uri, name, value);
413
	}
414 415 416

	assert(false);
	gcc_unreachable();
417 418
}

419 420
bool
sticker_find(const char *type, const char *base_uri, const char *name,
421
	     StickerOperator op, const char *value,
422
	     void (*func)(const char *uri, const char *value,
423
			  void *user_data),
424 425
	     void *user_data,
	     Error &error)
426
{
427
	assert(func != nullptr);
428 429
	assert(sticker_enabled());

430 431
	sqlite3_stmt *const stmt = BindFind(type, base_uri, name, op, value,
					    error);
432
	if (stmt == nullptr)
433 434
		return false;

435 436
	const bool success = ExecuteForEach(stmt, error,
					    [stmt, func, user_data](){
437 438 439
			func((const char*)sqlite3_column_text(stmt, 0),
			     (const char*)sqlite3_column_text(stmt, 1),
			     user_data);
440
		});
441 442 443 444

	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);

445
	return success;
446
}