/*
* This file is part of GNUnet
* (C) 2009, 2011 Christian Grothoff (and other contributing authors)
*
* GNUnet 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 3, or (at your
* option) any later version.
*
* GNUnet 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.
*
* You should have received a copy of the GNU General Public License
* along with GNUnet; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* @file datastore/plugin_datastore_sqlite.c
* @brief sqlite-based datastore backend
* @author Christian Grothoff
*/
#include "platform.h"
#include "gnunet_datastore_plugin.h"
#include <sqlite3.h>
/**
* We allocate items on the stack at times. To prevent a stack
* overflow, we impose a limit on the maximum size for the data per
* item. 64k should be enough.
*/
#define MAX_ITEM_SIZE 65536
/**
* After how many ms "busy" should a DB operation fail for good?
* A low value makes sure that we are more responsive to requests
* (especially PUTs). A high value guarantees a higher success
* rate (SELECTs in iterate can take several seconds despite LIMIT=1).
*
* The default value of 250ms should ensure that users do not experience
* huge latencies while at the same time allowing operations to succeed
* with reasonable probability.
*/
#define BUSY_TIMEOUT_MS 250
/**
* Log an error message at log-level 'level' that indicates
* a failure of the command 'cmd' on file 'filename'
* with the message given by strerror(errno).
*/
#define LOG_SQLITE(db, msg, level, cmd) do { GNUNET_log_from (level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); if (msg != NULL) GNUNET_asprintf(msg, _("`%s' failed at %s:%u with error: %s"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
/**
* Context for all functions in this plugin.
*/
struct Plugin
{
/**
* Our execution environment.
*/
struct GNUNET_DATASTORE_PluginEnvironment *env;
/**
* Database filename.
*/
char *fn;
/**
* Native SQLite database handle.
*/
sqlite3 *dbh;
/**
* Precompiled SQL for deletion.
*/
sqlite3_stmt *delRow;
/**
* Precompiled SQL for update.
*/
sqlite3_stmt *updPrio;
/**
* Get maximum repl value in database.
*/
sqlite3_stmt *maxRepl;
/**
* Precompiled SQL for replication decrement.
*/
sqlite3_stmt *updRepl;
/**
* Precompiled SQL for replication selection.
*/
sqlite3_stmt *selRepl;
/**
* Precompiled SQL for expiration selection.
*/
sqlite3_stmt *selExpi;
/**
* Precompiled SQL for expiration selection.
*/
sqlite3_stmt *selZeroAnon;
/**
* Precompiled SQL for insertion.
*/
sqlite3_stmt *insertContent;
/**
* Should the database be dropped on shutdown?
*/
int drop_on_shutdown;
};
/**
* @brief Prepare a SQL statement
*
* @param dbh handle to the database
* @param zSql SQL statement, UTF-8 encoded
* @param ppStmt set to the prepared statement
* @return 0 on success
*/
static int
sq_prepare (sqlite3 * dbh, const char *zSql, sqlite3_stmt ** ppStmt)
{
char *dummy;
int result;
result =
sqlite3_prepare_v2 (dbh, zSql, strlen (zSql), ppStmt,
(const char **) &dummy);
GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
"Prepared `%s' / %p: %d\n", zSql, *ppStmt, result);
return result;
}
/**
* Create our database indices.
*
* @param dbh handle to the database
*/
static void
create_indices (sqlite3 * dbh)
{
/* create indices */
if ((SQLITE_OK !=
sqlite3_exec (dbh, "CREATE INDEX IF NOT EXISTS idx_hash ON gn090 (hash)",
NULL, NULL, NULL)) ||
(SQLITE_OK !=
sqlite3_exec (dbh,
"CREATE INDEX IF NOT EXISTS idx_hash_vhash ON gn090 (hash,vhash)",
NULL, NULL, NULL)) ||
(SQLITE_OK !=
sqlite3_exec (dbh,
"CREATE INDEX IF NOT EXISTS idx_expire_repl ON gn090 (expire ASC,repl DESC)",
NULL, NULL, NULL)) ||
(SQLITE_OK !=
sqlite3_exec (