Files
bongo/src/libs/msgapi/sqldb.c
T

301 lines
5.8 KiB
C

/* Bongo Project licensing applies to this file, see COPYING
* (C) 2006 Novell
* (C) 2007 Alex Hudson
*/
/** \file
* SQLite Helper API
*/
/* This code all adapted from the Store, but made generic.
* Eventually, the store should probably use this
*/
#include <xpl.h>
#include <msgapi.h>
#include <sqlite3.h>
MsgSQLHandle *
MsgSQLOpen(char *path, BongoMemStack *memstack, int locktimeoutms)
{
MsgSQLHandle *handle = NULL;
int create = 0;
create = access(path, 0);
if (!(handle = MemMalloc(sizeof(MsgSQLHandle))) ||
memset(handle, 0, sizeof(MsgSQLHandle)), 0 ||
SQLITE_OK != sqlite3_open(path, &handle->db)) {
// FIXME Logging
XplConsolePrintf("msgapi: Failed to open database \"%s\".\r\n", path);
goto fail;
}
handle->memstack = memstack;
handle->lockTimeoutMs = locktimeoutms;
// FIXME
/* if (create && DStoreCreateDB(handle)) {
printf("Couldn't open db");
goto fail;
}
if (UpgradeDB(handle)) {
goto fail;
}
*/
return handle;
fail:
if (handle) {
if (handle->db) {
sqlite3_close(handle->db);
}
MemFree(handle);
}
return NULL;
}
void
MsgSQLClose(MsgSQLHandle *handle)
{
MsgSQLReset(handle);
if (SQLITE_BUSY == sqlite3_close(handle->db)) {
// FIXME logging
XplConsolePrintf ("msgapi: couldn't close database\r\n");
}
MemFree(handle);
}
void
MsgSQLFinalize(MsgSQLStatement *stmt)
{
if (stmt->stmt) {
sqlite3_finalize(stmt->stmt);
stmt->stmt = NULL;
}
}
void
MsgSQLReset(MsgSQLHandle *handle)
{
MsgSQLStatement *stmt;
for (stmt = &handle->stmts.begin;
stmt <= &handle->stmts.end;
stmt++)
{
if (stmt->stmt) {
sqlite3_finalize(stmt->stmt);
stmt->stmt = NULL;
}
}
}
BongoMemStack *
MsgSQLGetMemStack(MsgSQLHandle *handle)
{
return handle->memstack;
}
void
MsgSQLSetMemStack(MsgSQLHandle *handle, BongoMemStack *memstack)
{
handle->memstack = memstack;
}
void
MsgSQLSetLockTimeout(MsgSQLHandle *handle, int timeoutms)
{
handle->lockTimeoutMs = timeoutms;
}
// returns 0 on success, -2 db busy, -1 on error
int
MsgSQLBeginTransaction(MsgSQLHandle *handle)
{
MsgSQLStatement *stmt;
int result;
int count = 1 + handle->lockTimeoutMs / MSGSQL_STMT_SLEEP_MS;
if (handle->transactionDepth) {
/* FIXME: ensure it's an exclusive transaction */
abort ();
return 0;
}
stmt = MsgSQLPrepare(handle, "BEGIN EXCLUSIVE TRANSACTION;", &handle->stmts.begin);
if (!stmt) {
//DStoreStmtError(handle, stmt);
return -1;
}
do {
result = sqlite3_step(stmt->stmt);
sqlite3_reset(stmt->stmt);
} while (SQLITE_BUSY == result && --count && (XplDelay(MSGSQL_STMT_SLEEP_MS), 1));
switch (result) {
case SQLITE_DONE:
++handle->transactionDepth;
return 0;
case SQLITE_BUSY:
return -2;
default:
//DStoreStmtError(handle, stmt);
return -1;
}
}
// returns 0 on success, -1 on error
int
MsgSQLCommitTransaction(MsgSQLHandle *handle)
{
MsgSQLStatement *stmt;
int result;
int count = 1 + handle->lockTimeoutMs / MSGSQL_STMT_SLEEP_MS;
stmt = MsgSQLPrepare(handle, "END TRANSACTION;", &handle->stmts.end);
if (!stmt) {
// DStoreStmtError(handle, stmt);
return -1;
}
do {
result = sqlite3_step(stmt->stmt);
sqlite3_reset(stmt->stmt);
} while (SQLITE_BUSY == result && --count && (XplDelay(MSGSQL_STMT_SLEEP_MS), 1));
--handle->transactionDepth;
if (SQLITE_DONE != result) {
// DStoreStmtError(handle, stmt);
return -1;
}
return 0;
}
// returns 0 on success, -1 on error
int
MsgSQLAbortTransaction(MsgSQLHandle *handle)
{
MsgSQLStatement *stmt;
int result;
stmt = MsgSQLPrepare(handle, "ROLLBACK TRANSACTION;", &handle->stmts.abort);
if (!stmt) {
// DStoreStmtError(handle, stmt);
return -1;
}
result = sqlite3_step(stmt->stmt);
if (SQLITE_DONE != result) {
// DStoreStmtError(handle, stmt);
}
sqlite3_reset(stmt->stmt);
--handle->transactionDepth;
return SQLITE_DONE == result ? 0 : -1;
}
int
MsgSQLCancelTransactions(MsgSQLHandle *handle)
{
while (handle->transactionDepth) {
if (MsgSQLAbortTransaction(handle)) {
return -1;
}
}
return 0;
}
MsgSQLStatement *
MsgSQLPrepare(MsgSQLHandle *handle, const char *statement, MsgSQLStatement *stmt)
{
int count = 1 + handle->lockTimeoutMs / MSGSQL_STMT_SLEEP_MS;
// stmt->filter = NULL;
if (stmt->stmt) {
return stmt;
}
while (count--) {
switch (sqlite3_prepare(handle->db, statement, -1, &stmt->stmt, NULL)) {
case SQLITE_OK:
return stmt;
case SQLITE_BUSY:
XplDelay(MSGSQL_STMT_SLEEP_MS);
continue;
default:
// FIXME: should be logging here.
XplConsolePrintf("SQL Prepare statement \"%s\" failed; %s\r\n",
statement, sqlite3_errmsg(handle->db));
return NULL;
}
}
return NULL;
}
int
MsgSQLBindString(MsgSQLStatement *stmt, int var, const char *str, BOOL nullify)
{
if (str) {
return sqlite3_bind_text(stmt->stmt, var, str, strlen(str), SQLITE_STATIC);
} else {
if (nullify) {
return sqlite3_bind_null(stmt->stmt, var);
} else {
return sqlite3_bind_text(stmt->stmt, var, "", 0, SQLITE_STATIC);
}
}
}
int
MsgSQLExecute(MsgSQLHandle *handle, MsgSQLStatement *_stmt)
{
sqlite3_stmt *stmt = _stmt->stmt;
int result;
result = sqlite3_step(stmt);
if (SQLITE_DONE == result) {
return 0;
} else {
XplConsolePrintf("Sql (%d): %s\r\n", result, sqlite3_errmsg(handle->db));
return -1;
}
}
int
MsgSQLResults(MsgSQLHandle *handle, MsgSQLStatement *_stmt)
{
sqlite3_stmt *stmt = _stmt->stmt;
int result;
int count = 1 + handle->lockTimeoutMs / MSGSQL_STMT_SLEEP_MS;
while (count--) {
result = sqlite3_step(stmt);
switch (result) {
case SQLITE_DONE:
return 0; // no more results
break;
case SQLITE_ROW:
return 1;
break;
case SQLITE_BUSY:
XplDelay(MSGSQL_STMT_SLEEP_MS);
continue;
default:
// FIXME
//XplConsolePrintf("SQL Prepare statement \"%s\" failed; %s\r\n",
// statement, sqlite3_errmsg(handle->db));
return -1;
}
}
return -1;
}