StickerDatabase.cxx 13.1 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2013 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 "fs/Path.hxx"
Max Kellermann's avatar
Max Kellermann committed
23
#include "Idle.hxx"
24 25
#include "util/Error.hxx"
#include "util/Domain.hxx"
26
#include "util/Macros.hxx"
27
#include "Log.hxx"
28

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

32 33 34
#include <sqlite3.h>
#include <assert.h>

35 36 37 38
#if SQLITE_VERSION_NUMBER < 3003009
#define sqlite3_prepare_v2 sqlite3_prepare
#endif

39
struct sticker {
40
	std::map<std::string, std::string> table;
41 42
};

43 44 45 46 47 48
enum sticker_sql {
	STICKER_SQL_GET,
	STICKER_SQL_LIST,
	STICKER_SQL_UPDATE,
	STICKER_SQL_INSERT,
	STICKER_SQL_DELETE,
49
	STICKER_SQL_DELETE_VALUE,
50
	STICKER_SQL_FIND,
51 52 53
};

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

70 71 72 73 74 75 76 77 78 79 80 81
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;
82
static sqlite3_stmt *sticker_stmt[ARRAY_SIZE(sticker_sql)];
83

84
static constexpr Domain sticker_domain("sticker");
85

86 87 88 89 90 91
static void
LogError(sqlite3 *db, const char *msg)
{
	FormatError(sticker_domain, "%s: %s", msg, sqlite3_errmsg(db));
}

92
static sqlite3_stmt *
93
sticker_prepare(const char *sql, Error &error)
94 95 96 97
{
	int ret;
	sqlite3_stmt *stmt;

98
	ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, nullptr);
99
	if (ret != SQLITE_OK) {
100 101 102
		error.Format(sticker_domain, ret,
			     "sqlite3_prepare_v2() failed: %s",
			     sqlite3_errmsg(sticker_db));
103
		return nullptr;
104
	}
105 106 107 108

	return stmt;
}

109
bool
110
sticker_global_init(Path path, Error &error)
111
{
112
	assert(!path.IsNull());
113

114
	int ret;
115 116 117

	/* open/create the sqlite database */

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

	/* create the table and index */

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

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

140
	for (unsigned i = 0; i < ARRAY_SIZE(sticker_sql); ++i) {
141
		assert(sticker_sql[i] != nullptr);
142

143
		sticker_stmt[i] = sticker_prepare(sticker_sql[i], error);
144
		if (sticker_stmt[i] == nullptr)
145
			return false;
146
	}
147 148

	return true;
149 150 151 152 153
}

void
sticker_global_finish(void)
{
154
	if (sticker_db == nullptr)
155 156 157
		/* not configured */
		return;

158
	for (unsigned i = 0; i < ARRAY_SIZE(sticker_stmt); ++i) {
159
		assert(sticker_stmt[i] != nullptr);
160 161 162 163

		sqlite3_finalize(sticker_stmt[i]);
	}

164 165 166 167 168 169
	sqlite3_close(sticker_db);
}

bool
sticker_enabled(void)
{
170
	return sticker_db != nullptr;
171 172
}

173
std::string
174 175
sticker_load_value(const char *type, const char *uri, const char *name)
{
176
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_GET];
177 178 179
	int ret;

	assert(sticker_enabled());
180 181 182
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
183 184

	if (*name == 0)
185
		return std::string();
186

187
	sqlite3_reset(stmt);
188

189
	ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr);
190
	if (ret != SQLITE_OK) {
191
		LogError(sticker_db, "sqlite3_bind_text() failed");
192
		return std::string();
193 194
	}

195
	ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr);
196
	if (ret != SQLITE_OK) {
197
		LogError(sticker_db, "sqlite3_bind_text() failed");
198
		return std::string();
199 200
	}

201
	ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr);
202
	if (ret != SQLITE_OK) {
203
		LogError(sticker_db, "sqlite3_bind_text() failed");
204
		return std::string();
205 206 207
	}

	do {
208
		ret = sqlite3_step(stmt);
209 210
	} while (ret == SQLITE_BUSY);

211
	std::string value;
212 213
	if (ret == SQLITE_ROW) {
		/* record found */
214
		value = (const char*)sqlite3_column_text(stmt, 0);
215 216 217 218
	} else if (ret == SQLITE_DONE) {
		/* no record found */
	} else {
		/* error */
219
		LogError(sticker_db, "sqlite3_step() failed");
220 221
	}

222 223
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
224 225 226 227

	return value;
}

228
static bool
229 230
sticker_list_values(std::map<std::string, std::string> &table,
		    const char *type, const char *uri)
231
{
232
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST];
233 234
	int ret;

