mirror of
				https://github.com/RetroShare/RetroShare.git
				synced 2025-10-31 14:49:14 -04:00 
			
		
		
		
	added RetroDb:
- tests added which pass on windows. git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-new_cache_system@5036 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
		
							parent
							
								
									e5ed6bf479
								
							
						
					
					
						commit
						030b009f9a
					
				
					 6 changed files with 1858 additions and 28 deletions
				
			
		
							
								
								
									
										898
									
								
								libretroshare/src/util/retrodb.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										898
									
								
								libretroshare/src/util/retrodb.cpp
									
										
									
									
									
										Normal 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), mCurrStmt(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(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1,10 +1,34 @@ | |||
| #ifndef RSSQLITE_H | ||||
| #define RSSQLITE_H | ||||
| 
 | ||||
| /*
 | ||||
|  * 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 "sqlite3.h" | ||||
| 
 | ||||
| #include <string> | ||||
| #include <set> | ||||
| #include <list> | ||||
| #include <map> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -12,10 +36,10 @@ class ContentValue; | |||
| class RetroCursor; | ||||
| 
 | ||||
| /*!
 | ||||
|  * The idea of RsDb is to provide a means for Retroshare core and \n | ||||
|  * its services to maintain an easy to use random access file via a database \n | ||||
|  * RetroDb provide a means for Retroshare's core and \n | ||||
|  * services to maintain an easy to use random access file via a database \n | ||||
|  * It models itself after android's sqlite functionality \n | ||||
|  * This is essentially close of Androids SQLiteDatabase | ||||
|  * This is essentially unashamedly a clone of Android's SQLiteDatabase interface | ||||
|  */ | ||||
| class RetroDb | ||||
| { | ||||
|  | @ -34,7 +58,9 @@ public: | |||
| 
 | ||||
|     /*!
 | ||||
|      * opens sqlite data base | ||||
|      * @return false if we failed to open | ||||
|      * @param dbPath | ||||
|      * @param flags | ||||
|      * @return false if failed to open, true otherwise | ||||
|      */ | ||||
|     bool openDb(const std::string& dbPath, int flags = OPEN_READONLY); | ||||
| 
 | ||||
|  | @ -43,22 +69,40 @@ public: | |||
|      */ | ||||
|     void closeDb(); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * | ||||
|      * @return false if database is not open, true otherwise | ||||
|      */ | ||||
|     bool isOpen() const; | ||||
| 
 | ||||
|     /* modifying db */ | ||||
| public: | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     /*!
 | ||||
|      * To make queries which does not return a result | ||||
|      * To a make query which do not return a result \n | ||||
|      * below are the type of queries this method should be used for \n | ||||
|      * ALTER TABLE \n | ||||
|      * CREATE or DROP table / trigger / view / index / virtual table \n | ||||
|      * REINDEX \n | ||||
|      * RELEASE \n | ||||
|      * SAVEPOINT \n | ||||
|      * PRAGMA that returns no data \n | ||||
|      * @param query SQL query | ||||
|      * @return false if there was an sqlite error, true otherwise | ||||
|      */ | ||||
|     void execSQL(const std::string& query); | ||||
|     bool execSQL(const std::string& query); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * inserts a row in a database table | ||||
|      * @param table table you want to insert content values into | ||||
|      * @param nullColumnHack  SQL doesn't allow inserting a completely \n | ||||
|      *        empty row without naming at least one column name | ||||
|      * @param cv hold entries to insert | ||||
|      * @return true if insertion successful, false otherwise | ||||
|      */ | ||||
|     bool sqlInsert(std::string& table, std::string nullColumnHack, const ContentValue& cv); | ||||
|     bool sqlInsert(const std::string& table,const  std::string& nullColumnHack, const ContentValue& cv); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * update row in a database table | ||||
|  | @ -72,14 +116,15 @@ public: | |||
|     /*!
 | ||||
|      * Query the given table, returning a Cursor over the result set | ||||
|      * @param tableName the table name | ||||
|      * @param columns columns that should be returned | ||||
|      * @param columns list columns that should be returned and their order (the list's order) | ||||
|      * @param selection  A filter declaring which rows to return, formatted as \n | ||||
|      *        an SQL WHERE clause (excluding the WHERE itself). Passing null will \n | ||||
|      *        return all rows for the given table. | ||||
|      * @param order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself) | ||||
|      * @return cursor over result set | ||||
|      * @return cursor over result set, this allocated resource should be free'd after use \n | ||||
|      *         column order is in list order. | ||||
|      */ | ||||
|     RetroCursor* sqlQuery(const std::string& tableName, const std::set<std::string>& columns, | ||||
|     RetroCursor* sqlQuery(const std::string& tableName, const std::list<std::string>& columns, | ||||
|                           const std::string& selection, const std::string& orderBy); | ||||
| 
 | ||||
|     /*!
 | ||||
|  | @ -88,9 +133,10 @@ public: | |||
|      * @param whereClause formatted as where statement without 'WHERE' itself | ||||
|      * @return false | ||||
|      */ | ||||
|     bool sqlDelete(const std::string& tableName, const std::string& whereClause); | ||||
|     bool sqlDelete(const std::string& tableName, const std::string& whereClause, const std::string& whereArgs); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * TODO | ||||
|      * defragment database, should be done on databases if many modifications have occured | ||||
|      */ | ||||
|     void vacuum(); | ||||
|  | @ -104,6 +150,7 @@ public: | |||
| 
 | ||||
|     static const int OPEN_READONLY; | ||||
|     static const int OPEN_READWRITE; | ||||
|     static const int OPEN_READWRITE_CREATE; | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|  | @ -122,24 +169,27 @@ public: | |||
| 
 | ||||
|     /*!
 | ||||
|      * Initialises a null cursor | ||||
|      * @warning cursor takes ownership of statement passed to it | ||||
|      */ | ||||
|     RetroCursor(sqlite3_stmt*); | ||||
| 
 | ||||
|     ~RetroCursor(); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * move to first row of results | ||||
|      * @return false if no result | ||||
|      * @return false if no results | ||||
|      */ | ||||
|     bool moveToFirst(); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * move to next row of results | ||||
|      * @return false if no row to move next to | ||||
|      * @return false if no further results | ||||
|      */ | ||||
|     bool moveToNext(); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * move to last row of results | ||||
|      * @return false if no result | ||||
|      * @return false if no result, true otherwise | ||||
|      */ | ||||
|     bool moveToLast(); | ||||
| 
 | ||||
|  | @ -147,9 +197,35 @@ public: | |||
|      * gets current position of cursor | ||||
|      * @return current position of cursor | ||||
|      */ | ||||
|     uint32_t getPosition(); | ||||
|     int32_t getPosition() const; | ||||
| 
 | ||||
|     /* data retrieval */ | ||||
| 
 | ||||
|     /*!
 | ||||
|      * @return true if cursor is open and active, false otherwise | ||||
|      */ | ||||
|     bool isOpen() const; | ||||
| 
 | ||||
|     /*!
 | ||||
|      * cursor is closed, statement used to open cursor is deleted | ||||
|      * @return false if error on close (was already closed, error occured) | ||||
|      */ | ||||
|     bool close(); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * | ||||
|      * @return -1 if cursor is in error, otherwise number of rows in result | ||||
|      */ | ||||
|     int32_t getResultCount() const; | ||||
| 
 | ||||
|     /*!
 | ||||
|      * Current statement is closed and discarded (finalised) | ||||
|      * before actual opening occurs | ||||
|      * @param stm statement to open cursor on | ||||
|      * @return true if cursor is successfully opened | ||||
|      */ | ||||
|     bool open(sqlite3_stmt* stm); | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
| 
 | ||||
|  | @ -158,7 +234,7 @@ public: | |||
|      * @param columnIndex the zero-based index of the target column. | ||||
|      * @return  the value of the column as 32 bit integer | ||||
|      */ | ||||
|     int32_t getInt(int columnIndex); | ||||
|     int32_t getInt32(int columnIndex); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * Returns the value of the requested column as a String. | ||||
|  | @ -186,20 +262,21 @@ public: | |||
|      * @param columnIndex the zero-based index of the target column. | ||||
|      * @return the value of the column as a string | ||||
|      */ | ||||
|     std::string getString(int columnIndex); | ||||
|     void getString(int columnIndex, std::string& str); | ||||
| 
 | ||||
|     /*!
 | ||||
|      * Returns the value of the requested column as a String. | ||||
|      * @param columnIndex the zero-based index of the target column. | ||||
|      * @return  the value of the column as pointer to raw data | ||||
|      */ | ||||
|     void* getData(int columnIndex, uint32_t& datSize); | ||||
|     const void* getData(int columnIndex, uint32_t& datSize); | ||||
| 
 | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     sqlite3_stmt* mStmt; | ||||
|     int mCount; | ||||
|     int mCount; /// number of results
 | ||||
|     int mPosCounter; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -239,6 +316,8 @@ public: | |||
|      * Adds a value to the set | ||||
|      * @param key  the name of the value to put | ||||
|      * @param value  the data for the value to put | ||||
|      * @warning cast string literals explicitly as string, observed string literal \n | ||||
|      *          being casted to bool instead e.g. string("hello") rather than "hello" | ||||
|      */ | ||||
|     void put(const std::string& key, const std::string& value); | ||||
| 
 | ||||
|  | @ -275,29 +354,29 @@ public: | |||
|      * @param key  the name of the value to put | ||||
|      * @param value  the data for the value to put | ||||
|      */ | ||||
|     void put(const std::string& key, uint32_t len, char* value); | ||||
|     void put(const std::string& key, uint32_t len, const char* value); | ||||
| 
 | ||||
| 
 | ||||
|     /*!
 | ||||
|      * get as value as | ||||
|      * get value as 32 bit signed integer | ||||
|      * @param key the value to get | ||||
|      */ | ||||
|     bool getAsInt32(const std::string& key, int32_t& value) const; | ||||
| 
 | ||||
|     /*!
 | ||||
|      * get as value as 64 bit integer | ||||
|      * get value as 64 bit signed integer | ||||
|      * @param key the value to get | ||||
|      */ | ||||
|     bool getAsInt64(const std::string& key, int64_t& value) const; | ||||
| 
 | ||||
|     /*!
 | ||||
|      * get as value as bool | ||||
|      * get value as bool | ||||
|      * @param key the value to get | ||||
|      */ | ||||
|     bool getAsBool(const std::string& key, bool& value) const; | ||||
| 
 | ||||
|     /*!
 | ||||
|      * get as value as double | ||||
|      * get as value double | ||||
|      * @param key the value to get | ||||
|      */ | ||||
|     bool getAsDouble(const std::string& key, double& value) const; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 chrisparker126
						chrisparker126