Database.cxx 7.81 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2020 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/StringCompare.hxx"
26
#include "util/ScopeExit.hxx"
27

28 29
#include <iterator>

30 31
#include <assert.h>

32 33
using namespace Sqlite;

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

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

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

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

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

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

90
	int ret;
91 92 93

	/* create the table and index */

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

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

102
	for (unsigned i = 0; i < std::size(sticker_sql); ++i) {
103
		assert(sticker_sql[i] != nullptr);
104

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

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

113
	for (unsigned i = 0; i < std::size(stmt); ++i) {
114
		assert(stmt[i] != nullptr);
115

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

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

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

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

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

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

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

	return value;
}

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

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

155
	BindAll(s, type, uri);
156

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

162 163 164 165 166
	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));
	});
167 168
}

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

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

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

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

188
	bool modified = ExecuteModified(s);
189

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

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

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

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

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

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

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

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

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

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

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

242
	BindAll(s, type, uri);
243

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

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

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

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

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

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

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

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

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

284
	return s;
285
}
286

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

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

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

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

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

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

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

323
void
324 325 326 327 328
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)
329
{
330
	assert(func != nullptr);
331

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

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

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