235 236
	assert(type != nullptr);
	assert(uri != nullptr);
237 238
	assert(sticker_enabled());

239
	sqlite3_reset(stmt);
240

241
	ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr);
242
	if (ret != SQLITE_OK) {
243
		LogError(sticker_db, "sqlite3_bind_text() failed");
244
		return false;
245 246
	}

247
	ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr);
248
	if (ret != SQLITE_OK) {
249
		LogError(sticker_db, "sqlite3_bind_text() failed");
250
		return false;
251 252 253
	}

	do {
254
		ret = sqlite3_step(stmt);
255
		switch (ret) {
256 257
			const char *name, *value;

258
		case SQLITE_ROW:
259 260 261 262
			name = (const char*)sqlite3_column_text(stmt, 0);
			value = (const char*)sqlite3_column_text(stmt, 1);

			table.insert(std::make_pair(name, value));
263 264 265 266 267 268 269
			break;
		case SQLITE_DONE:
			break;
		case SQLITE_BUSY:
			/* no op */
			break;
		default:
270
			LogError(sticker_db, "sqlite3_step() failed");
271
			return false;
272 273 274
		}
	} while (ret != SQLITE_DONE);

275 276
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
277

278
	return true;
279 280
}

281 282 283 284
static bool
sticker_update_value(const char *type, const char *uri,
		     const char *name, const char *value)
{
285
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_UPDATE];
286 287
	int ret;

288 289 290
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
291
	assert(*name != 0);
292
	assert(value != nullptr);
293 294 295

	assert(sticker_enabled());

296
	sqlite3_reset(stmt);
297

298
	ret = sqlite3_bind_text(stmt, 1, value, -1, nullptr);
299
	if (ret != SQLITE_OK) {
300
		LogError(sticker_db, "sqlite3_bind_text() failed");
301 302 303
		return false;
	}

304
	ret = sqlite3_bind_text(stmt, 2, type, -1, nullptr);
305
	if (ret != SQLITE_OK) {
306
		LogError(sticker_db, "sqlite3_bind_text() failed");
307 308 309
		return false;
	}

310
	ret = sqlite3_bind_text(stmt, 3, uri, -1, nullptr);
311
	if (ret != SQLITE_OK) {
312
		LogError(sticker_db, "sqlite3_bind_text() failed");
313 314 315
		return false;
	}

316
	ret = sqlite3_bind_text(stmt, 4, name, -1, nullptr);
317
	if (ret != SQLITE_OK) {
318
		LogError(sticker_db, "sqlite3_bind_text() failed");
319 320 321 322
		return false;
	}

	do {
323
		ret = sqlite3_step(stmt);
324 325 326
	} while (ret == SQLITE_BUSY);

	if (ret != SQLITE_DONE) {
327
		LogError(sticker_db, "sqlite3_step() failed");
328 329 330 331 332
		return false;
	}

	ret = sqlite3_changes(sticker_db);

333 334
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
335

336
	idle_add(IDLE_STICKER);
337 338 339 340 341 342 343
	return ret > 0;
}

static bool
sticker_insert_value(const char *type, const char *uri,
		     const char *name, const char *value)
{
344
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_INSERT];
345 346
	int ret;

347 348 349
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
350
	assert(*name != 0);
351
	assert(value != nullptr);
352 353 354

	assert(sticker_enabled());

355
	sqlite3_reset(stmt);
356

357
	ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr);
358
	if (ret != SQLITE_OK) {
359
		LogError(sticker_db, "sqlite3_bind_text() failed");
360 361 362
		return false;
	}

363
	ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr);
364
	if (ret != SQLITE_OK) {
365
		LogError(sticker_db, "sqlite3_bind_text() failed");
366 367 368
		return false;
	}

369
	ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr);
370
	if (ret != SQLITE_OK) {
371
		LogError(sticker_db, "sqlite3_bind_text() failed");
372 373 374
		return false;
	}

375
	ret = sqlite3_bind_text(stmt, 4, value, -1, nullptr);
376
	if (ret != SQLITE_OK) {
377
		LogError(sticker_db, "sqlite3_bind_text() failed");
378 379 380 381
		return false;
	}

	do {
382
		ret = sqlite3_step(stmt);
383 384 385
	} while (ret == SQLITE_BUSY);

	if (ret != SQLITE_DONE) {
386
		LogError(sticker_db, "sqlite3_step() failed");
387 388 389
		return false;
	}

390 391
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
392

393 394

	idle_add(IDLE_STICKER);
395 396 397 398 399 400 401 402
	return true;
}

bool
sticker_store_value(const char *type, const char *uri,
		    const char *name, const char *value)
{
	assert(sticker_enabled());
403 404 405 406
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
	assert(value != nullptr);
407 408 409 410 411 412 413 414 415 416 417

	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)
{
418
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE];
419 420 421
	int ret;

	assert(sticker_enabled());
422 423
	assert(type != nullptr);
	assert(uri != nullptr);
424

425
	sqlite3_reset(stmt);
426

427
	ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr);
428
	if (ret != SQLITE_OK) {
429
		LogError(sticker_db, "sqlite3_bind_text() failed");
430 431 432
		return false;
	}

433
	ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr);
434
	if (ret != SQLITE_OK) {
435
		LogError(sticker_db, "sqlite3_bind_text() failed");
436 437 438 439
		return false;
	}

	do {
440
		ret = sqlite3_step(stmt);
441 442 443
	} while (ret == SQLITE_BUSY);

	if (ret != SQLITE_DONE) {
444
		LogError(sticker_db, "sqlite3_step() failed");
445 446 447
		return false;
	}

448 449
	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);
450

451
	idle_add(IDLE_STICKER);
452 453
	return true;
}
454

455 456 457 458 459 460 461
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());
462 463
	assert(type != nullptr);
	assert(uri != nullptr);
464 465 466

	sqlite3_reset(stmt);

467
	ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr);
468
	if (ret != SQLITE_OK) {
469
		LogError(sticker_db, "sqlite3_bind_text() failed");
470 471 472
		return false;
	}

473
	ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr);
474
	if (ret != SQLITE_OK) {
475
		LogError(sticker_db, "sqlite3_bind_text() failed");
476 477 478
		return false;
	}

479
	ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr);
480
	if (ret != SQLITE_OK) {
481
		LogError(sticker_db, "sqlite3_bind_text() failed");
482 483 484 485 486 487 488 489
		return false;
	}

	do {
		ret = sqlite3_step(stmt);
	} while (ret == SQLITE_BUSY);

	if (ret != SQLITE_DONE) {
490
		LogError(sticker_db, "sqlite3_step() failed");
491 492 493 494 495 496 497 498 499 500 501 502
		return false;
	}

	ret = sqlite3_changes(sticker_db);

	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);

	idle_add(IDLE_STICKER);
	return ret > 0;
}

503 504 505
void
sticker_free(struct sticker *sticker)
{
506
	delete sticker;
507 508 509
}

const char *
510
sticker_get_value(const struct sticker &sticker, const char *name)
511
{
512 513
	auto i = sticker.table.find(name);
	if (i == sticker.table.end())
514
		return nullptr;
515

516
	return i->second.c_str();
517 518 519
}

void
520
sticker_foreach(const sticker &sticker,
521
		void (*func)(const char *name, const char *value,
522 523
			     void *user_data),
		void *user_data)
524
{
525
	for (const auto &i : sticker.table)
526
		func(i.first.c_str(), i.second.c_str(), user_data);
527 528 529 530 531
}

struct sticker *
sticker_load(const char *type, const char *uri)
{
532
	sticker s;
533

534
	if (!sticker_list_values(s.table, type, uri))
535
		return nullptr;
536

537
	if (s.table.empty())
538
		/* don't return empty sticker objects */
539
		return nullptr;
540

541
	return new sticker(std::move(s));
542
}
543 544 545 546

bool
sticker_find(const char *type, const char *base_uri, const char *name,
	     void (*func)(const char *uri, const char *value,
547 548
			  void *user_data),
	     void *user_data)
549 550 551 552
{
	sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_FIND];
	int ret;

553 554 555
	assert(type != nullptr);
	assert(name != nullptr);
	assert(func != nullptr);
556 557 558 559
	assert(sticker_enabled());

	sqlite3_reset(stmt);

560
	ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr);
561
	if (ret != SQLITE_OK) {
562
		LogError(sticker_db, "sqlite3_bind_text() failed");
563 564 565
		return false;
	}

566
	if (base_uri == nullptr)
567 568
		base_uri = "";

569
	ret = sqlite3_bind_text(stmt, 2, base_uri, -1, nullptr);
570
	if (ret != SQLITE_OK) {
571
		LogError(sticker_db, "sqlite3_bind_text() failed");
572 573 574
		return false;
	}

575
	ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr);
576
	if (ret != SQLITE_OK) {
577
		LogError(sticker_db, "sqlite3_bind_text() failed");
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
		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:
595
			LogError(sticker_db, "sqlite3_step() failed");
596 597 598 599 600 601 602 603 604
			return false;
		}
	} while (ret != SQLITE_DONE);

	sqlite3_reset(stmt);
	sqlite3_clear_bindings(stmt);

	return true;
}