Database.cxx 7.82 KB
Newer Older
1
/*
2
 * Copyright 2003-2019 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 "Database.hxx"
21
#include "Sticker.hxx"
22
#include "lib/sqlite/Util.hxx"
23
#include "fs/Path.hxx"
Max Kellermann's avatar
Max Kellermann committed
24
#include "Idle.hxx"
25
#include "util/Macros.hxx"
26
#include "util/StringCompare.hxx"
27
#include "util/ScopeExit.hxx"
28 29 30

#include <assert.h>

31 32
using namespace Sqlite;

33 34 35 36 37 38
enum sticker_sql {
	STICKER_SQL_GET,
	STICKER_SQL_LIST,
	STICKER_SQL_UPDATE,
	STICKER_SQL_INSERT,
	STICKER_SQL_DELETE,
39
	STICKER_SQL_DELETE_VALUE,
40
	STICKER_SQL_FIND,
41
	STICKER_SQL_FIND_VALUE,
42 43
	STICKER_SQL_FIND_LT,
	STICKER_SQL_FIND_GT,
44
	STICKER_SQL_COUNT
45 46 47
};

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

	//[STICKER_SQL_FIND_VALUE] =
	"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?",
65 66 67 68 69 70

	//[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>?",
71 72
};

73 74 75 76 77 78 79 80 81 82 83
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);"
	"";

84
StickerDatabase::StickerDatabase(Path path)
85
	:db(path.c_str())
86
{
87
	assert(!path.IsNull());
88

89
	int ret;
90 91 92

	/* create the table and index */

93
	ret = sqlite3_exec(db, sticker_sql_create,
94
			   nullptr, nullptr, nullptr);
95
	if (ret != SQLITE_OK)
96
		throw SqliteError(db, ret,
97
				  "Failed to create sticker table");
98 99 100

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

101
	for (unsigned i = 0; i < ARRAY_SIZE(sticker_sql); ++i) {
102
		assert(sticker_sql[i] != nullptr);
103

104
		stmt[i] = Prepare(db, sticker_sql[i]);
105
	}
106 107
}

108
StickerDatabase::~StickerDatabase() noexcept
109
{
110
	assert(db != nullptr);
111

112 113
	for (unsigned i = 0; i < ARRAY_SIZE(stmt); ++i) {
		assert(stmt[i] != nullptr);
114

115
		sqlite3_finalize(stmt[i]);
116
	}
117 118
}

119
std::string
120
StickerDatabase::LoadValue(const char *type, const char *uri, const char *name)
121
{
122
	sqlite3_stmt *const s = stmt[STICKER_SQL_GET];
123

124 125 126
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
127

128
	if (StringIsEmpty(name))
129
		return std::string();
130

131
	BindAll(s, type, uri, name);
132

133 134 135
	AtScopeExit(s) {
		sqlite3_reset(s);
		sqlite3_clear_bindings(s);
136
	};
137

138
	std::string value;
139 140
	if (ExecuteRow(s))
		value = (const char*)sqlite3_column_text(s, 0);
141 142 143 144

	return value;
}

145 146 147
void
StickerDatabase::ListValues(std::map<std::string, std::string> &table,
			    const char *type, const char *uri)
148
{
149
	sqlite3_stmt *const s = stmt[STICKER_SQL_LIST];
150

151 152
	assert(type != nullptr);
	assert(uri != nullptr);
153

154
	BindAll(s, type, uri);
155

156 157 158
	AtScopeExit(s) {
		sqlite3_reset(s);
		sqlite3_clear_bindings(s);
159 160
	};

161 162 163 164 165
	ExecuteForEach(s, [s, &table](){
		const char *name = (const char *)sqlite3_column_text(s, 0);
		const char *value = (const char *)sqlite3_column_text(s, 1);
		table.insert(std::make_pair(name, value));
	});
166 167
}

168 169 170
bool
StickerDatabase::UpdateValue(const char *type, const char *uri,
			     const char *name, const char *value)
171
{
172
	sqlite3_stmt *const s = stmt[STICKER_SQL_UPDATE];
173

174 175 176
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
177
	assert(*name != 0);
178
	assert(value != nullptr);
179

180
	BindAll(s, value, type, uri, name);
181

182 183 184
	AtScopeExit(s) {
		sqlite3_reset(s);
		sqlite3_clear_bindings(s);
185
	};
186

187
	bool modified = ExecuteModified(s);
188

189 190 191
	if (modified)
		idle_add(IDLE_STICKER);
	return modified;
192 193
}

194 195 196
void
StickerDatabase::InsertValue(const char *type, const char *uri,
			     const char *name, const char *value)
197
{
198
	sqlite3_stmt *const s = stmt[STICKER_SQL_INSERT];
199

200 201 202
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
203
	assert(*name != 0);
204
	assert(value != nullptr);
205

206
	BindAll(s, type, uri, name, value);
207

208 209 210
	AtScopeExit(s) {
		sqlite3_reset(s);
		sqlite3_clear_bindings(s);
211
	};
212

213
	ExecuteCommand(s);
214
	idle_add(IDLE_STICKER);
215 216
}

217
void
218 219
StickerDatabase::StoreValue(const char *type, const char *uri,
			    const char *name, const char *value)
220
{
221 222 223 224
	assert(type != nullptr);
	assert(uri != nullptr);
	assert(name != nullptr);
	assert(value != nullptr);
225

226
	if (StringIsEmpty(name))
227
		return;
228

229 230
	if (!UpdateValue(type, uri, name, value))
		InsertValue(type, uri, name, value);
231 232 233
}

bool
234
StickerDatabase::Delete(const char *type, const char *uri)
235
{
236
	sqlite3_stmt *const s = stmt[STICKER_SQL_DELETE];
237

238 239
	assert(type != nullptr);
	assert(uri != nullptr);
240

241
	BindAll(s, type, uri);
242

243 244 245
	AtScopeExit(s) {
		sqlite3_reset(s);
		sqlite3_clear_bindings(s);
246
	};
247

248
	bool modified = ExecuteModified(s);
249 250 251
	if (modified)
		idle_add(IDLE_STICKER);
	return modified;
252
}
253

254
bool
255 256
StickerDatabase::DeleteValue(const char *type, const char *uri,
			     const char *name)
257
{
258
	sqlite3_stmt *const s = stmt[STICKER_SQL_DELETE_VALUE];
259

260 261
	assert(type != nullptr);
	assert(uri != nullptr);
262

263
	BindAll(s, type, uri, name);
264

265 266 267
	AtScopeExit(s) {
		sqlite3_reset(s);
		sqlite3_clear_bindings(s);
268
	};
269

270
	bool modified = ExecuteModified(s);
271 272 273
	if (modified)
		idle_add(IDLE_STICKER);
	return modified;
274 275
}

276
Sticker
277
StickerDatabase::Load(const char *type, const char *uri)
278
{
279
	Sticker s;
280

281
	ListValues(s.table, type, uri);
282

283
	return s;
284
}
285

286 287 288 289
sqlite3_stmt *
StickerDatabase::BindFind(const char *type, const char *base_uri,
			  const char *name,
			  StickerOperator op, const char *value)
290 291 292 293 294 295 296
{
	assert(type != nullptr);
	assert(name != nullptr);

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

297 298
	switch (op) {
	case StickerOperator::EXISTS:
299 300
		BindAll(stmt[STICKER_SQL_FIND], type, base_uri, name);
		return stmt[STICKER_SQL_FIND];
301

302
	case StickerOperator::EQUALS:
303
		BindAll(stmt[STICKER_SQL_FIND_VALUE],
304
			type, base_uri, name, value);
305
		return stmt[STICKER_SQL_FIND_VALUE];
306 307

	case StickerOperator::LESS_THAN:
308
		BindAll(stmt[STICKER_SQL_FIND_LT],
309
			type, base_uri, name, value);
310
		return stmt[STICKER_SQL_FIND_LT];
311 312

	case StickerOperator::GREATER_THAN:
313
		BindAll(stmt[STICKER_SQL_FIND_GT],
314
			type, base_uri, name, value);
315
		return stmt[STICKER_SQL_FIND_GT];
316
	}
317 318 319

	assert(false);
	gcc_unreachable();
320 321
}

322
void
323 324 325 326 327
StickerDatabase::Find(const char *type, const char *base_uri, const char *name,
		      StickerOperator op, const char *value,
		      void (*func)(const char *uri, const char *value,
				   void *user_data),
		      void *user_data)
328
{
329
	assert(func != nullptr);
330

331 332
	sqlite3_stmt *const s = BindFind(type, base_uri, name, op, value);
	assert(s != nullptr);
333

334 335 336
	AtScopeExit(s) {
		sqlite3_reset(s);
		sqlite3_clear_bindings(s);
337 338
	};

339 340 341
	ExecuteForEach(s, [s, func, user_data](){
			func((const char*)sqlite3_column_text(s, 0),
			     (const char*)sqlite3_column_text(s, 1),
342
			     user_data);
343
		});
344
}