📄 kv_sqlite.c
字号:
/* This file is part of GNUnet. (C) 2006 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 2, 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 applications/kvstore_sqlite/kv_sqlite.c * @brief SQLite based implementation of the kvstore service * @author Nils Durner * @author Christian Grothoff * @todo Indexes, statistics * * Database: SQLite */#include "platform.h"#include "gnunet_util.h"#include "gnunet_directories.h"#include "gnunet_kvstore_service.h"#include <sqlite3.h>#define DEBUG_SQLITE GNUNET_NO/** * Die with an error message that indicates * a failure of the command 'cmd' with the message given * by strerror(errno). */#define DIE_SQLITE(dbh, cmd) do { GNUNET_GE_LOG(ectx, GNUNET_GE_FATAL | GNUNET_GE_ADMIN | GNUNET_GE_BULK, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(dbh)); abort(); } while(0);/** * 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(dbh, level, cmd) do { GNUNET_GE_LOG(ectx, GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(dbh)); } while(0);/** * @brief Wrapper for SQLite */typedef struct{ /** * Native SQLite database handle - may not be shared between threads! */ sqlite3 *dbh; /** * Thread ID owning this handle */ struct GNUNET_ThreadHandle *tid;} sqliteHandle;/** * @brief Information about the database */typedef struct{ /** * bytes used */ double payload; /** * name of the database */ char *name; /** * filename of this database */ char *fn; /** * List of open handles */ sqliteHandle **handles; /** * Open handles (one per thread) */ unsigned int handle_count; unsigned int lastSync;} sqliteDatabase;static GNUNET_CoreAPIForPlugins *coreAPI;static struct GNUNET_GE_Context *ectx;static unsigned int databases;static sqliteDatabase **dbs;static struct GNUNET_Mutex *lock;/** * @brief Encode a binary buffer "in" of size n bytes so that it contains * no instances of character '\000'. * @param in input * @param n size of in * @param out output */static intsqlite_encode_binary (const unsigned char *in, int n, unsigned char *out){ char c; unsigned char *start = out; n--; for (; n > -1; n--) { c = *in; in++; if (c == 0 || c == 1) { *out = 1; out++; *out = c + 1; } else { *out = c; } out++; } return (int) (out - start);}/** * @brief Decode the string "in" into binary data and write it into "out". * @param in input * @param out output * @param num size of the output buffer * @return number of output bytes, -1 on error */static intsqlite_decode_binary_n (const unsigned char *in, unsigned char *out, unsigned int num){ unsigned char *start = out; unsigned char *stop = (unsigned char *) (in + num); while (in != stop) { if (*in == 1) { in++; *out = *in - 1; } else *out = *in; in++; out++; } return (int) (out - start);}/** * @brief Prepare a SQL statement */static intsq_prepare (sqliteHandle * dbh, const char *zSql, /* SQL statement, UTF-8 encoded */ sqlite3_stmt ** ppStmt){ /* OUT: Statement handle */ char *dummy; return sqlite3_prepare (dbh->dbh, zSql, strlen (zSql), ppStmt, (const char **) &dummy);}/** * Get path to database file */static char *getDBFileName (const char *name){ char *dir; char *fn; size_t mem; GNUNET_GC_get_configuration_value_filename (coreAPI->cfg, "KEYVALUE_DATABASE", "DIR", GNUNET_DEFAULT_DAEMON_VAR_DIRECTORY "/kvstore/", &dir); GNUNET_disk_directory_create (ectx, dir); mem = strlen (dir) + strlen (name) + 6; fn = GNUNET_malloc (mem); GNUNET_snprintf (fn, mem, "%s/%s.dat", dir, name); GNUNET_free (dir); return fn;}/** * @brief Get information about an open database * @param name the name of the database */static sqliteDatabase *getDB (const char *name){ unsigned int idx; sqliteDatabase *db; for (idx = 0; idx < databases; idx++) if (0 == strcmp (dbs[idx]->name, name)) return dbs[idx]; db = GNUNET_malloc (sizeof (sqliteDatabase)); memset (db, 0, sizeof (sqliteDatabase)); db->fn = getDBFileName (name); db->name = GNUNET_strdup (name); GNUNET_array_append (dbs, databases, db); return db;}/** * @brief Get a database handle for this thread. * @note SQLite handles may no be shared between threads - see * http://permalink.gmane.org/gmane.network.gnunet.devel/1377 * We therefore (re)open the database in each thread. * @return the native SQLite database handle */static sqliteHandle *getDBHandle (const char *name){ unsigned int idx; sqliteHandle *dbh; sqliteDatabase *db; GNUNET_mutex_lock (lock); db = getDB (name); for (idx = 0; idx < db->handle_count; idx++) if (GNUNET_thread_test_self (db->handles[idx]->tid)) { sqliteHandle *ret = db->handles[idx]; GNUNET_mutex_unlock (lock); return ret; } /* we haven't opened the DB for this thread yet */ dbh = GNUNET_malloc (sizeof (sqliteHandle)); dbh->tid = GNUNET_thread_get_self (); if (sqlite3_open (db->fn, &dbh->dbh) != SQLITE_OK) { printf ("FN: %s\n", db->fn); LOG_SQLITE (dbh->dbh, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, "sqlite3_open"); sqlite3_close (dbh->dbh); GNUNET_mutex_unlock (lock); GNUNET_thread_release_self (dbh->tid); GNUNET_free (dbh); return NULL; } GNUNET_array_append (db->handles, db->handle_count, dbh); sqlite3_exec (dbh->dbh, "PRAGMA temp_store=MEMORY", NULL, NULL, NULL); sqlite3_exec (dbh->dbh, "PRAGMA synchronous=OFF", NULL, NULL, NULL); sqlite3_exec (dbh->dbh, "PRAGMA count_changes=OFF", NULL, NULL, NULL); sqlite3_exec (dbh->dbh, "PRAGMA page_size=4096", NULL, NULL, NULL); GNUNET_mutex_unlock (lock); return dbh;}static voidclose_database (sqliteDatabase * db){ unsigned int idx; for (idx = 0; idx < db->handle_count; idx++) { sqliteHandle *dbh = db->handles[idx]; GNUNET_thread_release_self (dbh->tid); if (sqlite3_close (dbh->dbh) != SQLITE_OK) LOG_SQLITE (dbh->dbh, LOG_ERROR, "sqlite_close"); GNUNET_free (dbh); } GNUNET_array_grow (db->handles, db->handle_count, 0); GNUNET_free (db->fn); GNUNET_free (db->name); GNUNET_free (db);}/** * @brief Delete the database. */static voiddropDatabase (const char *name){ sqliteDatabase *db; unsigned int idx; char *fn; GNUNET_mutex_lock (lock); for (idx = 0; idx < databases; idx++) { if (0 == strcmp (dbs[idx]->name, name)) { db = dbs[idx]; close_database (db); dbs[idx] = dbs[databases - 1]; GNUNET_array_grow (dbs, databases, databases - 1); break; } } fn = getDBFileName (name); UNLINK (fn); GNUNET_free (fn); GNUNET_mutex_unlock (lock);}/** * @brief Open a Key/Value-Table * @param table the name of the Key/Value-Table * @return a handle */static GNUNET_KeyValueRecord *getTable (const char *database, const char *table){ sqlite3_stmt *stmt; unsigned int len; GNUNET_KeyValueRecord *ret; sqliteHandle *dbh; char *idx; dbh = getDBHandle (database); if (dbh == NULL) return NULL; sq_prepare (dbh, "Select 1 from sqlite_master where tbl_name = ?", &stmt); len = strlen (table); sqlite3_bind_text (stmt, 1, table, len, SQLITE_STATIC); if (sqlite3_step (stmt) == SQLITE_DONE) { char *create = GNUNET_malloc (len + 58);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -