/* * File: sqldbm.cc * Summary: dbm wrapper for SQLite * Written by: Darshan Shaligram */ #include "AppHdr.h" #include "sqldbm.h" #include "stuff.h" #include #ifdef USE_SQLITE_DBM SQL_DBM::SQL_DBM(const std::string &dbname, bool do_open) : error(), errc(SQLITE_OK), db(NULL), s_insert(NULL), s_query(NULL), s_iterator(NULL), dbfile(dbname) { if (do_open && !dbfile.empty()) open(); } SQL_DBM::~SQL_DBM() { close(); } int SQL_DBM::ec(int err) { if (err == SQLITE_OK) error.clear(); else if (db) error = sqlite3_errmsg(db); else error = "Unknown error"; return (errc = err); } bool SQL_DBM::is_open() const { return !!db; } int SQL_DBM::open(const std::string &s) { close(); if (!s.empty()) dbfile = s; if (!dbfile.empty()) { if (dbfile.find(".db") != dbfile.length() - 3) dbfile += ".db"; if (ec( sqlite3_open(dbfile.c_str(), &db) ) != SQLITE_OK) { std::string saveerr = error; int serrc = errc; close(); error = saveerr; errc = serrc; return (errc); } init_schema(); } else error = "No filename!"; return (errc); } int SQL_DBM::init_schema() { int err = ec(sqlite3_exec( db, "CREATE TABLE dbm (key STRING UNIQUE PRIMARY KEY," " value STRING);", NULL, NULL, NULL)); // Turn off auto-commit sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL); return (err); } void SQL_DBM::close() { if (db) { sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); finalise_query(&s_insert); finalise_query(&s_query); finalise_query(&s_iterator); sqlite3_close(db); db = NULL; } } int SQL_DBM::insert(const std::string &key, const std::string &value) { if (init_insert() != SQLITE_OK) return (errc); ec(sqlite3_bind_text(s_insert, 1, key.c_str(), -1, SQLITE_TRANSIENT)); if (errc != SQLITE_OK) return (errc); ec(sqlite3_bind_text(s_insert, 2, value.c_str(), -1, SQLITE_TRANSIENT)); if (errc != SQLITE_OK) return (errc); ec(sqlite3_step(s_insert)); sqlite3_reset(s_insert); return (errc); } int SQL_DBM::init_insert() { return s_insert? SQLITE_OK : prepare_query(&s_insert, "INSERT INTO dbm VALUES (?, ?)"); } std::string SQL_DBM::query(const std::string &key) { if (init_query() != SQLITE_OK) return (""); if (ec(sqlite3_bind_text(s_query, 1, key.c_str(), -1, SQLITE_TRANSIENT)) != SQLITE_OK) { return (""); } int err = SQLITE_OK; std::string res; while ((err = ec(sqlite3_step(s_query))) == SQLITE_ROW) res = (const char *) sqlite3_column_text(s_query, 0); sqlite3_reset(s_query); return (res); } std::auto_ptr SQL_DBM::firstkey() { if (init_iterator() != SQLITE_OK) { std::auto_ptr result; return (result); } return nextkey(); } std::auto_ptr SQL_DBM::nextkey() { std::auto_ptr result; if (s_iterator) { int err = SQLITE_OK; if ((err = ec(sqlite3_step(s_iterator))) == SQLITE_ROW) result.reset( new std::string( (const char *) sqlite3_column_text(s_iterator, 0) )); else sqlite3_reset(s_iterator); } return (result); } int SQL_DBM::init_query() { return s_query? SQLITE_OK : prepare_query(&s_query, "SELECT value FROM dbm WHERE key = ?"); } int SQL_DBM::init_iterator() { return s_iterator? SQLITE_OK : prepare_query(&s_iterator, "SELECT key FROM dbm"); } int SQL_DBM::finalise_query(sqlite3_stmt **q) { if (!*q) return (SQLITE_OK); sqlite3_reset(*q); int ret = ec(sqlite3_finalize(*q)); *q = NULL; return (ret); } int SQL_DBM::prepare_query(sqlite3_stmt **q, const char *sql) { if (*q) finalise_query(q); const char *query_tail; return ec(sqlite3_prepare_v2(db, sql, -1, q, &query_tail)); } //////////////////////////////////////////////////////////////////////// sql_datum::sql_datum() : dptr(NULL), dsize(0), need_free(false) { } sql_datum::sql_datum(const std::string &s) : dptr(NULL), dsize(s.length()), need_free(false) { if ((dptr = new char [dsize])) { if (dsize) memcpy(dptr, s.c_str(), dsize); need_free = true; } } sql_datum::sql_datum(const sql_datum &dat) : dptr(NULL), dsize(0), need_free(false) { init_from(dat); } sql_datum::~sql_datum() { reset(); } sql_datum &sql_datum::operator = (const sql_datum &d) { if (&d != this) { reset(); init_from(d); } return (*this); } void sql_datum::reset() { if (need_free) delete [] dptr; dptr = NULL; dsize = 0; } void sql_datum::init_from(const sql_datum &d) { dsize = d.dsize; need_free = false; if (d.need_free) { if ((dptr = new char [dsize])) { if (dsize) memcpy(dptr, d.dptr, dsize); need_free = true; } } else { need_free = false; dptr = d.dptr; } } std::string sql_datum::to_str() const { return std::string(dptr, dsize); } //////////////////////////////////////////////////////////////////////// SQL_DBM *dbm_open(const char *filename, int, int) { SQL_DBM *n = new SQL_DBM(filename, true); if (!n->is_open()) { delete n; return (NULL); } return (n); } int dbm_close(SQL_DBM *db) { delete db; return (0); } sql_datum dbm_fetch(SQL_DBM *db, const sql_datum &key) { std::string ans = db->query(std::string(key.dptr, key.dsize)); return sql_datum(ans); } static sql_datum dbm_key( SQL_DBM *db, std::auto_ptr (SQL_DBM::*key)()) { std::auto_ptr res = (db->*key)(); if (res.get()) return sql_datum(*res.get()); else { sql_datum dummy; return dummy; } } sql_datum dbm_firstkey(SQL_DBM *db) { return dbm_key(db, &SQL_DBM::firstkey); } sql_datum dbm_nextkey(SQL_DBM *db) { return dbm_key(db, &SQL_DBM::nextkey); } int dbm_store(SQL_DBM *db, const sql_datum &key, const sql_datum &value, int) { int err = db->insert(key.to_str(), value.to_str()); if (err == SQLITE_DONE || err == SQLITE_CONSTRAINT) err = SQLITE_OK; else end(1, false, "%d: %s", db->errc, db->error.c_str()); return (err); } #endif // USE_SQLITE_DBM