libretroshare src now compilable.

nxsitems not tested yet
renamed retrodb src file to .cc regular librs naming convention
wrote deserialisation section of nxsitems, need to write print and clear rsitem base functions

git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-new_cache_system@5144 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
chrisparker126 2012-05-05 19:55:24 +00:00
parent db8d019b3f
commit 50a0ed0656
6 changed files with 705 additions and 133 deletions
libretroshare/src/util

View file

@ -0,0 +1,898 @@
/*
* RetroShare : RetroDb functionality
*
* Copyright 2012 Christopher Evi-Parker
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License Version 2 as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Please report all bugs and problems to "retroshare@lunamutt.com".
*
*/
#include <iostream>
#include <sstream>
#include <memory.h>
#include <time.h>
#include "retrodb.h"
#define RETRODB_DEBUG
const uint8_t ContentValue::BOOL_TYPE = 1;
const uint8_t ContentValue::DATA_TYPE = 2;
const uint8_t ContentValue::STRING_TYPE = 3;
const uint8_t ContentValue::DOUBLE_TYPE = 4;
const uint8_t ContentValue::INT32_TYPE = 5;
const uint8_t ContentValue::INT64_TYPE = 6;
const int RetroDb::OPEN_READONLY = SQLITE_OPEN_READONLY;
const int RetroDb::OPEN_READWRITE = SQLITE_OPEN_READWRITE;
const int RetroDb::OPEN_READWRITE_CREATE = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
RetroDb::RetroDb(const std::string &dbPath, int flags) : mDb(NULL) {
int rc = sqlite3_open_v2(dbPath.c_str(), &mDb, flags, NULL);
if(rc){
std::cerr << "Can't open database, Error code: " << sqlite3_errmsg(mDb)
<< std::endl;
sqlite3_close(mDb);
}
}
RetroDb::~RetroDb(){
if(mDb)
sqlite3_close(mDb);
}
void RetroDb::closeDb(){
int rc;
if(mDb)
rc = sqlite3_close(mDb);
#ifdef RETRODB_DEBUG
std::cerr << "RetroDb::closeDb(): Error code on close: " << rc << std::endl;
#endif
}
#define TIME_LIMIT 3
bool RetroDb::execSQL(const std::string &query){
// prepare statement
sqlite3_stmt* stm = NULL;
#ifdef RETRODB_DEBUG
std::cerr << "Query: " << query << std::endl;
#endif
int rc = sqlite3_prepare_v2(mDb, query.c_str(), query.length(), &stm, NULL);
// check if there are any errors
if(rc != SQLITE_OK){
std::cerr << "RetroDb::execSQL(): Error preparing statement\n";
std::cerr << "Error code: " << sqlite3_errmsg(mDb)
<< std::endl;
return false;
}
uint32_t delta = 3;
time_t stamp = time(NULL), now = 0;
bool timeOut = false, ok = false;
while(!timeOut){
rc = sqlite3_step(stm);
if(rc == SQLITE_DONE){
ok = true;
break;
}
if(rc != SQLITE_BUSY){
ok = false;
break;
}
now = time(NULL);
delta = stamp - now;
if(delta > TIME_LIMIT){
ok = false;
timeOut = true;
}
// TODO add sleep so not to waste
// precious cycles
}
if(!ok){
if(rc == SQLITE_BUSY){
std::cerr << "RetroDb::execSQL()\n" ;
std::cerr << "SQL timed out!" << std::endl;
}else{
std::cerr << "RetroDb::execSQL(): Error executing statement (code: " << rc << ")\n";
std::cerr << "Sqlite Error msg: " << sqlite3_errmsg(mDb)
<< std::endl;
}
}
// finalise statement or else db cannot be closed
sqlite3_finalize(stm);
return ok;
}
RetroCursor* RetroDb::sqlQuery(const std::string& tableName, const std::list<std::string>& columns,
const std::string& selection, const std::string& orderBy){
if(tableName.empty() || columns.empty()){
std::cerr << "RetroDb::sqlQuery(): No table or columns given" << std::endl;
return NULL;
}
std::string columnSelection; // the column names to return
sqlite3_stmt* stmt = NULL;
std::list<std::string>::const_iterator it = columns.begin();
for(; it != columns.end(); it++){
columnSelection += *it;
it++;
if(it != columns.end())
columnSelection += ",";
it--;
}
// construct query
// SELECT columnSelection FROM tableName WHERE selection
std::string sqlQuery = "SELECT " + columnSelection + " FROM " +
tableName;
// add selection clause if present
if(!selection.empty())
sqlQuery += " WHERE " + selection;
// add 'order by' clause if present
if(!orderBy.empty())
sqlQuery += " ORDER BY " + orderBy + ";";
else
sqlQuery += ";";
#ifdef RETRODB_DEBUG
std::cerr << "RetroDb::sqlQuery(): " << sqlQuery << std::endl;
#endif
sqlite3_prepare_v2(mDb, sqlQuery.c_str(), sqlQuery.length(), &stmt, NULL);
return (new RetroCursor(stmt));
}
bool RetroDb::isOpen() const {
return (mDb==NULL ? false : true);
}
bool RetroDb::sqlInsert(const std::string &table, const std::string& nullColumnHack, const ContentValue &cv){
std::map<std::string, uint8_t> keyTypeMap;
cv.getKeyTypeMap(keyTypeMap);
std::map<std::string, uint8_t>::iterator mit = keyTypeMap.begin();
// build columns part of insertion
std::string qColumns = table + "(";
for(; mit != keyTypeMap.end(); mit++){
qColumns += mit->first;
mit++;
// add comma if more columns left
if(mit == keyTypeMap.end())
qColumns += ")"; // close bracket if at end
else
qColumns += ",";
mit--;
}
// build values part of insertion
std::string qValues = "VALUES(";
std::ostringstream oStrStream;
for(mit=keyTypeMap.begin(); mit!=keyTypeMap.end(); mit++){
uint8_t type = mit->second;
std::string key = mit->first;
switch(type){
case ContentValue::BOOL_TYPE:
{
bool value;
cv.getAsBool(key, value);
oStrStream << value;
qValues += oStrStream.str();
break;
}
case ContentValue::DOUBLE_TYPE:
{
double value;
cv.getAsDouble(key, value);
oStrStream << value;
qValues += oStrStream.str();
break;
}
case ContentValue::DATA_TYPE:
{
char* value;
uint32_t len;
cv.getAsData(key, len, value);
oStrStream.write(value, len);
qValues += "'" + oStrStream.str() + "'";
break;
}
case ContentValue::STRING_TYPE:
{
std::string value;
cv.getAsString(key, value);
qValues += "'" + value +"'";
break;
}
case ContentValue::INT32_TYPE:
{
int32_t value;
cv.getAsInt32(key, value);
oStrStream << value;
qValues += oStrStream.str();
break;
}
case ContentValue::INT64_TYPE:
{
int64_t value;
cv.getAsInt64(key, value);
oStrStream << value;
qValues += oStrStream.str();
break;
}
}
mit++;
if(mit != keyTypeMap.end()){ // add comma if more columns left
qValues += ",";
}
else{ // at end close brackets
qValues += ");";
}
mit--;
// clear stream strings
oStrStream.str("");
}
// complete insertion query
std::string sqlQuery = "INSERT INTO " + qColumns + " " + qValues;
#ifdef RETRODB_DEBUG
std::cerr << "RetroDb::sqlInsert(): " << sqlQuery << std::endl;
#endif
// execute query
execSQL(sqlQuery);
return true;
}
bool RetroDb::sqlDelete(const std::string &tableName, const std::string &whereClause, const std::string &whereArgs){
std::string sqlQuery = "DELETE FROM " + tableName;
if(!whereClause.empty()){
sqlQuery += " WHERE " + whereClause + ";";
}else
sqlQuery += ";";
return execSQL(sqlQuery);
}
bool RetroDb::sqlUpdate(const std::string &tableName, std::string whereClause, const ContentValue& cv){
std::string sqlQuery = "UPDATE " + tableName + " SET ";
std::map<std::string, uint8_t> keyTypeMap;
std::map<std::string, uint8_t>::iterator mit;
cv.getKeyTypeMap(keyTypeMap);
// build SET part of update
std::string qValues = "";
std::ostringstream oStrStream;
for(mit=keyTypeMap.begin(); mit!=keyTypeMap.end(); mit++){
uint8_t type = mit->second;
std::string key = mit->first;
switch(type){
case ContentValue::BOOL_TYPE:
{
bool value;
cv.getAsBool(key, value);
oStrStream << value;
qValues += key + "='" + oStrStream.str();
break;
}
case ContentValue::DOUBLE_TYPE:
{
double value;
cv.getAsDouble(key, value);
oStrStream << value;
qValues += key + "='" + oStrStream.str();
break;
}
case ContentValue::DATA_TYPE:
{
char* value;
uint32_t len;
cv.getAsData(key, len, value);
oStrStream.write(value, len);
qValues += key + "='" + oStrStream.str() + "' ";
break;
}
case ContentValue::STRING_TYPE:
{
std::string value;
cv.getAsString(key, value);
qValues += key + "='" + value + "' ";
break;
}
case ContentValue::INT32_TYPE:
{
int32_t value;
cv.getAsInt32(key, value);
oStrStream << value;
qValues += key + "='" + oStrStream.str() + "' ";
break;
}
case ContentValue::INT64_TYPE:
{
int64_t value;
cv.getAsInt64(key, value);
oStrStream << value;
qValues += key + "='" + oStrStream.str() + "' ";
break;
}
}
mit++;
if(mit != keyTypeMap.end()){ // add comma if more columns left
qValues += ",";
}
mit--;
// clear stream strings
oStrStream.str("");
}
if(qValues.empty())
return false;
else
sqlQuery += qValues;
// complete update
if(!whereClause.empty()){
sqlQuery += " WHERE " + whereClause + ";";
}
else{
sqlQuery += ";";
}
// execute query
return execSQL(sqlQuery);
}
/********************** RetroCursor ************************/
RetroCursor::RetroCursor(sqlite3_stmt *stmt)
: mStmt(NULL), mCount(0), mPosCounter(0) {
open(stmt);
}
RetroCursor::~RetroCursor(){
// finalise statement
if(mStmt){
sqlite3_finalize(mStmt);
}
}
bool RetroCursor::moveToFirst(){
#ifdef RETRODB_DEBUG
std::cerr << "RetroCursor::moveToFirst()\n";
#endif
if(!isOpen())
return -1;
// reset statement
int rc = sqlite3_reset(mStmt);
if(rc != SQLITE_OK){
#ifdef RETRODB_DEBUG
std::cerr << "Error code: " << rc << std::endl;
#endif
return false;
}
rc = sqlite3_step(mStmt);
if(rc == SQLITE_ROW){
mPosCounter = 0;
return true;
}
#ifdef RETRODB_DEBUG
std::cerr << "Error code: " << rc << std::endl;
#endif
return false;
}
bool RetroCursor::moveToLast(){
#ifdef RETRODB_DEBUG
std::cerr << "RetroCursor::moveToLast()\n";
#endif
if(!isOpen())
return -1;
// go to begining
int rc = sqlite3_reset(mStmt);
if(rc != SQLITE_OK)
return false;
rc = sqlite3_step(mStmt);
while(rc == SQLITE_ROW){
rc = sqlite3_step(mStmt);
}
if(rc != SQLITE_DONE){
std::cerr << "Error executing statement (code: " << rc << ")\n"
<< std::endl;
return false;
}else{
mPosCounter = mCount;
return true;
}
}
int RetroCursor::getResultCount() const {
if(isOpen())
return mCount;
else
return -1;
}
bool RetroCursor::isOpen() const {
return !(mStmt == NULL);
}
bool RetroCursor::close(){
if(!isOpen())
return false;
int rc = sqlite3_finalize(mStmt);
mStmt = NULL;
mPosCounter = 0;
mCount = 0;
return (rc == SQLITE_OK);
}
bool RetroCursor::open(sqlite3_stmt *stm){
#ifdef RETRODB_DEBUG
std::cerr << "RetroCursor::open() \n";
#endif
if(isOpen())
close();
mStmt = stm;
// ensure statement is valid
int rc = sqlite3_reset(mStmt);
if(rc == SQLITE_OK){
while((rc = sqlite3_step(mStmt)) == SQLITE_ROW)
mCount++;
sqlite3_reset(mStmt);
return true;
}
else{
std::cerr << "Error Opening cursor (code: " << rc << ")\n";
close();
return false;
}
}
bool RetroCursor::moveToNext(){
#ifdef RETRODB_DEBUG
std::cerr << "RetroCursor::moveToNext()\n";
#endif
if(!isOpen())
return false;
int rc = sqlite3_step(mStmt);
if(rc == SQLITE_ROW){
mPosCounter++;
return true;
}else if(rc == SQLITE_DONE){ // no more results
return false;
}
else if(rc == SQLITE_BUSY){ // should not enter here
std::cerr << "RetroDb::moveToNext()\n" ;
std::cerr << "Busy!, possible multiple accesses to Db" << std::endl
<< "serious error";
return false;
}else{
std::cerr << "Error executing statement (code: " << rc << ")\n";
return false;
}
}
int32_t RetroCursor::getPosition() const {
if(isOpen())
return mPosCounter;
else
return -1;
}
int32_t RetroCursor::getInt32(int columnIndex){
return sqlite3_column_int(mStmt, columnIndex);
}
int64_t RetroCursor::getInt64(int columnIndex){
return sqlite3_column_int64(mStmt, columnIndex);
}
bool RetroCursor::getBool(int columnIndex){
return sqlite3_column_int(mStmt, columnIndex);
}
double RetroCursor::getDouble(int columnIndex){
return sqlite3_column_double(mStmt, columnIndex);
}
void RetroCursor::getString(int columnIndex, std::string &str){
char* raw_str = (char*)sqlite3_column_text(mStmt, columnIndex);
if(raw_str != NULL)
str.assign(raw_str);
}
const void* RetroCursor::getData(int columnIndex, uint32_t &datSize){
const void* val = sqlite3_column_blob(mStmt, columnIndex);
datSize = sqlite3_column_bytes(mStmt, columnIndex);
return val;
}
/**************** content value implementation ******************/
typedef std::pair<std::string, uint8_t> KeyTypePair;
ContentValue::ContentValue(){
}
ContentValue::~ContentValue(){
}
ContentValue::ContentValue(ContentValue &from){
std::map<std::string, uint8_t> keyTypeMap;
from.getKeyTypeMap(keyTypeMap);
std::map<std::string, uint8_t>::const_iterator cit =
keyTypeMap.begin();
uint8_t type = 0;
std::string currKey;
std::string val = "";
char *src = NULL;
uint32_t data_len = 0;
for(; cit != keyTypeMap.end(); cit++){
type = cit->second;
currKey = cit->first;
switch(type){
case INT32_TYPE:
{
int32_t value;
from.getAsInt32(currKey, value);
put(currKey, value);
break;
}
case INT64_TYPE:
{
int64_t value;
from.getAsInt64(currKey, value);
put(currKey, value);
break;
}
case STRING_TYPE:
{
from.getAsString(currKey, val);
put(currKey, val);
break;
}
case BOOL_TYPE:
{
bool value;
from.getAsBool(currKey, value);
put(currKey, value);
break;
}
case DATA_TYPE:
{
from.getAsData(currKey, data_len, src);
put(currKey, data_len, src);
break;
}
case DOUBLE_TYPE:
double value;
from.getAsDouble(currKey, value);
put(currKey, value);
break;
default:
std::cerr << "ContentValue::ContentValue(ContentValue &from):"
<< "Error! Unrecognised data type!" << std::endl;
}
}
}
void ContentValue::put(const std::string &key, bool value){
if(mKvSet.find(key) != mKvSet.end())
removeKeyValue(key);
mKvSet.insert(KeyTypePair(key, BOOL_TYPE));
mKvBool.insert(std::pair<std::string, bool>(key, value));
}
void ContentValue::put(const std::string &key, const std::string &value){
if(mKvSet.find(key) != mKvSet.end())
removeKeyValue(key);
mKvSet.insert(KeyTypePair(key, STRING_TYPE));
mKvString.insert(std::pair<std::string, std::string>(key, value));
}
void ContentValue::put(const std::string &key, double value){
if(mKvSet.find(key) != mKvSet.end())
removeKeyValue(key);
mKvSet.insert(KeyTypePair(key,DOUBLE_TYPE));
mKvDouble.insert(std::pair<std::string, double>(key, value));
}
void ContentValue::put(const std::string &key, int32_t value){
if(mKvSet.find(key) != mKvSet.end())
removeKeyValue(key);
mKvSet.insert(KeyTypePair(key, INT32_TYPE));
mKvInt32.insert(std::pair<std::string, int32_t>(key, value));
}
void ContentValue::put(const std::string &key, int64_t value){
if(mKvSet.find(key) != mKvSet.end())
removeKeyValue(key);
mKvSet.insert(KeyTypePair(key, INT64_TYPE));
mKvInt64.insert(std::pair<std::string, int64_t>(key, value));
}
void ContentValue::put(const std::string &key, uint32_t len, const char* value){
// release memory from old key value if key
// exists
if(mKvSet.find(key) != mKvSet.end()) {
removeKeyValue(key);
}
mKvSet.insert(KeyTypePair(key, DATA_TYPE));
char* dest = NULL;
// len is zero then just put a NULL entry
if(len != 0){
dest = new char[len];
memcpy(dest, value, len);
}
mKvData.insert(std::pair<std::string, std::pair<uint32_t, char*> >
(key, std::pair<uint32_t, char*>(len, dest)));
}
bool ContentValue::getAsBool(const std::string &key, bool& value) const{
if(mKvBool.find(key) == mKvBool.end())
return false;
value = mKvBool.at(key);
return true;
}
bool ContentValue::getAsInt32(const std::string &key, int32_t& value) const{
if(mKvInt32.find(key) == mKvInt32.end())
return false;
value = mKvInt32.at(key);
return true;
}
bool ContentValue::getAsInt64(const std::string &key, int64_t& value) const{
if(mKvInt64.find(key) == mKvInt64.end())
return false;
value = mKvInt64.at(key);
return true;
}
bool ContentValue::getAsString(const std::string &key, std::string &value) const{
if(mKvString.find(key) == mKvString.end())
return false;
value = mKvString.at(key);
return true;
}
bool ContentValue::getAsData(const std::string& key, uint32_t &len, char*& value) const{
if(mKvData.find(key) == mKvData.end())
return false;
const std::pair<uint32_t, char*> &kvRef = mKvData.at(key);
len = kvRef.first;
value = kvRef.second;
return true;
}
bool ContentValue::getAsDouble(const std::string &key, double& value) const{
if(mKvDouble.find(key) == mKvDouble.end())
return false;
value = mKvDouble.at(key);
return true;
}
bool ContentValue::removeKeyValue(const std::string &key){
std::map<std::string, uint8_t>::iterator mit;
if((mit = mKvSet.find(key)) == mKvSet.end())
return false;
if(mit->second == BOOL_TYPE)
mKvBool.erase(key);
if(mit->second == INT64_TYPE)
mKvInt64.erase(key);
if(mit->second == DATA_TYPE){
delete[] (mKvData[key].second);
mKvData.erase(key);
}
if(mit->second == DOUBLE_TYPE)
mKvDouble.erase(key);
if(mit->second == STRING_TYPE)
mKvString.erase(key);
if(mit->second == INT32_TYPE)
mKvInt32.erase(key);
mKvSet.erase(key);
return true;
}
void ContentValue::getKeyTypeMap(std::map<std::string, uint8_t> &keySet) const {
keySet = mKvSet;
}
void ContentValue::clear(){
mKvSet.clear();
mKvBool.clear();
mKvDouble.clear();
mKvString.clear();
mKvInt32.clear();
mKvInt64.clear();
clearData();
}
void ContentValue::clearData(){
std::map<std::string, std::pair<uint32_t, char*> >::iterator
mit = mKvData.begin();
for(; mit != mKvData.end(); mit++){
if(mit->second.first != 0)
delete[] (mit->second.second);
}
mKvData.clear();
}