Skip to content

Commit

Permalink
Split Engine object
Browse files Browse the repository at this point in the history
Split Engine object with a a EngineBase object. The latter can then be used for the R Syntax: this EngineBase object has all the necessary functions used by the RBridge object. It can be also instantiated with a In memory sqlite database.
  • Loading branch information
boutinb committed Dec 16, 2024
1 parent 7ff5bd9 commit 85b08cb
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 165 deletions.
38 changes: 22 additions & 16 deletions CommonData/databaseinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ void DatabaseInterface::upgradeDBFromVersion(Version originalVersion)
transactionWriteEnd();
}

DatabaseInterface::DatabaseInterface(bool createDb)
DatabaseInterface::DatabaseInterface(bool createDb, bool inMemory)
{
assert(!_singleton);
_singleton = this;

if(createDb) create();
else load();
if(createDb) create(inMemory);
else load(inMemory);
}

DatabaseInterface::~DatabaseInterface()
Expand Down Expand Up @@ -1353,11 +1353,17 @@ void DatabaseInterface::labelsWrite(Column *column)
transactionWriteEnd();
}

std::string DatabaseInterface::dbFile(bool onlyName) const
std::string DatabaseInterface::dbFile(bool onlyName, bool inMemory) const
{
JASPTIMER_SCOPE(DatabaseInterface::dbFile);

return onlyName ? "internal.sqlite" : Utils::osPath(TempFiles::sessionDirName() + "/internal.sqlite").string();
static std::string fileName = "internal.sqlite";
static std::string memoryName = ":memory:";

if (inMemory)
return memoryName;

return onlyName ? fileName : Utils::osPath(TempFiles::sessionDirName() + "/" + fileName).string();
}

void DatabaseInterface::runQuery(const std::string & query, std::function<void(sqlite3_stmt *stmt)> bindParameters, std::function<void(size_t row, sqlite3_stmt *stmt)> processRow)
Expand Down Expand Up @@ -1603,49 +1609,49 @@ void DatabaseInterface::_runStatementsRepeatedly(const std::string & statements,
}
}

void DatabaseInterface::create()
void DatabaseInterface::create(bool inMemory)
{
JASPTIMER_SCOPE(DatabaseInterface::create);
assert(!_db);

if(std::filesystem::exists(dbFile()))
if(!inMemory && std::filesystem::exists(dbFile(false, inMemory)))
{
Log::log() << "DatabaseInterface::create: Removing existing sqlite internal db at " << dbFile() << std::endl;
std::filesystem::remove(dbFile());
Log::log() << "DatabaseInterface::create: Removing existing sqlite internal db at " << dbFile(false, inMemory) << std::endl;
std::filesystem::remove(dbFile(false, inMemory));
}

int ret = sqlite3_open_v2(dbFile().c_str(), &_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL);
int ret = sqlite3_open_v2(dbFile(false, inMemory).c_str(), &_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL);

if(ret != SQLITE_OK)
{
Log::log() << "Couldnt open sqlite internal db, because of: " << (_db ? sqlite3_errmsg(_db) : "not even a broken sqlite3 obj was returned..." ) << std::endl;
throw std::runtime_error("JASP cannot run without an internal database and it cannot be created. Contact the JASP team for help.");
}
else
Log::log() << "Opened internal sqlite database for creation at '" << dbFile() << "'." << std::endl;
Log::log() << "Opened internal sqlite database for creation at '" << dbFile(false, inMemory) << "'." << std::endl;

transactionWriteBegin();
runStatements(_dbConstructionSql);
transactionWriteEnd();
}

void DatabaseInterface::load()
void DatabaseInterface::load(bool inMemory)
{
JASPTIMER_SCOPE(DatabaseInterface::load);
assert(!_db);

if(!std::filesystem::exists(dbFile()))
throw std::runtime_error("Trying to load '" + dbFile() + "' but it doesn't exist!");
if(!std::filesystem::exists(dbFile(false, inMemory)))
throw std::runtime_error("Trying to load '" + dbFile(false, inMemory) + "' but it doesn't exist!");

int ret = sqlite3_open_v2(dbFile().c_str(), &_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, NULL);
int ret = sqlite3_open_v2(dbFile(false, inMemory).c_str(), &_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, NULL);

if(ret != SQLITE_OK)
{
Log::log() << "Couldnt open sqlite internal db, because of: " << (_db ? sqlite3_errmsg(_db) : "not even a broken sqlite3 obj was returned..." ) << std::endl;
throw std::runtime_error("JASP cannot run without an internal database and it cannot be created. Contact the JASP team for help.");
}
else
Log::log() << "Opened internal sqlite database for loading at '" << dbFile() << "'." << std::endl;
Log::log() << "Opened internal sqlite database for loading at '" << dbFile(false, inMemory) << "'." << std::endl;

}

