Simple, unified database interface
parent
355b5648d7
commit
5aa18256bc
|
@ -0,0 +1,2 @@
|
|||
default
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
minetest.register_on_chat_message( function(name, message)
|
||||
|
||||
local meta_name = "lastmessage"
|
||||
|
||||
if message:sub(1,1) == '/' then
|
||||
|
||||
local cmd = "/lastmessage"
|
||||
local s
|
||||
|
||||
if message:sub(0, #cmd) == cmd then
|
||||
local v = minetest.get_player_meta(name,meta_name,"string")
|
||||
if v then
|
||||
minetest.chat_send_player(name, 'Your last message: '..v)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
minetest.set_player_meta(name,meta_name,"string",message)
|
||||
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
-- minetest.register_on_chat_message( function(name, message)
|
||||
-- print("test-jachoo: name="..dump(name).." message="..dump(message))
|
||||
|
||||
-- -- types: string, int, double, bool, v3s16, v3f
|
||||
|
||||
-- -- local t = {string="a", int=0, double=0.0, bool=true, v3s16={x=1,y=10,z=100}, v3f={x=1.1,y=2.2,z=3.3}}
|
||||
|
||||
-- -- for s,def in pairs(t) do
|
||||
-- -- print("wczytuje ["..s.."]")
|
||||
-- -- local v = minetest.get_player_meta(name,"test-"..s,s)
|
||||
-- -- print(v)
|
||||
-- -- end
|
||||
|
||||
-- -- for s,def in pairs(t) do
|
||||
-- -- print("zapisuje ["..s.."]")
|
||||
-- -- minetest.set_player_meta(name,"test-"..s,s,def)
|
||||
-- -- end
|
||||
|
||||
|
||||
-- local v = nil
|
||||
-- local t = nil
|
||||
|
||||
-- t = "string"
|
||||
-- v = minetest.get_player_meta(name,"test-"..t,t)
|
||||
-- if v == nil
|
||||
-- then v = "a"
|
||||
-- else
|
||||
-- print("odczytano ["..t.."] = "..v)
|
||||
-- v = v .. "a"
|
||||
-- end
|
||||
-- minetest.set_player_meta(name,"test-"..t,t,v)
|
||||
|
||||
-- t = "int"
|
||||
-- v = minetest.get_player_meta(name,"test-"..t,t)
|
||||
-- if v == nil
|
||||
-- then v = 0
|
||||
-- else
|
||||
-- print("odczytano ["..t.."] = "..v)
|
||||
-- v = v + 1
|
||||
-- end
|
||||
-- minetest.set_player_meta(name,"test-"..t,t,v)
|
||||
|
||||
-- t = "double"
|
||||
-- v = minetest.get_player_meta(name,"test-"..t,t)
|
||||
-- if v == nil
|
||||
-- then v = 0.1
|
||||
-- else
|
||||
-- print("odczytano ["..t.."] = "..v)
|
||||
-- v = v + 1.1
|
||||
-- end
|
||||
-- minetest.set_player_meta(name,"test-"..t,t,v)
|
||||
|
||||
-- t = "bool"
|
||||
-- v = minetest.get_player_meta(name,"test-"..t,t)
|
||||
-- if v == nil
|
||||
-- then v = true
|
||||
-- else
|
||||
-- local x
|
||||
-- if v then x = "T" else x = "F" end
|
||||
-- print("odczytano ["..t.."] = "..x)
|
||||
-- v = not v
|
||||
-- end
|
||||
-- minetest.set_player_meta(name,"test-"..t,t,v)
|
||||
|
||||
-- t = "v3s16"
|
||||
-- v = minetest.get_player_meta(name,"test-"..t,t)
|
||||
-- if v == nil
|
||||
-- then v = {x=1,y=2,z=3}
|
||||
-- else
|
||||
-- print("odczytano ["..t.."] = "..v.x..","..v.y..","..v.z)
|
||||
-- v = {x=v.x+1,y=v.y+1,z=v.z+1}
|
||||
-- end
|
||||
-- minetest.set_player_meta(name,"test-"..t,t,v)
|
||||
|
||||
-- t = "v3f"
|
||||
-- v = minetest.get_player_meta(name,"test-"..t,t)
|
||||
-- if v == nil
|
||||
-- then v = {x=1.1,y=2.2,z=3.3}
|
||||
-- else
|
||||
-- print("odczytano ["..t.."] = "..v.x..","..v.y..","..v.z)
|
||||
-- v = {x=v.x+1.1,y=v.y+1.1,z=v.z+1.1}
|
||||
-- end
|
||||
-- minetest.set_player_meta(name,"test-"..t,t,v)
|
||||
|
||||
-- t = "v3fpos"
|
||||
-- v = minetest.get_player_meta(name,"test-"..t,t)
|
||||
-- if v == nil
|
||||
-- then v = {x=1.1,y=-20000.2,z=30000.3}
|
||||
-- else
|
||||
-- print("odczytano ["..t.."] = "..v.x..","..v.y..","..v.z)
|
||||
-- v = {x=v.x+1.1,y=v.y+1.1,z=v.z+1.1}
|
||||
-- end
|
||||
-- minetest.set_player_meta(name,"test-"..t,t,v)
|
||||
|
||||
|
||||
-- end)
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program 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 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* Author: Jan Cychnerski */
|
||||
|
||||
#include "db.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* bool */
|
||||
|
||||
template<> const std::string DBTypeTraits<bool>::name = "INT";
|
||||
template<> void DBTypeTraits<bool>::getColumn(sqlite3_stmt *stmt, int iCol, bool& val)
|
||||
{
|
||||
val = sqlite3_column_int(stmt,iCol) != 0;
|
||||
}
|
||||
template<> void DBTypeTraits<bool>::bind(sqlite3_stmt* stmt, int num, const bool& val)
|
||||
{
|
||||
bool d = sqlite3_bind_int(stmt,num,val?1:0);
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
/* int */
|
||||
|
||||
template<> const std::string DBTypeTraits<int>::name = "INT";
|
||||
template<> void DBTypeTraits<int>::getColumn(sqlite3_stmt *stmt, int iCol, int& val)
|
||||
{
|
||||
val = sqlite3_column_int(stmt,iCol);
|
||||
}
|
||||
template<> void DBTypeTraits<int>::bind(sqlite3_stmt* stmt, int num, const int& val)
|
||||
{
|
||||
int d = sqlite3_bind_int(stmt,num,val);
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
/* u64, db_key */
|
||||
|
||||
template<> const std::string DBTypeTraits<u64>::name = "INT";
|
||||
template<> void DBTypeTraits<u64>::getColumn(sqlite3_stmt *stmt, int iCol, u64& val)
|
||||
{
|
||||
val = sqlite3_column_int64(stmt,iCol);
|
||||
}
|
||||
template<> void DBTypeTraits<u64>::bind(sqlite3_stmt* stmt, int num, const u64& val)
|
||||
{
|
||||
u64 d = sqlite3_bind_int64(stmt,num,val);
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
template<> const std::string DBTypeTraits<db_key>::name = "INT";
|
||||
template<> void DBTypeTraits<db_key>::getColumn(sqlite3_stmt *stmt, int iCol, db_key& val)
|
||||
{
|
||||
val = sqlite3_column_int64(stmt,iCol);
|
||||
}
|
||||
template<> void DBTypeTraits<db_key>::bind(sqlite3_stmt* stmt, int num, const db_key& val)
|
||||
{
|
||||
db_key d = sqlite3_bind_int64(stmt,num,val);
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
/* v3s16 */
|
||||
|
||||
template<> const std::string DBTypeTraits<v3s16>::name = "INT";
|
||||
template<> void DBTypeTraits<v3s16>::getColumn(sqlite3_stmt *stmt, int iCol, v3s16& val)
|
||||
{
|
||||
val = DBKey( sqlite3_column_int64(stmt,iCol) );
|
||||
}
|
||||
template<> void DBTypeTraits<v3s16>::bind(sqlite3_stmt* stmt, int num, const v3s16& val)
|
||||
{
|
||||
int d = sqlite3_bind_int64(stmt,num,DBKey(val));
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
/* v2s16 */
|
||||
|
||||
template<> const std::string DBTypeTraits<v2s16>::name = "INT";
|
||||
template<> void DBTypeTraits<v2s16>::getColumn(sqlite3_stmt *stmt, int iCol, v2s16 & val)
|
||||
{
|
||||
val = DBKey( sqlite3_column_int64(stmt,iCol) );
|
||||
}
|
||||
template<> void DBTypeTraits<v2s16>::bind(sqlite3_stmt* stmt, int num, const v2s16& val)
|
||||
{
|
||||
int d = sqlite3_bind_int64(stmt,num,DBKey(val));
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
/* v3f */
|
||||
|
||||
template<> const std::string DBTypeTraits<v3f>::name = "BLOB";
|
||||
template<> void DBTypeTraits<v3f>::getColumn(sqlite3_stmt *stmt, int iCol, v3f& val)
|
||||
{
|
||||
if(sqlite3_column_bytes(stmt,iCol) != 12) throw DatabaseException("Value is not v3f");
|
||||
const f32* data = (const f32*)sqlite3_column_blob(stmt,iCol);
|
||||
val.X = data[0];
|
||||
val.Y = data[1];
|
||||
val.Z = data[2];
|
||||
}
|
||||
template<> void DBTypeTraits<v3f>::bind(sqlite3_stmt* stmt, int num, const v3f& val)
|
||||
{
|
||||
const float data[3] = {val.X, val.Y, val.Z};
|
||||
int d = sqlite3_bind_blob(stmt,num,data,12,SQLITE_TRANSIENT);
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
/* string - stored as BLOB (!) */
|
||||
|
||||
template<> const std::string DBTypeTraits<std::string>::name = "BLOB";
|
||||
template<> void DBTypeTraits<std::string>::getColumn(sqlite3_stmt *stmt, int iCol, std::string& val)
|
||||
{
|
||||
const char * data = (const char *)sqlite3_column_blob(stmt,iCol);
|
||||
size_t len = sqlite3_column_bytes(stmt,iCol);
|
||||
|
||||
val.assign(data,len);
|
||||
}
|
||||
template<> void DBTypeTraits<std::string>::bind(sqlite3_stmt* stmt, int num, const std::string& val)
|
||||
{
|
||||
int d = sqlite3_bind_blob(stmt,num,val.c_str(),val.length(),NULL);
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
/* float */
|
||||
|
||||
template<> const std::string DBTypeTraits<float>::name = "REAL";
|
||||
template<> void DBTypeTraits<float>::getColumn(sqlite3_stmt *stmt, int iCol, float& val)
|
||||
{
|
||||
val = (float)sqlite3_column_double(stmt,iCol);
|
||||
}
|
||||
template<> void DBTypeTraits<float>::bind(sqlite3_stmt* stmt, int num, const float& val)
|
||||
{
|
||||
float d = sqlite3_bind_double(stmt,num,val);
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
/* double */
|
||||
|
||||
template<> const std::string DBTypeTraits<double>::name = "REAL";
|
||||
template<> void DBTypeTraits<double>::getColumn(sqlite3_stmt *stmt, int iCol, double& val)
|
||||
{
|
||||
val = sqlite3_column_double(stmt,iCol);
|
||||
}
|
||||
template<> void DBTypeTraits<double>::bind(sqlite3_stmt* stmt, int num, const double& val)
|
||||
{
|
||||
double d = sqlite3_bind_double(stmt,num,val);
|
||||
if(d != SQLITE_OK) throw DatabaseException("Bind error");
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,459 @@
|
|||
/*
|
||||
Minetest-c55
|
||||
Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program 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 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* Author: Jan Cychnerski */
|
||||
|
||||
#ifndef DB_HEADER
|
||||
#define DB_HEADER
|
||||
|
||||
#include <jmutex.h>
|
||||
#include <jmutexautolock.h>
|
||||
#include <jthread.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include "common_irrlicht.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
extern "C" {
|
||||
#include "sqlite3.h"
|
||||
}
|
||||
|
||||
#define DBTYPE_BASE 0
|
||||
#define DBTYPE_SERVER 1
|
||||
#define DBTYPE_CLIENT 2
|
||||
|
||||
class DatabaseException : public BaseException {
|
||||
public:
|
||||
DatabaseException() : BaseException("Database access error") {}
|
||||
DatabaseException(const char* msg) : BaseException(msg) {}
|
||||
};
|
||||
|
||||
//type of 64-bit database key
|
||||
typedef sqlite3_int64 db_key;
|
||||
|
||||
/* Some helper functions */
|
||||
|
||||
inline s32 unsignedToSigned(s32 i, s32 max_positive)
|
||||
{
|
||||
if(i < max_positive)
|
||||
return i;
|
||||
else
|
||||
return i - 2*max_positive;
|
||||
}
|
||||
|
||||
// modulo of a negative number does not work consistently in C
|
||||
inline db_key pythonmodulo(db_key i, db_key mod)
|
||||
{
|
||||
if(i >= 0)
|
||||
return i % mod;
|
||||
return mod - ((-i) % mod);
|
||||
}
|
||||
|
||||
inline v3s16 getIntegerAsBlock(db_key i)
|
||||
{
|
||||
s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
|
||||
i = (i - x) / 4096;
|
||||
s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
|
||||
i = (i - y) / 4096;
|
||||
s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
|
||||
return v3s16(x,y,z);
|
||||
}
|
||||
|
||||
inline v2s16 getIntegerAsSector(db_key i)
|
||||
{
|
||||
v3s16 v = getIntegerAsBlock(i);
|
||||
return v2s16(v.X,v.Z);
|
||||
}
|
||||
|
||||
inline db_key getBlockAsInteger(const v3s16& pos) {
|
||||
return (db_key)pos.Z*16777216 +
|
||||
(db_key)pos.Y*4096 + (db_key)pos.X;
|
||||
}
|
||||
|
||||
inline db_key getSectorAsInteger(const v2s16& pos) {
|
||||
return getBlockAsInteger(v3s16(pos.X,0,pos.Y));
|
||||
}
|
||||
|
||||
//database INT key type with implicit constructors
|
||||
struct DBKey {
|
||||
db_key i;
|
||||
|
||||
DBKey(const db_key& i):i(i){}
|
||||
DBKey(const u64& i):i(i){}
|
||||
DBKey(const v3s16& i):i(getBlockAsInteger(i)){}
|
||||
DBKey(const v2s16& i):i(getSectorAsInteger(i)){}
|
||||
|
||||
operator db_key() const { return i; }
|
||||
operator u64() const { return i; }
|
||||
operator v3s16() const { return getIntegerAsBlock(i); }
|
||||
operator v2s16() const { return getIntegerAsSector(i); }
|
||||
};
|
||||
|
||||
//for future use - BLOB data is stored in minetest in std::string for now
|
||||
typedef std::string binary_t;
|
||||
|
||||
//traits for types stored in databases
|
||||
//available: int, u64, db_key, v3s16, v2s16, std::string (maybe more in db.cpp)
|
||||
template<class T>
|
||||
struct DBTypeTraits {
|
||||
static const std::string name;
|
||||
typedef T t;
|
||||
static inline T getColumn(sqlite3_stmt* stmt, int iCol) { T v; getColumn(stmt,iCol,v); return v; }
|
||||
static void getColumn(sqlite3_stmt* stmt, int iCol, T& val);
|
||||
static void bind(sqlite3_stmt* stmt, int num, const T& val);
|
||||
};
|
||||
|
||||
|
||||
//traits for database _key_ types (for future use)
|
||||
template<class T>
|
||||
struct DBKeyTypeTraits : public DBTypeTraits<T> {};
|
||||
|
||||
//traits for database _data_ types (for future use)
|
||||
template<class T>
|
||||
struct DBDataTypeTraits : public DBTypeTraits<T> {};
|
||||
|
||||
|
||||
//base class for tables in db
|
||||
class ITable {
|
||||
public:
|
||||
ITable(sqlite3* database, const std::string& name, const std::string& key, const std::string& data, bool old_names = false):
|
||||
m_database(database),
|
||||
name(name),
|
||||
key_name(key),
|
||||
data_name(data),
|
||||
old_names(old_names)
|
||||
{
|
||||
assert(database);
|
||||
|
||||
create();
|
||||
|
||||
int d;
|
||||
std::string q;
|
||||
|
||||
std::string id_name = old_names ? "pos" : "id";
|
||||
|
||||
q = "SELECT `data` FROM `"+name+"` WHERE `"+id_name+"`=? LIMIT 1";
|
||||
d = sqlite3_prepare_v2(m_database,q.c_str(), -1, &m_read, NULL);
|
||||
if(d != SQLITE_OK) {
|
||||
//infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot prepare read statement");
|
||||
}
|
||||
|
||||
q = "REPLACE INTO `"+name+"` ("+id_name+",data) VALUES(?, ?)";
|
||||
d = sqlite3_prepare_v2(m_database,q.c_str(), -1, &m_write, NULL);
|
||||
if(d != SQLITE_OK) {
|
||||
//infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot prepare write statement");
|
||||
}
|
||||
|
||||
q = "SELECT `"+id_name+"` FROM `"+name+"`";
|
||||
d = sqlite3_prepare_v2(m_database,q.c_str(), -1, &m_list, NULL);
|
||||
if(d != SQLITE_OK) {
|
||||
//infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot prepare read statement");
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~ITable() {
|
||||
if(m_read)
|
||||
sqlite3_finalize(m_read);
|
||||
if(m_write)
|
||||
sqlite3_finalize(m_write);
|
||||
if(m_list)
|
||||
sqlite3_finalize(m_list);
|
||||
}
|
||||
|
||||
//creates the table or returns false if failed
|
||||
virtual bool createNoEx()
|
||||
{
|
||||
std::string id_name = old_names ? "pos" : "id";
|
||||
std::string query =
|
||||
"CREATE TABLE IF NOT EXISTS `" + name + "` ("
|
||||
"`"+id_name+"` " + key_name + " NOT NULL PRIMARY KEY,"
|
||||
"`data` " + data_name +
|
||||
");";
|
||||
|
||||
return exec(query);
|
||||
}
|
||||
|
||||
//creates the table or throws DatabaseException
|
||||
void create()
|
||||
{
|
||||
if(!createNoEx()) throw DatabaseException("Cannot create table!");
|
||||
}
|
||||
|
||||
//inserts or replaces data in row with given key
|
||||
//if failed, returns false
|
||||
template<class Key, class Data>
|
||||
bool put(const Key& key, const Data& data)
|
||||
{
|
||||
DBKeyTypeTraits<Key>::bind(m_write,1,key);
|
||||
DBDataTypeTraits<Data>::bind(m_write,2,data);
|
||||
int d = sqlite3_step(m_write);
|
||||
sqlite3_reset(m_write);
|
||||
return d == SQLITE_DONE;
|
||||
}
|
||||
|
||||
//loads data from row with given key to buffer 'data'
|
||||
//if failed, returns false ('data' will not be modified!)
|
||||
template<class Key, class Data>
|
||||
bool getNoEx(const Key& key, Data& data)
|
||||
{
|
||||
DBKeyTypeTraits<Key>::bind(m_read,1,key);
|
||||
|
||||
if(sqlite3_step(m_read) != SQLITE_ROW){
|
||||
sqlite3_reset(m_read);
|
||||
return false;
|
||||
}
|
||||
|
||||
DBDataTypeTraits<Data>::getColumn(m_read,0,data);
|
||||
|
||||
sqlite3_reset(m_read);
|
||||
return true;
|
||||
}
|
||||
|
||||
//loads and returns data from row with given key
|
||||
//if failed (i.e. key doesn't exist) throws DatabaseException
|
||||
//NOTE: reversed template arguments!
|
||||
template<class Data,class Key>
|
||||
Data get(const Key& key)
|
||||
{
|
||||
Data d;
|
||||
if(!getNoEx(key,d)) throw DatabaseException("Database read error");
|
||||
return d;
|
||||
}
|
||||
|
||||
//inserts all ids from tabale to given list
|
||||
template<class Key>
|
||||
bool getKeys(core::list<Key>& list)
|
||||
{
|
||||
while(sqlite3_step(m_list) == SQLITE_ROW)
|
||||
{
|
||||
Key key = DBKeyTypeTraits<Key>::getColumn(m_list,0);
|
||||
list.push_back(key);
|
||||
}
|
||||
sqlite3_reset(m_list);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string name; //name of the table
|
||||
const std::string key_name; //name of sqlite key type
|
||||
const std::string data_name; //name of sqlite data type
|
||||
const bool old_names; //if true, primary key has name 'pos' and not 'id'
|
||||
|
||||
protected:
|
||||
sqlite3* m_database;
|
||||
sqlite3_stmt *m_read;
|
||||
sqlite3_stmt *m_write;
|
||||
sqlite3_stmt *m_list;
|
||||
|
||||
bool exec(const std::string& query)
|
||||
{
|
||||
assert(m_database);
|
||||
int e = sqlite3_exec(m_database,query.c_str(), NULL, NULL, NULL);
|
||||
return e == SQLITE_OK;
|
||||
}
|
||||
private:
|
||||
ITable(const ITable&); //disable copy constructor
|
||||
};
|
||||
|
||||
//template class for tables in database
|
||||
template<class Key, class Data = void>
|
||||
class Table : protected ITable {
|
||||
public:
|
||||
Table(sqlite3* database, const std::string& name, bool old_names = false):
|
||||
ITable(database, name, key_traits::name, data_traits::name, old_names)
|
||||
{}
|
||||
|
||||
//inserts or replaces data in row with given key
|
||||
//if failed, returns false
|
||||
bool put(const Key& key, const Data& data)
|
||||
{
|
||||
return ITable::put(key,data);
|
||||
}
|
||||
|
||||
//loads data from row with given key to buffer 'data'
|
||||
//if failed, returns false ('data' will not be modified!)
|
||||
bool getNoEx(const Key& key, Data& data)
|
||||
{
|
||||
return ITable::getNoEx(key,data);
|
||||
}
|
||||
|
||||
//loads and returns data from row with given key
|
||||
//if failed (i.e. key doesn't exist) throws DatabaseException
|
||||
inline Data get(const Key& key)
|
||||
{
|
||||
return ITable::get<Data>(key);
|
||||
}
|
||||
|
||||
//inserts all ids from tabale to given list
|
||||
bool getKeys(core::list<Key>& list)
|
||||
{
|
||||
return ITable::getKeys(list);
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef DBKeyTypeTraits<Key> key_traits;
|
||||
typedef DBDataTypeTraits<Data> data_traits;
|
||||
};
|
||||
|
||||
//template class for tables in database
|
||||
template<class Key>
|
||||
class Table<Key,void> : protected ITable {
|
||||
public:
|
||||
Table(sqlite3* database, const std::string& name, const std::string& data_type, bool old_names = false):
|
||||
ITable(database, name, key_traits::name, data_type, old_names)
|
||||
{}
|
||||
|
||||
//inserts or replaces data in row with given key
|
||||
//if failed, returns false
|
||||
template<class Data>
|
||||
bool put(const Key& key, const Data& data)
|
||||
{
|
||||
return ITable::put(key,data);
|
||||
}
|
||||
|
||||
//loads data from row with given key to buffer 'data'
|
||||
//if failed, returns false ('data' will not be modified!)
|
||||
template<class Data>
|
||||
bool getNoEx(const Key& key, Data& data)
|
||||
{
|
||||
return ITable::getNoEx(key,data);
|
||||
}
|
||||
|
||||
//loads and returns data from row with given key
|
||||
//if failed (i.e. key doesn't exist) throws DatabaseException
|
||||
template<class Data>
|
||||
inline Data get(const Key& key)
|
||||
{
|
||||
return ITable::get<Data>(key);
|
||||
}
|
||||
|
||||
//inserts all ids from tabale to given list
|
||||
bool getKeys(core::list<Key>& list)
|
||||
{
|
||||
return ITable::getKeys(list);
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef DBKeyTypeTraits<Key> key_traits;
|
||||
};
|
||||
|
||||
//database interface
|
||||
class Database {
|
||||
public:
|
||||
Database(const std::string& file)
|
||||
{
|
||||
m_is_new = false;
|
||||
|
||||
int d = sqlite3_open_v2(file.c_str(), &m_database, SQLITE_OPEN_READWRITE, NULL);
|
||||
|
||||
if(d != SQLITE_OK) {
|
||||
//can't open a file. try to create it.
|
||||
m_is_new = true;
|
||||
d = sqlite3_open_v2(file.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
||||
}
|
||||
|
||||
if(d != SQLITE_OK) {
|
||||
//infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot create/open database file");
|
||||
}
|
||||
}
|
||||
|
||||
~Database()
|
||||
{
|
||||
tables.clear(); //finalize all queries to tables
|
||||
if(m_database)
|
||||
sqlite3_close(m_database);
|
||||
}
|
||||
|
||||
//creates or loads a table with given key type, data type and name
|
||||
//if old_names=true, then primary key will have name 'pos' instead of 'id'
|
||||
//BE CAREFULL! if in the database exists a table with another key/data types - result is unpredictable!
|
||||
//sometimes it may then throw DatabaseException, but don't rely on this!
|
||||
template<class Key, class Data>
|
||||
Table<Key,Data>& getTable(const std::string& name, bool old_names = false)
|
||||
{
|
||||
SharedPtr<ITable>& ptr = tables[name];
|
||||
if(ptr==NULL)
|
||||
ptr = (ITable*) new Table<Key,Data>(m_database,name,old_names);
|
||||
|
||||
if(typeid(Table<Key,Data>) != typeid(*ptr)) throw DatabaseException("Wrong key/data type(s)!");
|
||||
|
||||
return (Table<Key,Data>&)*ptr;
|
||||
}
|
||||
|
||||
//creates or loads a table with given key type, data type and name
|
||||
//if old_names=true, then primary key will have name 'pos' instead of 'id'
|
||||
//BE CAREFULL! if in the database exists a table with another key/data types - result is unpredictable!
|
||||
//sometimes it may then throw DatabaseException, but don't rely on this!
|
||||
template<class Key>
|
||||
Table<Key>& getTable(const std::string& name, bool old_names = false)
|
||||
{
|
||||
SharedPtr<ITable>& ptr = tables[name];
|
||||
if(ptr==NULL)
|
||||
ptr = (ITable*) new Table<Key>(m_database,name,"BLOB",old_names);
|
||||
|
||||
if(typeid(Table<Key>) != typeid(*ptr)) throw DatabaseException("Wrong key/data type(s)!");
|
||||
|
||||
return (Table<Key>&)*ptr;
|
||||
}
|
||||
|
||||
//creates or loads a typeless table
|
||||
//if old_names=true, then primary key will have name 'pos' instead of 'id'
|
||||
ITable& getTable(const std::string& name, bool old_names = false)
|
||||
{
|
||||
SharedPtr<ITable>& ptr = tables[name];
|
||||
if(ptr==NULL)
|
||||
ptr = new ITable(m_database,name,"BLOB","BLOB",old_names);
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
//begins a transaction
|
||||
void begin()
|
||||
{
|
||||
sqlite3_exec(m_database,"BEGIN;", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
//commits a transaction
|
||||
void commit()
|
||||
{
|
||||
sqlite3_exec(m_database,"COMMIT;", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
//returns true if database was created from scratch (i.e. no database file existed before)
|
||||
inline bool isNew() const
|
||||
{
|
||||
return m_is_new;
|
||||
}
|
||||
|
||||
private:
|
||||
sqlite3* m_database;
|
||||
std::map<std::string,SharedPtr<ITable> > tables;
|
||||
bool m_is_new;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue