Sélectionner une révision Git
RegisterController.php
-
Romain 'Table' DROUIN a rédigé
Creation du fichier Image.php + utilisation dans l'inscription + modifiction de la table r_joueur pour ajouter une colonne pour savoir si le joueur a un avatar
Romain 'Table' DROUIN a rédigéCreation du fichier Image.php + utilisation dans l'inscription + modifiction de la table r_joueur pour ajouter une colonne pour savoir si le joueur a un avatar
queue.c 22,74 Kio
#define _POSIX_C_SOURCE 200809L
#include <lektor/database.h>
#include <lektor/macro.h>
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
/* Find in in database/open.c */
extern int is_sql_str_invalid(const char *);
bool
database_queue_state(sqlite3 *db, struct lkt_queue_state *res)
{
static const char *SQL_STMT =
"SELECT"
" volume, paused, random, repeat, single, current, duration, consume, "
" (SELECT COUNT(*) FROM queue) AS length "
"FROM queue_state;\n";
sqlite3_stmt *stmt = 0;
bool ret = false;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
if (sqlite3_step(stmt) != SQLITE_ROW) {
fprintf(stderr, " ! database_queue_state: queue_state has no row.\n");
goto error;
}
res->volume = sqlite3_column_int(stmt, 0);
res->paused = sqlite3_column_int(stmt, 1);
res->random = sqlite3_column_int(stmt, 2);
res->repeat = sqlite3_column_int(stmt, 3);
res->single = sqlite3_column_int(stmt, 4);
if (sqlite3_column_type(stmt, 5) == SQLITE_NULL)
res->current = -1;
else
res->current = sqlite3_column_int(stmt, 5);
if (sqlite3_column_type(stmt, 6) == SQLITE_NULL)
res->duration = 0;
else
res->duration = sqlite3_column_int(stmt, 6);
res->consume = sqlite3_column_int(stmt, 7);
res->length = sqlite3_column_int(stmt, 8);
ret = true;
error:
sqlite3_finalize(stmt);
return ret;
}
bool
database_queue_current_kara(sqlite3 *db, struct kara_metadata *res, int *id)
{
static const char *SQL_STMT =
"SELECT song_name, source_name, category, language, author_name, song_type, song_number, kara_id"
" FROM kara"
" JOIN queue_ ON kara_id = kara.id"
" JOIN queue_state ON current = position";
sqlite3_stmt *stmt = 0;
int ret = false;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
if (sqlite3_step(stmt) == SQLITE_ROW) {
/* Here use gotos because of optimisations done by compilators.
Most of the time it won't be NULL. */
if (!res)
goto no_metadata;
strncpy(res->song_name, (const char *) sqlite3_column_text(stmt, 0), LEKTOR_TAG_MAX - 1);
strncpy(res->source_name, (const char *) sqlite3_column_text(stmt, 1), LEKTOR_TAG_MAX - 1);
strncpy(res->category, (const char *) sqlite3_column_text(stmt, 2), LEKTOR_TAG_MAX - 1);
strncpy(res->language, (const char *) sqlite3_column_text(stmt, 3), LEKTOR_TAG_MAX - 1);
strncpy(res->author_name, (const char *) sqlite3_column_text(stmt, 4), LEKTOR_TAG_MAX - 1);
strncpy(res->song_type, (const char *) sqlite3_column_text(stmt, 5), LEKTOR_TAG_MAX - 1);
res->song_number = sqlite3_column_int(stmt, 6);
no_metadata:
/* Most of the time this will be NULL. */
if (id)
*id = sqlite3_column_int(stmt, 7);
} else {
fprintf(stderr, " ! database_queue_current_kara: failed: %s\n",
sqlite3_errmsg(db));
goto error;
}
ret = true;
error:
sqlite3_finalize(stmt);
return ret;
}
static bool
queue_add_with_col_like_str(sqlite3 *db, const char *col, const char *val, int priority)
{
if (is_sql_str_invalid(col)) {
fprintf(stderr, " ! queue_add_with_col_like_str: Column name %s is invalid\n", col);
return false;
}
char SQL[LKT_MAX_SQLITE_STATEMENT];
static const char *SQL_STMT =
"INSERT INTO queue (kara_id, priority)"
" SELECT id, ?"
" FROM kara"
" WHERE %s LIKE ? AND available = 1"
" ORDER BY RANDOM();";
bool status = false;
sqlite3_stmt *stmt = NULL;
snprintf(SQL, LKT_MAX_SQLITE_STATEMENT - 1, SQL_STMT, col);
SQL[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
SQLITE_PREPARE(db, stmt, SQL, error);
SQLITE_BIND_INT(db, stmt, 1, priority, error);
SQLITE_BIND_TEXT(db, stmt, 2, val, error);
if (sqlite3_step(stmt) != SQLITE_DONE) {
fprintf(stderr, " ! queue_add_with_col_like_str: Failed to insert: %s\n",
sqlite3_errmsg(db));
goto error;
}
status = true;
error:
sqlite3_finalize(stmt);
return status;
}
static bool
queue_insert_with_col_like_str(sqlite3 *db, const char *col, const char *val, int pos)
{
if (is_sql_str_invalid(col)) {
fprintf(stderr, " ! queue_insert_with_col_like_str: Column name %s is invalid\n", col);
return false;
}
char SQL[LKT_MAX_SQLITE_STATEMENT];
static const char *SQL_STMT =
"BEGIN TRANSACTION;"
"CREATE TEMPORARY TABLE queue_tmp"
" ( position_tmp INTEGER PRIMARY KEY AUTOINCREMENT"
" , kara_id_tmp INTEGER"
" , priority_tmp INTEGER NOT NULL DEFAULT 1 CHECK(priority > 0 AND priority < 6)"
" );"
"INERT INTO queue_tmp (kara_id_tmp, priority_tmp)"
" SELECT (kara_id, priority)"
" FROM queue"
" WHERE position >= ?"
" ORDER BY position ASC;"
"INSERT INTO queue (kara_id, priority)"
"SELECT id, priority"
" FROM kara"
" WHERE %s LIKE ? AND available = 1"
" ORDER BY RANDOM();"
"INSERT INTO queue (kara_id, priority)"
" SELECT kara_id_tmp, priority"
" FROM queue_tmp"
" ORDER BY position ASC;"
"DROP TABLE queue_tmp;"
"COMMIT TRANSACTION;";
bool status = false;
sqlite3_stmt *stmt = NULL;
snprintf(SQL, LKT_MAX_SQLITE_STATEMENT - 1, SQL_STMT, col);
SQL[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
SQLITE_PREPARE(db, stmt, SQL, error);
SQLITE_BIND_INT(db, stmt, 1, pos, error);
SQLITE_BIND_TEXT(db, stmt, 2, val, error);
if (sqlite3_step(stmt) != SQLITE_DONE) {
fprintf(stderr, " ! queue_insert_with_col_like_str: Failed to insert: %s\n",
sqlite3_errmsg(db));
goto error;
}
status = true;
error:
sqlite3_finalize(stmt);
return status;
}
bool
database_queue_insert_query(sqlite3 *db, const char *query, int pos)
{
bool status = queue_insert_with_col_like_str(db, "string", query, pos);
if (status)
fprintf(stderr, " * database_queue_insert_query: Successfull add for query '%s'\n", query);
else
fprintf(stderr, " ! database_queue_insert_query: Failed to add for query '%s'\n", query);
return status;
}
bool
database_queue_add_plt(sqlite3 *db, const char *plt_name, int priority)
{
static const char *SQL_STMT =
"INSERT INTO queue (kara_id, priority) "
"SELECT kara.id, ?"
" FROM kara"
" JOIN kara_playlist ON kara_id = kara.id AND kara.available = 1"
" JOIN playlist ON playlist_id = playlist.id AND playlist.name = ?"
" ORDER BY RANDOM();";
bool status = false;
sqlite3_stmt *stmt = NULL;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
SQLITE_BIND_INT(db, stmt, 1, priority, error);
SQLITE_BIND_TEXT(db, stmt, 2, plt_name, error);
if (sqlite3_step(stmt) != SQLITE_DONE) {
fprintf(stderr, " ! database_queue_add_plt: Failed to insert: %s\n",
sqlite3_errmsg(db));
goto error;
}
status = true;
error:
sqlite3_finalize(stmt);
return status;
}
bool
database_queue_add_query(sqlite3 *db, const char *query, int priority)
{
bool status = queue_add_with_col_like_str(db, "string", query, priority);
if (status)
fprintf(stderr, " * database_queue_add_query: Successfull add for query '%s'\n", query);
else
fprintf(stderr, " ! database_queue_add_query: Failed to add for query '%s'\n", query);
return status;
}
bool
database_queue_add_author(sqlite3 *db, const char *author, int priority)
{
bool status = queue_add_with_col_like_str(db, "author_name", author, priority);
if (status)
fprintf(stderr, " * database_queue_add_author: Successfull add for author '%s'\n", author);
else
fprintf(stderr, " ! database_queue_add_author: Failed to add for author '%s'\n", author);
return status;
}
bool
database_queue_add_language(sqlite3 *db, const char *language, int priority)
{
bool status = queue_add_with_col_like_str(db, "language", language, priority);
if (status)
fprintf(stderr, " * database_queue_add_language: Successfull add for language '%s'\n", language);
else
fprintf(stderr, " ! database_queue_add_language: Failed to add for language '%s'\n", language);
return status;
}
bool
database_queue_add_category(sqlite3 *db, const char *cat, int priority)
{
bool status = queue_add_with_col_like_str(db, "song_type", cat, priority);
if (status)
fprintf(stderr, " * database_queue_add_category: Successfull add for category (lektor song_type) '%s'\n",
cat);
else
fprintf(stderr, " ! database_queue_add_category: Failed to add for category (lektor song_type) '%s'\n", cat);
return status;
}
bool
database_queue_add_type(sqlite3 *db, const char *type, int priority)
{
bool status = queue_add_with_col_like_str(db, "category", type, priority);
if (status)
fprintf(stderr, " * database_queue_add_type: Successfull add for type (lektor category) '%s'\n", type);
else
fprintf(stderr, " ! database_queue_add_type: Failed to add for type (lektor category) '%s'\n", type);
return status;
}
bool
database_queue_add_id(sqlite3 *db, int id, int priority)
{
static const char *SQL_STMT = "INSERT INTO queue (kara_id, priority) VALUES (?,?);";
bool status = false;
sqlite3_stmt *stmt = NULL;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
SQLITE_BIND_INT(db, stmt, 1, id, error);
SQLITE_BIND_INT(db, stmt, 2, priority, error);
if (sqlite3_step(stmt) != SQLITE_DONE)
goto error;
status = true;
error:
sqlite3_finalize(stmt);
return status;
}
bool
database_queue_del_id(sqlite3 *db, int id)
{
static const char *SQL_TEMPLATE =
"BEGIN TRANSACTION;"
"WITH before(pos) AS (SELECT position FROM queue_ JOIN queue_state WHERE position < current ORDER BY position DESC LIMIT 1) "
"UPDATE queue_state SET current = CASE"
" WHEN current IS NULL THEN NULL"
" ELSE (SELECT pos FROM before) "
"END WHERE current > (SELECT position FROM queue_ WHERE kara_id = %d);"
"DELETE FROM queue WHERE kara_id = %d LIMIT 1;"
"COMMIT TRANSACTION;";
char SQL[LKT_MAX_SQLITE_STATEMENT];
snprintf(SQL, LKT_MAX_SQLITE_STATEMENT - 1, SQL_TEMPLATE, id, id, id);
SQL[LKT_MAX_SQLITE_STATEMENT - 1] = '\0';
SQLITE_EXEC(db, SQL, error);
return true;
error:
return false;
}
bool
database_queue_next(sqlite3 *db, char filepath[PATH_MAX])
{
static const char *SQL_STMT =
"SELECT file_path, position, RANDOM()"
" FROM kara"
" JOIN queue_ ON kara.id = queue_.kara_id"
" JOIN queue_state"
" ON current IS NULL"
" OR (CASE WHEN (SELECT single FROM queue_state LIMIT 1) = 1 THEN position = current"
" WHEN (SELECT random FROM queue_state LIMIT 1) = 1 THEN position <> current"
" ELSE position > current END)"
" ORDER BY CASE WHEN (SELECT random FROM queue_state LIMIT 1) = 1 THEN RANDOM() ELSE 2 END"
" LIMIT 1";
char SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT];
bool status = false;
int code = SQLITE_OK, id;
sqlite3_stmt *stmt = NULL;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
code = sqlite3_step(stmt);
if (code == SQLITE_ROW) {
id = MAX(1, sqlite3_column_int(stmt, 1));
snprintf(SQL_UPDATE, LKT_MAX_SQLITE_STATEMENT - 1, "UPDATE queue_state SET current = %d;", id);
SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
if (filepath != NULL)
strncpy(filepath, (const char *) sqlite3_column_text(stmt, 0), PATH_MAX);
else {
fprintf(stderr, " ! database_queue_next: Failed to get file, id was %d\n", id);
goto error;
}
}
else if (code == SQLITE_DONE) {
fprintf(stderr, " * database_queue_next: Failed to get next, no setting current to NULL\n");
goto error;
}
else {
fprintf(stderr, "database_queue_next: Failed to fetch next kara: %s\n",
sqlite3_errmsg(db));
goto error;
}
SQLITE_EXEC(db, SQL_UPDATE, error);
status = true;
error:
sqlite3_finalize(stmt);
return status;
}
bool
database_queue_toggle_pause(sqlite3 *db)
{
static const char *SQL_STMT = "UPDATE queue_state SET paused = 1 - paused";
SQLITE_EXEC(db, SQL_STMT, error);
return true;
error:
return false;
}
bool
database_queue_prev(sqlite3 *db, char filepath[PATH_MAX])
{
static const char *SQL_STMT =
"SELECT file_path, position "
" FROM kara"
" JOIN queue_ ON kara.id = queue_.kara_id"
" JOIN queue_state ON CASE "
" WHEN (SELECT single FROM queue_state LIMIT 1) = 1 THEN position = current"
" ELSE queue_.position < queue_state.current"
" END"
" ORDER BY position DESC LIMIT 1;";
char SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT];
bool status = false;
int code = SQLITE_OK, id;
sqlite3_stmt *stmt = NULL;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
code = sqlite3_step(stmt);
if (code == SQLITE_ROW) {
id = MAX(1, sqlite3_column_int(stmt, 1));
snprintf(SQL_UPDATE, LKT_MAX_SQLITE_STATEMENT - 1, "UPDATE queue_state SET current = %d;", id);
SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
if (filepath != NULL)
strncpy(filepath, (const char *) sqlite3_column_text(stmt, 0), PATH_MAX);
else {
fprintf(stderr, " ! database_queue_prev: Failed to get file, position was %d\n", id);
goto error;
}
}
else if (code == SQLITE_DONE) {
fprintf(stderr, " * database_queue_prev: Failed to get previous, no setting current to NULL\n");
goto error;
}
else {
fprintf(stderr, " ! database_queue_prev: Failed to fetch prev kara: %s\n",
sqlite3_errmsg(db));
goto error;
}
SQLITE_EXEC(db, SQL_UPDATE, error);
status = true;
error:
sqlite3_finalize(stmt);
return status;
}
bool
database_queue_clear(sqlite3 *db)
{
static const char *SQL_STMT =
"DELETE FROM queue;"
"DELETE FROM sqlite_sequence WHERE name = 'queue';"
"UPDATE queue_state SET current = NULL;";
SQLITE_EXEC(db, SQL_STMT, error);
return true;
error:
return false;
}
bool
database_queue_crop(sqlite3 *db)
{
static const char *SQL_STMT =
"DELETE FROM queue WHERE queue.kara_id <> (SELECT current FROM queue_state LIMIT 1);";
SQLITE_EXEC(db, SQL_STMT, error);
return true;
error:
return false;
}
bool
database_queue_move(sqlite3 *db, int from, int to)
{
/* stmt arguments in order:
* 1. to
* 2. to
* 3. from
*/
static const char *SQL_STMT =
"BEGIN TRANSACTION;"
"UPDATE queue SET position = position + 1 WHERE position >= ?;"
"UPDATE queue SET position = ? WHERE position = ?;"
"COMMIT TRANSACTION;";
sqlite3_stmt *stmt = 0;
bool ret = false;
int code;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
SQLITE_BIND_INT(db, stmt, 1, to, error);
SQLITE_BIND_INT(db, stmt, 2, to, error);
SQLITE_BIND_INT(db, stmt, 3, from, error);
code = sqlite3_step(stmt);
if (code != SQLITE_ROW && code != SQLITE_DONE && code != SQLITE_OK) {
fprintf(stderr, " ! database_queue_move: move failed.\n");
goto error;
}
ret = true;
error:
sqlite3_finalize(stmt);
return ret;
}
bool
database_queue_play(sqlite3 *db, int pos)
{
static const char *SQL_STMT =
"UPDATE queue_state SET current = ?, paused = 0"
" WHERE current <= 0 OR current IS NULL";
int code = SQLITE_OK;
bool ret = false;
sqlite3_stmt *stmt = NULL;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
SQLITE_BIND_INT(db, stmt, 1, pos, error);
code = sqlite3_step(stmt);
if (code != SQLITE_OK && code != SQLITE_DONE) {
fprintf(stderr, " ! database_queue_play: Failed to update queue_state: %s\n",
sqlite3_errmsg(db));
goto error;
}
ret = true;
error:
return ret;
}
bool
database_queue_stop(sqlite3 *db)
{
static const char *SQL_STMT = "UPDATE queue_state SET current = NULL;";
SQLITE_EXEC(db, SQL_STMT, error);
return true;
error:
return false;
}
bool
database_queue_set_current_index(sqlite3 *db, int idx)
{
static const char *SQL_GET_TEMPLATE = "UPDATE queue_state SET current = %d;";
char SQL_GET[LKT_MAX_SQLITE_STATEMENT];
if (idx <= 0) {
fprintf(stderr, " ! database_queue_set_current_index: an idx of %d is invalid, must be >= 0\n", idx);
return false;
}
if (snprintf(SQL_GET, LKT_MAX_SQLITE_STATEMENT - 1, SQL_GET_TEMPLATE, idx) < 0) {
fprintf(stderr, " ! database_queue_set_current_index: snprintf failed\n");
return false;
}
SQL_GET[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
SQLITE_EXEC(db, SQL_GET, error);
return true;
error:
return false;
}
bool
database_queue_get_current_file(sqlite3 *db, char filepath[PATH_MAX])
{
static const char *SQL_STMT =
"SELECT file_path"
" FROM kara"
" JOIN queue_ ON kara.id = queue_.kara_id"
" JOIN queue_state ON queue_.position = queue_state.current";
bool status = false;
int code = SQLITE_OK;
sqlite3_stmt *stmt = NULL;
if (filepath == NULL)
goto error;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
code = sqlite3_step(stmt);
if (code == SQLITE_ROW)
strncpy(filepath, (const char *) sqlite3_column_text(stmt, 0), PATH_MAX);
else {
fprintf(stderr, "database_queue_get_current_file: Failed to fetch prev kara: %s\n",
sqlite3_errmsg(db));
goto error;
}
status = true;
error:
sqlite3_finalize(stmt);
return status;
}
bool
database_queue_set_paused(sqlite3 *db, bool paused)
{
const char *SQL;
if (paused)
SQL = "UPDATE queue_state SET paused = 1;";
else
SQL = "UPDATE queue_state SET paused = 0;";
SQLITE_EXEC(db, SQL, error);
return true;
error:
return false;
}
bool
database_queue_shuffle(sqlite3 *db)
{
const char *SQL =
"BEGIN TRANSACTION;"
/* Create temporary queue. */
"CREATE TEMPORARY TABLE queue_tmp"
" ( position INTEGER PRIMARY KEY AUTOINCREMENT CHECK(position > 0)"
" , kara_id INTEGER"
" , priority INTEGER NOT NULL DEFAULT 1 CHECK(priority > 0 AND priority < 6)"
" );"
/* When current is NULL, that thing is also NULL, so no insertion is done. */
"INSERT INTO queue_tmp (kara_id, priority)"
" SELECT kara_id, priority"
" FROM queue"
" JOIN queue_state ON queue.position = queue_state.current;"
/* Insert Max priority in order. */
"INSERT INTO queue_tmp (kara_id, priority)"
" SELECT kara_id, priority"
" FROM queue"
" JOIN queue_state ON"
" queue.position <> CASE"
" WHEN queue_state.current IS NOT NULL THEN queue_state.current"
" ELSE 0 END"
" AND queue.priority = 5;"
/* Insert the others karas, where the priority is < 5. */
"INSERT INTO queue_tmp (kara_id, priority)"
" SELECT kara_id, priority"
" FROM queue"
" JOIN queue_state ON"
" queue.position <> CASE"
" WHEN queue_state.current IS NOT NULL THEN queue_state.current"
" ELSE 0 END"
" AND queue.priority <> 5"
" ORDER BY RANDOM();"
/* Do the insertion in the other way. */
"DELETE FROM queue;"
"DELETE FROM sqlite_sequence WHERE name = 'queue';"
"INSERT INTO queue (position, kara_id, priority)"
" SELECT position, kara_id, priority"
" FROM queue_tmp"
" ORDER BY priority, position;"
"DROP TABLE queue_tmp;"
"DELETE FROM sqlite_sequence WHERE name = 'queue_tmp';"
/* Set the current to the right kara when needed. */
"UPDATE queue_state"
" SET current ="
" CASE"
" WHEN current NOT NULL THEN 1"
" ELSE NULL"
" END;"
"COMMIT;";
SQLITE_EXEC(db, SQL, error);
return true;
error:
return false;
}
bool
database_queue_list_abs(sqlite3 *db, unsigned int from, unsigned int to, void *args,
database_callback callback)
{
const char *SQL_STMT =
"WITH content AS ("
" SELECT kara.id AS id, string, LENGTH(CAST(kara.id AS TEXT)) AS len"
" FROM queue_"
" JOIN kara ON kara_id = kara.id"
" WHERE position >= ? AND position <= ?"
" GROUP BY position ORDER BY position)"
"SELECT id, string, (SELECT MAX(len) FROM content)"
" FROM content;";
int code, id, id_len;
const char *row;
bool ret = false;
sqlite3_stmt *stmt;
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
SQLITE_BIND_INT(db, stmt, 1, from, error);
SQLITE_BIND_INT(db, stmt, 2, to, error);
for (;;) {
code = sqlite3_step(stmt);
if (code == SQLITE_ROW) {
id = sqlite3_column_int(stmt, 0);
row = (const char *) sqlite3_column_text(stmt, 1);
id_len = sqlite3_column_int(stmt, 2);
if (callback(args, id, id_len, row))
continue;
else
break;
}
else if (code == SQLITE_OK || code == SQLITE_DONE)
goto done;
else
break;
}
done:
ret = true;
error:
sqlite3_finalize(stmt);
return ret;
}
bool
database_queue_list_from(sqlite3 *db, unsigned int count, void *args,
database_callback callback)
{
const char *SQL_TEMPLATE =
"WITH content AS ("
" SELECT kara.id AS id, string, LENGTH(CAST(kara.id AS TEXT)) AS len"
" FROM queue_"
" JOIN queue_state ON queue_.position >= CASE"
" WHEN queue_state.current IS NULL THEN 1"
" ELSE queue_state.current END"
" JOIN kara ON kara_id = kara.id"
" GROUP BY position ORDER BY position LIMIT %d)"
"SELECT id, string, (SELECT MAX(len) FROM content)"
" FROM content;";
static const int stmt_len = 512;
char SQL_STMT[stmt_len];
int code, id, id_len;
const char *row;
bool ret = false;
sqlite3_stmt *stmt;
snprintf(SQL_STMT, stmt_len, SQL_TEMPLATE, count);
SQLITE_PREPARE(db, stmt, SQL_STMT, error);
for (;;) {
code = sqlite3_step(stmt);
if (code == SQLITE_ROW) {
id = sqlite3_column_int(stmt, 0);
row = (const char *) sqlite3_column_text(stmt, 1);
id_len = sqlite3_column_int(stmt, 2);
if (callback(args, id, id_len, row))
continue;
else
break;
}
else if (code == SQLITE_OK || code == SQLITE_DONE)
goto done;
else
break;
}
done:
ret = true;
error:
sqlite3_finalize(stmt);
return ret;
}