Expand Down
8 changes: 4 additions & 4 deletions CommonData/databaseinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ class DatabaseInterface
public:
typedef std::function<void(sqlite3_stmt *stmt)> bindParametersType;

DatabaseInterface(bool create = false); ///< Creates or loads a sqlite database based on the argument
DatabaseInterface(bool create = false, bool inMemory = false); ///< Creates or loads a sqlite database based on the argument
~DatabaseInterface();
std::string dbFile(bool onlyPostfix=false) const; ///< Convenience function for getting the filename where sqlite db should be
std::string dbFile(bool onlyPostfix, bool inMemory = false) const; ///< Convenience function for getting the filename where sqlite db should be

static DatabaseInterface * singleton() { return _singleton; } ///< There can be only one! https://www.youtube.com/watch?v=sqcLjcSloXs

Expand Down Expand Up @@ -166,8 +166,8 @@ class DatabaseInterface
void _runStatements( const std::string & statements, std::function<void(sqlite3_stmt *stmt)> * bindParameters = nullptr, std::function<void(size_t row, sqlite3_stmt *stmt)> * processRow = nullptr); ///< Runs several sql statements without looking at the results. Unless processRow is not NULL, then this is called for each row.
void _runStatementsRepeatedly( const std::string & statements, std::function<bool( std::function<void(sqlite3_stmt *stmt)> ** bindParameters, size_t row)> bindParameterFactory, std::function<void(size_t row, size_t repetition, sqlite3_stmt *stmt)> * processRow = nullptr);

void create(); ///< Creates a new sqlite database in sessiondir and loads it
void load(); ///< Loads a sqlite database from sessiondir (after loading a jaspfile)
void create(bool inMemory = false); ///< Creates a new sqlite database in sessiondir and loads it
void load(bool inMemory = false); ///< Loads a sqlite database from sessiondir (after loading a jaspfile)
void close(); ///< Closes the loaded database and disconnects
bool tableHasColumn(const std::string & tableName, const std::string & columnName);

Expand Down
117 changes: 1 addition & 116 deletions Engine/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,12 @@ bool PollMessagesFunctionForJaspResults()
Engine * Engine::_EngineInstance = NULL;

Engine::Engine(int slaveNo, unsigned long parentPID)
: _engineNum(slaveNo), _parentPID(parentPID)
: EngineBase(parentPID), _engineNum(slaveNo), _parentPID(parentPID)
{
JASPTIMER_SCOPE(Engine Constructor);
assert(_EngineInstance == NULL);
_EngineInstance = this;

JASPTIMER_START(TempFiles Attach);
TempFiles::attach(parentPID);
JASPTIMER_STOP(TempFiles Attach);

if(parentPID != 0) //Otherwise we are just running to fix R packages
_db = new DatabaseInterface();

_extraEncodings = new ColumnEncoder("JaspExtraOptions_");
}

Expand Down Expand Up @@ -871,59 +864,6 @@ analysisResultStatus Engine::getStatusToAnalysisStatus()
}
}

int Engine::getColumnType(const std::string &columnName)
{
return int(!isColumnNameOk(columnName) ? columnType::unknown : provideAndUpdateDataSet()->column(columnName)->type());
}

int Engine::getColumnAnalysisId(const std::string &columnName)
{
return !isColumnNameOk(columnName)
? -1
: provideAndUpdateDataSet()->column(columnName)->analysisId();
}

std::string Engine::createColumn(const std::string &columnName)
{
if(columnName.empty() || isColumnNameOk(columnName))
return "";

DataSet * data = provideAndUpdateDataSet();
Column * col = data->newColumn(columnName);

col->setAnalysisId(_analysisId);
col->setCodeType(computedColumnType::analysisNotComputed);

reloadColumnNames();

return rbridge_encodeColumnName(columnName.c_str());
}

bool Engine::deleteColumn(const std::string &columnName)
{
if(!isColumnNameOk(columnName))
return false;

DataSet * data = provideAndUpdateDataSet();
Column * col = data->column(columnName);

if(col->analysisId() != _analysisId)
return false;

data->removeColumn(columnName);

reloadColumnNames();

return true;
}

bool Engine::setColumnDataAndType(const std::string &columnName, const std::vector<std::string> &data, columnType colType)
{
if(!isColumnNameOk(columnName))
return false;

return provideAndUpdateDataSet()->column(columnName)->overwriteDataAndType(data, colType);
}

void Engine::sendAnalysisResults()
{
Expand Down Expand Up @@ -969,55 +909,6 @@ void Engine::removeNonKeepFiles(const Json::Value & filesToKeepValue)
TempFiles::deleteList(tempFilesFromLastTime);
}

