431 lines
11 KiB
C++
431 lines
11 KiB
C++
/*
|
|
|
|
1.1.1 Class Implementation
|
|
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2017,
|
|
Faculty of Mathematics and Computer Science,
|
|
Database Systems for New Applications.
|
|
|
|
SECONDO 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.
|
|
|
|
SECONDO 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 SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
*/
|
|
#include "Algebras/DBService2/Derivative.hpp"
|
|
|
|
#include <sstream>
|
|
|
|
#include <loguru.hpp>
|
|
|
|
using namespace std;
|
|
|
|
namespace DBService {
|
|
|
|
Derivative::Derivative() : Record::Record() {
|
|
setName("");
|
|
setDatabase("");
|
|
setFunction("");
|
|
}
|
|
|
|
Derivative::Derivative(string newName, string newFunction) :
|
|
Record::Record() {
|
|
|
|
setName(newName);
|
|
setFunction(newFunction);
|
|
}
|
|
|
|
Derivative::Derivative(string newName,
|
|
string newFunction, shared_ptr<DBService::Relation> newRelation) :
|
|
Record::Record() {
|
|
|
|
setName(newName);
|
|
setFunction(newFunction);
|
|
setRelation(newRelation);
|
|
}
|
|
|
|
shared_ptr<Derivative> Derivative::build(string newName, string newFunction) {
|
|
shared_ptr<Derivative> instance(new Derivative(newName, newFunction));
|
|
return instance;
|
|
}
|
|
|
|
shared_ptr<Derivative> Derivative::build(string newName,
|
|
string newFunction, shared_ptr<DBService::Relation> newRelation) {
|
|
|
|
shared_ptr<Derivative> instance(
|
|
new Derivative(newName, newFunction, newRelation));
|
|
|
|
return instance;
|
|
}
|
|
|
|
string Derivative::getName() const {
|
|
return name;
|
|
}
|
|
|
|
void Derivative::setName(std::string newName) {
|
|
if (newName != name) {
|
|
name = newName;
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
string Derivative::getFunction() const {
|
|
return function;
|
|
}
|
|
|
|
void Derivative::setFunction(std::string newFunction) {
|
|
if (newFunction != function) {
|
|
function = newFunction;
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<DBService::Relation> Derivative::getRelation() const {
|
|
return relation;
|
|
}
|
|
|
|
void Derivative::setRelation(
|
|
std::shared_ptr<DBService::Relation> newRelation, bool force) {
|
|
|
|
if (newRelation == nullptr)
|
|
throw "Can't execute setRelation(nullptr)!";
|
|
|
|
if (newRelation != relation) {
|
|
relation = newRelation;
|
|
|
|
if (force == false)
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
void Derivative::syncReplicasWithRelation() {
|
|
if (relation->getReplicaCount() > getReplicaCount()) {
|
|
addDerivativeReplicas(relation->getReplicas());
|
|
}
|
|
}
|
|
|
|
/*
|
|
Creates a derivative Replica based on the provided relation Replica.
|
|
Prevents adding Replica duplicates.
|
|
*/
|
|
void Derivative::addDerivativeReplica(
|
|
std::shared_ptr<Replica> relationReplica) {
|
|
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
shared_ptr<Replica> derivativeReplica = make_shared<Replica>();
|
|
|
|
derivativeReplica->setDatabase(getDatabase());
|
|
derivativeReplica->setType(Replica::replicaTypeDerivative);
|
|
derivativeReplica->setDerivativeId(getId());
|
|
derivativeReplica->setTargetNode(relationReplica->getTargetNode());
|
|
derivativeReplica->setRelation(relationReplica->getRelation());
|
|
derivativeReplica->setDerivative(shared_from_this());
|
|
|
|
if (!doesReplicaExist(derivativeReplica)) {
|
|
replicas.push_back(derivativeReplica);
|
|
setDirty();
|
|
}
|
|
}
|
|
|
|
//TODO Remove code duplicity with Relation
|
|
bool Derivative::doesReplicaExist(shared_ptr<Replica> newReplica) {
|
|
for( const auto& replica : replicas) {
|
|
if (replica == newReplica)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
This function is meant to be called once the Derivative has been created.
|
|
It is assumed that this happens after relation-Replicas have been added to
|
|
the Relation.
|
|
|
|
Furthermore the amount of Replicas is assumed to be a system-wide static
|
|
setting that won't change over time.
|
|
*/
|
|
void Derivative::addDerivativeReplicas(std::vector<std::shared_ptr<Replica> >
|
|
relationReplicas) {
|
|
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
for(auto& relationReplica : relationReplicas) {
|
|
addDerivativeReplica(relationReplica);
|
|
}
|
|
}
|
|
|
|
int Derivative::getReplicaCount() const {
|
|
return replicas.size();
|
|
}
|
|
|
|
void Derivative::loadReplicas() {
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
if (replicas.empty()) {
|
|
vector<shared_ptr<Replica> > shrPtrReplicas =
|
|
Replica::findByDerivativeId(
|
|
database, getId());
|
|
|
|
for( shared_ptr<Replica> shrdPtrReplica : shrPtrReplicas) {
|
|
|
|
// true -> avoid dirty checking by forcefully setting the Relation
|
|
// shrdPtrReplica->setDerivative(shared_from_this(), true);
|
|
|
|
shrdPtrReplica->setDerivativeId(getId());
|
|
shrdPtrReplica->setDerivative(shared_from_this());
|
|
|
|
replicas.push_back(shrdPtrReplica);
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<shared_ptr<Replica> > Derivative::getReplicas() const {
|
|
return replicas;
|
|
}
|
|
|
|
void Derivative::saveReplicas() {
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
if (getReplicaCount() <= 0)
|
|
return;
|
|
|
|
for( auto& replica : getReplicas()) {
|
|
if (replica->getIsDirty()) {
|
|
replica->setDatabase(getDatabase());
|
|
|
|
// If the derivative has been saved in the meanwhile, there is now an
|
|
// id != -1 which will be needed to retrieve the replica later.
|
|
replica->setDerivativeId(getId());
|
|
|
|
replica->save();
|
|
}
|
|
}
|
|
}
|
|
|
|
shared_ptr<Replica> Derivative::findReplica(string targetHost,
|
|
int targetPort) {
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
DBService::Node nodeToFind(
|
|
as_const(targetHost), as_const(targetPort), "");
|
|
|
|
for (auto& replica : replicas) {
|
|
if (*replica->getTargetNode() == nodeToFind) {
|
|
return replica;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void Derivative::updateReplicaStatus(string targetHost, int targetPort,
|
|
string replicaStatus) {
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
shared_ptr<Replica> replica = findReplica(
|
|
targetHost, targetPort);
|
|
|
|
if (replica == nullptr)
|
|
throw "Couldn't find replica by targetHost and targetPort.";
|
|
|
|
replica->setStatus(replicaStatus);
|
|
replica->save();
|
|
}
|
|
|
|
string Derivative::str(int indentationLevel) const {
|
|
string ind = std::string(2 * indentationLevel, ' ');
|
|
string ind2 = ind + " ";
|
|
|
|
stringstream msg;
|
|
|
|
msg << ind << "{" << endl;
|
|
msg << ind2 << "Name: " << getName() << endl;
|
|
msg << ind2 << "Function: " << getFunction() << endl;
|
|
|
|
if (relation != nullptr) {
|
|
msg << ind2 << "Relation: " << endl;
|
|
msg << relation->str(indentationLevel + 1) << endl;
|
|
}
|
|
|
|
msg << ind2 << "Derivative Replicas: (";
|
|
msg << getReplicaCount() << ");" << endl;
|
|
|
|
if (getReplicaCount() > 0) {
|
|
for (const auto& replica : replicas) {
|
|
msg << replica->str((indentationLevel + 1)) << endl;
|
|
}
|
|
}
|
|
|
|
msg << ind << "}";
|
|
|
|
return msg.str();
|
|
}
|
|
|
|
string Derivative::getRelationName() {
|
|
return "dbs_derivatives";
|
|
}
|
|
|
|
string Derivative::createRelationStatement() {
|
|
return "let " + getRelationName() + " = [const rel(tuple([Name: string, \
|
|
Function: string, RelationId: int])) value ()]";
|
|
}
|
|
|
|
string Derivative::createStatement() const {
|
|
const string quote = "\"";
|
|
|
|
stringstream createStatement;
|
|
|
|
if (relation == nullptr)
|
|
throw "Can't execute Derivative::createStatement() without relation!";
|
|
|
|
createStatement << "query " << Derivative::getRelationName();
|
|
createStatement << " inserttuple" << "[";
|
|
|
|
createStatement << quote << getName() << quote << ", ";
|
|
createStatement << quote << getFunction() << quote << ", ";
|
|
createStatement << relation->getId();
|
|
|
|
createStatement << "] consume";
|
|
return createStatement.str();
|
|
}
|
|
|
|
string Derivative::updateStatement() const {
|
|
|
|
// A neutral, non-mutating query as a Derivative can't be updated.
|
|
return "query 1";
|
|
}
|
|
|
|
Query Derivative::query(string recordDatabase) {
|
|
stringstream query;
|
|
|
|
query << "query" << " " << getRelationName();
|
|
|
|
return Query(recordDatabase, query.str());
|
|
}
|
|
|
|
void Derivative::beforeSave() { }
|
|
void Derivative::afterSave() {
|
|
saveReplicas();
|
|
}
|
|
|
|
bool Derivative::empty() const {
|
|
return replicas.size();
|
|
}
|
|
|
|
shared_ptr<Derivative> Derivative::findOne(string database, Query query) {
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
return query.retrieveObject<Derivative, SecondoDerivativeAdapter>();
|
|
}
|
|
|
|
vector<shared_ptr<Derivative> > Derivative::findAll(string database) {
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
|
|
Query query = Derivative::query(database).feed().addid().consume();
|
|
|
|
std::vector<std::shared_ptr<Derivative> > manyRecords =
|
|
query.retrieveVector<Derivative, SecondoDerivativeAdapter>();
|
|
|
|
syncToCache(manyRecords);
|
|
|
|
return manyRecords;
|
|
}
|
|
|
|
bool Derivative::operator==(const DBService::Derivative &other) const {
|
|
return (getName() == other.getName() &&
|
|
getFunction() == other.getFunction() &&
|
|
*relation == *other.getRelation());
|
|
}
|
|
bool Derivative::operator!=(const DBService::Derivative &other) const {
|
|
return !(getName() == other.getName() &&
|
|
getFunction() == other.getFunction() &&
|
|
*relation == *other.getRelation());
|
|
}
|
|
|
|
vector<shared_ptr<Derivative> > Derivative::findByRelationId(
|
|
string database, int relationId) {
|
|
|
|
LOG_SCOPE_FUNCTION(INFO);
|
|
LOG_F(INFO, "Looking for Derivatives with relationId %d...", relationId);
|
|
|
|
//TODO is there a way to use a template function with lamdas
|
|
// to make the cache lookup generic?
|
|
|
|
// Cache
|
|
vector<shared_ptr<Derivative> > resultsFromCache;
|
|
|
|
std::map<int, std::shared_ptr<Derivative> >::iterator it = cache.begin();
|
|
|
|
//TODO With a Derivative specific Map complexity can be reduced from linear
|
|
// to log.
|
|
// while (it != cache.end()) {
|
|
// auto currentDerivative = it->second;
|
|
|
|
// if(currentDerivative->getRelationId() == relationId) {
|
|
// LOG_F(INFO, "Cache relationId %d, relationId: %d",
|
|
// currentDerivative->getRelationId(), relationId);
|
|
|
|
// resultsFromCache.push_back(currentDerivative);
|
|
// }
|
|
|
|
// it++;
|
|
// }
|
|
|
|
auto matchPredicate = [=](
|
|
std::pair<int, std::shared_ptr<Derivative>> const& currentPair) {
|
|
return currentPair.second->getRelationId() == relationId;
|
|
};
|
|
|
|
resultsFromCache = findManyInCache(matchPredicate);
|
|
|
|
if(useCache() && cacheValidity == true) {
|
|
LOG_F(INFO, "Cache hit. Found %d cached Derivative(s).",
|
|
resultsFromCache.size());
|
|
|
|
return resultsFromCache;
|
|
}
|
|
|
|
LOG_F(INFO, "%s", "Cache miss. Loading from DB...");
|
|
|
|
// DB Query
|
|
Query query = DBService::Derivative::query(database).feed().filter(
|
|
".RelationId = ?", relationId).addid().consume();
|
|
return query.retrieveVector<Derivative, SecondoDerivativeAdapter>();
|
|
}
|
|
|
|
void Derivative::afterDestroy() {
|
|
deleteReplicas();
|
|
}
|
|
|
|
void Derivative::deleteReplicas() {
|
|
for(auto& replica : replicas)
|
|
replica->destroy();
|
|
}
|
|
|
|
void Derivative::setRelationId(int newRelationId) {
|
|
relationId = newRelationId;
|
|
}
|
|
|
|
int Derivative::getRelationId() {
|
|
return relationId;
|
|
}
|
|
|
|
ostream &operator<<(ostream &os, Derivative const &derivative) {
|
|
os << derivative.str();
|
|
}
|
|
} |