2009-01-19 18:51:57 +01:00
|
|
|
/*
|
2021-01-01 19:54:25 +01:00
|
|
|
* Copyright 2003-2021 The Music Player Daemon Project
|
2009-01-19 18:51:57 +01:00
|
|
|
* 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.
|
2009-03-13 18:43:16 +01:00
|
|
|
*
|
|
|
|
* 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.
|
2009-01-19 18:51:57 +01:00
|
|
|
*/
|
|
|
|
|
2019-04-24 15:15:19 +02:00
|
|
|
#include "Database.hxx"
|
2019-04-24 15:03:52 +02:00
|
|
|
#include "Sticker.hxx"
|
2014-12-12 20:39:36 +01:00
|
|
|
#include "lib/sqlite/Util.hxx"
|
2013-08-07 19:54:38 +02:00
|
|
|
#include "fs/Path.hxx"
|
2021-05-25 18:45:18 +02:00
|
|
|
#include "fs/NarrowPath.hxx"
|
2013-01-09 08:36:52 +01:00
|
|
|
#include "Idle.hxx"
|
2015-11-06 09:37:07 +01:00
|
|
|
#include "util/StringCompare.hxx"
|
2016-10-27 07:50:08 +02:00
|
|
|
#include "util/ScopeExit.hxx"
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2020-03-12 23:20:59 +01:00
|
|
|
#include <cassert>
|
2019-08-03 13:10:49 +02:00
|
|
|
#include <iterator>
|
|
|
|
|
2019-04-25 11:56:52 +02:00
|
|
|
using namespace Sqlite;
|
|
|
|
|
2009-03-27 17:05:04 +01:00
|
|
|
enum sticker_sql {
|
|
|
|
STICKER_SQL_GET,
|
|
|
|
STICKER_SQL_LIST,
|
|
|
|
STICKER_SQL_UPDATE,
|
|
|
|
STICKER_SQL_INSERT,
|
|
|
|
STICKER_SQL_DELETE,
|
2009-04-28 20:23:27 +02:00
|
|
|
STICKER_SQL_DELETE_VALUE,
|
2009-04-01 18:42:06 +02:00
|
|
|
STICKER_SQL_FIND,
|
2014-12-12 22:12:19 +01:00
|
|
|
STICKER_SQL_FIND_VALUE,
|
2014-12-12 22:26:04 +01:00
|
|
|
STICKER_SQL_FIND_LT,
|
|
|
|
STICKER_SQL_FIND_GT,
|
2019-04-24 15:18:01 +02:00
|
|
|
STICKER_SQL_COUNT
|
2009-03-27 17:05:04 +01:00
|
|
|
};
|
|
|
|
|
2020-09-27 06:24:41 +02:00
|
|
|
static constexpr auto sticker_sql = std::array {
|
2013-01-02 22:25:17 +01:00
|
|
|
//[STICKER_SQL_GET] =
|
2009-03-27 17:05:04 +01:00
|
|
|
"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?",
|
2013-01-02 22:25:17 +01:00
|
|
|
//[STICKER_SQL_LIST] =
|
2009-03-27 17:05:04 +01:00
|
|
|
"SELECT name,value FROM sticker WHERE type=? AND uri=?",
|
2013-01-02 22:25:17 +01:00
|
|
|
//[STICKER_SQL_UPDATE] =
|
2009-03-27 17:05:04 +01:00
|
|
|
"UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?",
|
2013-01-02 22:25:17 +01:00
|
|
|
//[STICKER_SQL_INSERT] =
|
2009-03-27 17:05:04 +01:00
|
|
|
"INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)",
|
2013-01-02 22:25:17 +01:00
|
|
|
//[STICKER_SQL_DELETE] =
|
2009-03-27 17:05:04 +01:00
|
|
|
"DELETE FROM sticker WHERE type=? AND uri=?",
|
2013-01-02 22:25:17 +01:00
|
|
|
//[STICKER_SQL_DELETE_VALUE] =
|
2009-04-28 20:23:27 +02:00
|
|
|
"DELETE FROM sticker WHERE type=? AND uri=? AND name=?",
|
2013-01-02 22:25:17 +01:00
|
|
|
//[STICKER_SQL_FIND] =
|
2009-04-01 18:42:06 +02:00
|
|
|
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",
|
2014-12-12 22:12:19 +01:00
|
|
|
|
|
|
|
//[STICKER_SQL_FIND_VALUE] =
|
|
|
|
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?",
|
2014-12-12 22:26:04 +01:00
|
|
|
|
|
|
|
//[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>?",
|
2009-03-27 17:05:04 +01:00
|
|
|
};
|
|
|
|
|
2020-09-27 06:24:41 +02:00
|
|
|
static constexpr const char sticker_sql_create[] =
|
2009-01-19 18:51:57 +01:00
|
|
|
"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);"
|
|
|
|
"";
|
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
StickerDatabase::StickerDatabase(Path path)
|
2021-05-25 18:45:18 +02:00
|
|
|
:db(NarrowPath(path))
|
2009-01-19 18:51:57 +01:00
|
|
|
{
|
2013-10-17 21:59:35 +02:00
|
|
|
assert(!path.IsNull());
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2013-10-17 21:59:35 +02:00
|
|
|
int ret;
|
2009-01-19 18:51:57 +01:00
|
|
|
|
|
|
|
/* create the table and index */
|
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
ret = sqlite3_exec(db, sticker_sql_create,
|
2013-10-19 18:19:03 +02:00
|
|
|
nullptr, nullptr, nullptr);
|
2016-10-27 07:50:08 +02:00
|
|
|
if (ret != SQLITE_OK)
|
2019-04-24 15:18:01 +02:00
|
|
|
throw SqliteError(db, ret,
|
2016-10-27 07:50:08 +02:00
|
|
|
"Failed to create sticker table");
|
2009-01-19 18:51:57 +01:00
|
|
|
|
|
|
|
/* prepare the statements we're going to use */
|
|
|
|
|
2020-09-27 06:24:41 +02:00
|
|
|
for (size_t i = 0; i < sticker_sql.size(); ++i) {
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(sticker_sql[i] != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
stmt[i] = Prepare(db, sticker_sql[i]);
|
2009-03-27 17:05:04 +01:00
|
|
|
}
|
2009-01-19 18:51:57 +01:00
|
|
|
}
|
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
StickerDatabase::~StickerDatabase() noexcept
|
2009-01-19 18:51:57 +01:00
|
|
|
{
|
2019-04-24 15:18:01 +02:00
|
|
|
assert(db != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2020-09-27 06:24:41 +02:00
|
|
|
for (const auto &sticker : stmt) {
|
|
|
|
assert(sticker != nullptr);
|
2009-03-27 17:05:04 +01:00
|
|
|
|
2020-09-27 06:24:41 +02:00
|
|
|
sqlite3_finalize(sticker);
|
2009-03-27 17:05:04 +01:00
|
|
|
}
|
2009-01-19 18:51:57 +01:00
|
|
|
}
|
|
|
|
|
2013-10-17 19:37:51 +02:00
|
|
|
std::string
|
2019-04-24 15:18:01 +02:00
|
|
|
StickerDatabase::LoadValue(const char *type, const char *uri, const char *name)
|
2009-01-19 18:51:57 +01:00
|
|
|
{
|
2019-04-24 15:18:01 +02:00
|
|
|
sqlite3_stmt *const s = stmt[STICKER_SQL_GET];
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(type != nullptr);
|
|
|
|
assert(uri != nullptr);
|
|
|
|
assert(name != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2015-11-06 09:37:07 +01:00
|
|
|
if (StringIsEmpty(name))
|
2013-10-17 19:37:51 +02:00
|
|
|
return std::string();
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(s, type, uri, name);
|
2016-10-27 07:50:08 +02:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
AtScopeExit(s) {
|
|
|
|
sqlite3_reset(s);
|
|
|
|
sqlite3_clear_bindings(s);
|
2016-10-27 07:50:08 +02:00
|
|
|
};
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2013-10-17 19:37:51 +02:00
|
|
|
std::string value;
|
2019-04-24 15:18:01 +02:00
|
|
|
if (ExecuteRow(s))
|
|
|
|
value = (const char*)sqlite3_column_text(s, 0);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
void
|
|
|
|
StickerDatabase::ListValues(std::map<std::string, std::string> &table,
|
|
|
|
const char *type, const char *uri)
|
2009-03-03 07:49:23 +01:00
|
|
|
{
|
2019-04-24 15:18:01 +02:00
|
|
|
sqlite3_stmt *const s = stmt[STICKER_SQL_LIST];
|
2009-03-03 07:49:23 +01:00
|
|
|
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(type != nullptr);
|
|
|
|
assert(uri != nullptr);
|
2009-03-03 07:49:23 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(s, type, uri);
|
2009-03-03 07:49:23 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
AtScopeExit(s) {
|
|
|
|
sqlite3_reset(s);
|
|
|
|
sqlite3_clear_bindings(s);
|
2016-10-27 07:50:08 +02:00
|
|
|
};
|
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
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));
|
|
|
|
});
|
2009-03-03 07:49:23 +01:00
|
|
|
}
|
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
bool
|
|
|
|
StickerDatabase::UpdateValue(const char *type, const char *uri,
|
|
|
|
const char *name, const char *value)
|
2009-01-19 18:51:57 +01:00
|
|
|
{
|
2019-04-24 15:18:01 +02:00
|
|
|
sqlite3_stmt *const s = stmt[STICKER_SQL_UPDATE];
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(type != nullptr);
|
|
|
|
assert(uri != nullptr);
|
|
|
|
assert(name != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
assert(*name != 0);
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(value != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(s, value, type, uri, name);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
AtScopeExit(s) {
|
|
|
|
sqlite3_reset(s);
|
|
|
|
sqlite3_clear_bindings(s);
|
2016-10-27 07:50:08 +02:00
|
|
|
};
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
bool modified = ExecuteModified(s);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2014-12-12 21:21:56 +01:00
|
|
|
if (modified)
|
|
|
|
idle_add(IDLE_STICKER);
|
|
|
|
return modified;
|
2009-01-19 18:51:57 +01:00
|
|
|
}
|
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
void
|
|
|
|
StickerDatabase::InsertValue(const char *type, const char *uri,
|
|
|
|
const char *name, const char *value)
|
2009-01-19 18:51:57 +01:00
|
|
|
{
|
2019-04-24 15:18:01 +02:00
|
|
|
sqlite3_stmt *const s = stmt[STICKER_SQL_INSERT];
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(type != nullptr);
|
|
|
|
assert(uri != nullptr);
|
|
|
|
assert(name != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
assert(*name != 0);
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(value != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(s, type, uri, name, value);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
AtScopeExit(s) {
|
|
|
|
sqlite3_reset(s);
|
|
|
|
sqlite3_clear_bindings(s);
|
2016-10-27 07:50:08 +02:00
|
|
|
};
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
ExecuteCommand(s);
|
2016-10-27 07:50:08 +02:00
|
|
|
idle_add(IDLE_STICKER);
|
2009-01-19 18:51:57 +01:00
|
|
|
}
|
|
|
|
|
2016-10-27 07:50:08 +02:00
|
|
|
void
|
2019-04-24 15:18:01 +02:00
|
|
|
StickerDatabase::StoreValue(const char *type, const char *uri,
|
|
|
|
const char *name, const char *value)
|
2009-01-19 18:51:57 +01:00
|
|
|
{
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(type != nullptr);
|
|
|
|
assert(uri != nullptr);
|
|
|
|
assert(name != nullptr);
|
|
|
|
assert(value != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2015-11-06 09:37:07 +01:00
|
|
|
if (StringIsEmpty(name))
|
2016-10-27 07:50:08 +02:00
|
|
|
return;
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
if (!UpdateValue(type, uri, name, value))
|
|
|
|
InsertValue(type, uri, name, value);
|
2009-01-19 18:51:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-04-24 15:18:01 +02:00
|
|
|
StickerDatabase::Delete(const char *type, const char *uri)
|
2009-01-19 18:51:57 +01:00
|
|
|
{
|
2019-04-24 15:18:01 +02:00
|
|
|
sqlite3_stmt *const s = stmt[STICKER_SQL_DELETE];
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(type != nullptr);
|
|
|
|
assert(uri != nullptr);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(s, type, uri);
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
AtScopeExit(s) {
|
|
|
|
sqlite3_reset(s);
|
|
|
|
sqlite3_clear_bindings(s);
|
2016-10-27 07:50:08 +02:00
|
|
|
};
|
2009-01-19 18:51:57 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
bool modified = ExecuteModified(s);
|
2014-12-12 21:21:56 +01:00
|
|
|
if (modified)
|
|
|
|
idle_add(IDLE_STICKER);
|
|
|
|
return modified;
|
2009-01-19 18:51:57 +01:00
|
|
|
}
|
2009-03-14 14:20:01 +01:00
|
|
|
|
2009-04-28 20:23:27 +02:00
|
|
|
bool
|
2019-04-24 15:18:01 +02:00
|
|
|
StickerDatabase::DeleteValue(const char *type, const char *uri,
|
|
|
|
const char *name)
|
2009-04-28 20:23:27 +02:00
|
|
|
{
|
2019-04-24 15:18:01 +02:00
|
|
|
sqlite3_stmt *const s = stmt[STICKER_SQL_DELETE_VALUE];
|
2009-04-28 20:23:27 +02:00
|
|
|
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(type != nullptr);
|
|
|
|
assert(uri != nullptr);
|
2009-04-28 20:23:27 +02:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(s, type, uri, name);
|
2009-04-28 20:23:27 +02:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
AtScopeExit(s) {
|
|
|
|
sqlite3_reset(s);
|
|
|
|
sqlite3_clear_bindings(s);
|
2016-10-27 07:50:08 +02:00
|
|
|
};
|
2009-04-28 20:23:27 +02:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
bool modified = ExecuteModified(s);
|
2014-12-12 21:21:56 +01:00
|
|
|
if (modified)
|
|
|
|
idle_add(IDLE_STICKER);
|
|
|
|
return modified;
|
2009-04-28 20:23:27 +02:00
|
|
|
}
|
|
|
|
|
2019-04-24 15:05:05 +02:00
|
|
|
Sticker
|
2019-04-24 15:18:01 +02:00
|
|
|
StickerDatabase::Load(const char *type, const char *uri)
|
2009-03-14 14:20:01 +01:00
|
|
|
{
|
2015-01-28 19:33:56 +01:00
|
|
|
Sticker s;
|
2009-03-14 14:21:11 +01:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
ListValues(s.table, type, uri);
|
2009-03-14 14:20:01 +01:00
|
|
|
|
2019-04-24 15:05:05 +02:00
|
|
|
return s;
|
2009-03-14 14:20:01 +01:00
|
|
|
}
|
2009-04-01 18:42:06 +02:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
sqlite3_stmt *
|
|
|
|
StickerDatabase::BindFind(const char *type, const char *base_uri,
|
|
|
|
const char *name,
|
|
|
|
StickerOperator op, const char *value)
|
2014-12-12 22:04:43 +01:00
|
|
|
{
|
|
|
|
assert(type != nullptr);
|
|
|
|
assert(name != nullptr);
|
|
|
|
|
|
|
|
if (base_uri == nullptr)
|
|
|
|
base_uri = "";
|
|
|
|
|
2014-12-12 13:47:57 +01:00
|
|
|
switch (op) {
|
|
|
|
case StickerOperator::EXISTS:
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(stmt[STICKER_SQL_FIND], type, base_uri, name);
|
|
|
|
return stmt[STICKER_SQL_FIND];
|
2014-12-12 13:47:57 +01:00
|
|
|
|
2014-12-12 22:12:19 +01:00
|
|
|
case StickerOperator::EQUALS:
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(stmt[STICKER_SQL_FIND_VALUE],
|
2016-10-27 07:50:08 +02:00
|
|
|
type, base_uri, name, value);
|
2019-04-24 15:18:01 +02:00
|
|
|
return stmt[STICKER_SQL_FIND_VALUE];
|
2014-12-12 22:26:04 +01:00
|
|
|
|
|
|
|
case StickerOperator::LESS_THAN:
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(stmt[STICKER_SQL_FIND_LT],
|
2016-10-27 07:50:08 +02:00
|
|
|
type, base_uri, name, value);
|
2019-04-24 15:18:01 +02:00
|
|
|
return stmt[STICKER_SQL_FIND_LT];
|
2014-12-12 22:26:04 +01:00
|
|
|
|
|
|
|
case StickerOperator::GREATER_THAN:
|
2019-04-24 15:18:01 +02:00
|
|
|
BindAll(stmt[STICKER_SQL_FIND_GT],
|
2016-10-27 07:50:08 +02:00
|
|
|
type, base_uri, name, value);
|
2019-04-24 15:18:01 +02:00
|
|
|
return stmt[STICKER_SQL_FIND_GT];
|
2014-12-12 22:12:19 +01:00
|
|
|
}
|
2014-12-12 13:47:57 +01:00
|
|
|
|
|
|
|
assert(false);
|
|
|
|
gcc_unreachable();
|
2014-12-12 22:04:43 +01:00
|
|
|
}
|
|
|
|
|
2016-10-27 07:50:08 +02:00
|
|
|
void
|
2019-04-24 15:18:01 +02:00
|
|
|
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)
|
2009-04-01 18:42:06 +02:00
|
|
|
{
|
2013-10-19 18:19:03 +02:00
|
|
|
assert(func != nullptr);
|
2009-04-01 18:42:06 +02:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
sqlite3_stmt *const s = BindFind(type, base_uri, name, op, value);
|
|
|
|
assert(s != nullptr);
|
2009-04-01 18:42:06 +02:00
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
AtScopeExit(s) {
|
|
|
|
sqlite3_reset(s);
|
|
|
|
sqlite3_clear_bindings(s);
|
2016-10-27 07:50:08 +02:00
|
|
|
};
|
|
|
|
|
2019-04-24 15:18:01 +02:00
|
|
|
ExecuteForEach(s, [s, func, user_data](){
|
|
|
|
func((const char*)sqlite3_column_text(s, 0),
|
|
|
|
(const char*)sqlite3_column_text(s, 1),
|
2009-04-01 18:42:06 +02:00
|
|
|
user_data);
|
2014-12-12 20:32:58 +01:00
|
|
|
});
|
2009-04-01 18:42:06 +02:00
|
|
|
}
|