DataSet * Engine::provideAndUpdateDataSet()
{
JASPTIMER_RESUME(Engine::provideAndUpdateDataSet());
//Log::log() << "Engine::provideAndUpdateDataSet()" << std::endl;

bool setColumnNames = !_dataSet;

if(!_dataSet && _db->dataSetGetId() != -1)
_dataSet = new DataSet(_db->dataSetGetId());

if(_dataSet)
setColumnNames |= _dataSet->checkForUpdates();

if(_dataSet && setColumnNames)
ColumnEncoder::columnEncoder()->setCurrentNames(_dataSet->getColumnNames(), true);

JASPTIMER_STOP(Engine::provideDataSet());

return _dataSet;
}

void Engine::provideStateFileName(std::string & root, std::string & relativePath)
{
return TempFiles::createSpecific("state", _analysisId, root, relativePath);
}

void Engine::provideJaspResultsFileName(std::string & root, std::string & relativePath)
{
return TempFiles::createSpecific("jaspResults.json", _analysisId, root, relativePath);
}

void Engine::provideSpecificFileName(const std::string & specificName, std::string & root, std::string & relativePath)
{
return TempFiles::createSpecific(specificName, _analysisId, root, relativePath);
}

void Engine::provideTempFileName(const std::string & extension, std::string & root, std::string & relativePath)
{
TempFiles::create(extension, _analysisId, root, relativePath);
}

bool Engine::isColumnNameOk(const std::string & columnName)
{
if(columnName == "" || !provideAndUpdateDataSet())
return false;

return provideAndUpdateDataSet()->column(columnName);
}

void Engine::stopEngine()
{
Log::log() << "Engine::stopEngine() received, closing engine." << std::endl;
Expand Down Expand Up @@ -1103,12 +994,6 @@ void Engine::sendEnginePaused()
sendString(rCodeResponse.toStyledString());
}

void Engine::reloadColumnNames()
{
Log::log() << "Engine rescanning columnNames for en/decoding" << std::endl;
ColumnEncoder::columnEncoder()->setCurrentColumnNames(provideAndUpdateDataSet() == nullptr ? std::vector<std::string>({}) : provideAndUpdateDataSet()->getColumnNames());
}

void Engine::resumeEngine(const Json::Value & jsonRequest)
{
Log::log() << "Engine resuming and absorbing settings from request." << std::endl;
Expand Down
22 changes: 3 additions & 19 deletions Engine/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#ifndef ENGINE_H
#define ENGINE_H

#include "enginebase.h"
#include "enginedefinitions.h"
#include "dataset.h"
#include "ipcchannel.h"
Expand All @@ -27,7 +28,7 @@
/// The Engine handles communication between Desktop and R
/// It can be in a variety of states _currentEngineState and can run analyses, filters, compute columns and Rcode.
/// It also contains some utility functions for use by rbridge and by extension R
class Engine
class Engine : public EngineBase
{
public:
typedef engineAnalysisStatus Status;
Expand All @@ -48,21 +49,7 @@ class Engine
analysisResultStatus getStatusToAnalysisStatus();

//the following functions in public can all be called (indirectly) from R and/or rbridge:
int getColumnType( const std::string & columnName);
int getColumnAnalysisId( const std::string & columnName);
std::string createColumn( const std::string & columnName); ///< Returns encoded columnname on success or "" on failure (cause it already exists)
bool deleteColumn( const std::string & columnName);
bool setColumnDataAndType( const std::string & columnName, const std::vector<std::string> & nominalData, columnType colType); ///< return true for any changes
bool isColumnNameOk( const std::string & columnName);
int dataSetRowCount() { return static_cast<int>(provideAndUpdateDataSet()->rowCount()); }
bool paused() { return _engineState == engineState::paused; }
DataSet * provideAndUpdateDataSet();

void provideTempFileName( const std::string & extension, std::string & root, std::string & relativePath);
void provideStateFileName( std::string & root, std::string & relativePath);
void provideJaspResultsFileName( std::string & root, std::string & relativePath);
void provideSpecificFileName( const std::string & specificName, std::string & root, std::string & relativePath);
void reloadColumnNames();

private:
void initialize();
Expand Down Expand Up @@ -111,15 +98,12 @@ class Engine
static Engine * _EngineInstance;
const int _engineNum;
const unsigned long _parentPID;
DataSet * _dataSet = nullptr;
DatabaseInterface * _db = nullptr;
IPCChannel * _channel = nullptr;
ColumnEncoder * _extraEncodings = nullptr;
engineState _engineState = engineState::initializing,
_lastRequest = engineState::initializing;
Status _analysisStatus = Status::empty;
int _analysisId,
_analysisRevision,
int _analysisRevision,
_progress,
_ppi = 96,
_numDecimals = 3;
Expand Down
Loading

0 comments on commit 85b08cb

Please sign in to comment.