Files
secondo/include/DerivedObj.h
2026-01-23 17:03:45 +08:00

530 lines
14 KiB
C++

/*
----
This file is part of SECONDO.
Copyright (C) 2004, University in Hagen, Department of 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
----
//characters [1] verbatim: [\verb@] [@]
1 DerivedObj, a class for objects created by the user command derive
May 06, 2004. M. Spiekermann: Initial Version
May 20, 2004. M. Spiekermann: A bug during Berkeley-DB environment close has
been fixed. Now the relation object will be closed properly in the destructor
Dec 02, 2006. M. Spiekermann: A new update value is now created by
~inObject~ instead of a little query. Moreover, some changes in the Algebra
interface made it necessary to call ~QP::DestroyValuesArray~,
since ~QP::AnnotateX~ open objects. Now the derive command
works again.
December 2005, Victor Almeida deleted the deprecated algebra levels
(~executable~, ~descriptive~, and ~hibrid~). Only the executable
level remains.
This class creates and maintains the system table
SEC\_DERIVED\_OBJ(name: string, value: text, usedObj: text)
which will be created in a database when a user decides to create an object via
the derive command. The derive command has the same syntax as the let command
but the created objects are not saved in a textual nested list representation
when a database is saved. When a database contains derived objects these are
recreated after all non-derived objects are restored.
This allows to restore btree objects which have no nested list representation or
to keep experimental results created from querys without storing them explicitly
in files. Since the objects are created by user interaction in the correct order
it is not necessary to do topological sorting at the dependency graph.
If an object is deleted a warning message will list all the derived objects
which use this object.
*/
#ifndef CLASS_DERIVEDOBJ_H
#define CLASS_DERIVEDOBJ_H
#include <SecondoSystem.h>
#include <SecondoCatalog.h>
#include <QueryProcessor.h>
#include <SecondoInterface.h>
#include <NestedList.h>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <list>
class DerivedObj {
public:
DerivedObj() :
tableExists(false),
derivedObjRelName("SEC_DERIVED_OBJ"),
derivedObjTypeStr("(rel(tuple((Name string)(Value text)(UsedObjs text))))"),
nl( *SecondoSystem::GetNestedList() ),
ctlg( *SecondoSystem::GetCatalog() ),
qp( *SecondoSystem::GetQueryProcessor() )
{
// check if the system tables are already present
// and read in the stored values
if ( ctlg.IsObjectName(derivedObjRelName) ) {
bool defined = false;
bool ok = false;
ok = ctlg.GetObject( derivedObjRelName, derivedObjWord, defined );
assert( ok && defined );
derivedObjValueList = ctlg.OutObject(typeExpr(), derivedObjWord);
ListToMemory( derivedObjValueList );
ctlg.CloseObject( typeExpr(), derivedObjWord );
tableExists = true;
}
}
~DerivedObj(){ // close relation object
// free allocated memory
for ( std::vector<ObjRecord*>::const_iterator it = derivedObjRecords.begin();
it != derivedObjRecords.end();
it++ )
{
delete (*it);
}
}
/*
1.1 Creation of relation objects
*/
void createTableIfNecessary() {
if (tableExists)
return;
if ( !ctlg.IsObjectName(derivedObjRelName) ) {
std::string typeName = "";
if ( ctlg.CreateObject( derivedObjRelName, typeName, typeExpr(), 0 ) )
{
tableExists = true;
} else {
std::cerr << "Error: Creation of " << derivedObjRelName
<< " failed!" << std::endl;
}
}
}
/*
The next function stores information of the value expression of the derive
command and extracts the object dependencies from the annotated query.
*/
void addObj(std::string& objName, const ListExpr valueExpr ) {
createTableIfNecessary();
// extract dependent objects
bool defined = false;
ListExpr annotatedList = qp.AnnotateX( valueExpr, defined);
// delete and close objects which were opened during annotate
qp.DestroyValuesArray();
//nl.WriteListExpr(annotatedList);
std::vector<ListExpr> atoms;
nl.ExtractAtoms(annotatedList, atoms);
std::string valueExprStr = "";
nl.WriteToString( valueExprStr, valueExpr );
// create new entries for derived objects
ObjRecord* newObj = new ObjRecord(objName, valueExprStr);
for ( std::vector<ListExpr>::const_iterator it = atoms.begin();
it != atoms.end();
it++ )
{
if ( nl.AtomType(*it) == SymbolType
&& nl.SymbolValue(*it) == "object" ) {
std::string val = nl.SymbolValue(*(it - 1));
newObj->addDepObj( val );
usedObjs.insert( val );
}
}
derivedObjNames[objName] = derivedObjRecords.size();
derivedObjRecords.push_back( newObj );
updateTable();
}
void reportObjDeps(const std::string& objName) {
std::set<std::string>::const_iterator it = usedObjs.find(objName);
if ( it == usedObjs.end() )
return;
std::cout << "Warning: dependent objects ";
// to do: iterate over all dependent obj.
std::cout << "can not be restored after save database." << std::endl;
}
bool deleteObj(const std::string& objName, bool internal=false) {
bool deleted = false;
std::map<std::string,int>::iterator it = derivedObjNames.find(objName);
if ( it != derivedObjNames.end() ) {
ObjRecord& objRec = *(derivedObjRecords[it->second]);
objRec.deleted = true;
deleted = true;
derivedObjNames.erase(it);
if (!internal) { // in the rebuild phase no messages are printed
reportObjDeps(objName);
updateTable();
}
}
return deleted;
}
bool isDerived(const std::string& objName) const {
std::map<std::string,int>::const_iterator it = derivedObjNames.find(objName);
return ( it != derivedObjNames.end() );
}
// called after a database is restored.
void rebuildObjs() {
if ( !tableExists )
return;
std::cout << std::endl << "Rebuilding derived objects ..." << std::endl;
for ( std::vector<ObjRecord*>::const_iterator it = derivedObjRecords.begin();
it != derivedObjRecords.end();
it++ )
{
assert( !(*it)->deleted );
ListExpr valueList = nl.TheEmptyList();
nl.ReadFromString( (*it)->value, valueList );
std::cout << " " << (*it)->name << " ... ";
SecondoSystem::BeginTransaction();
int rc = createObj( (*it)->name, valueList );
if (rc != 0) {
std::cout << "failed." << std::endl;
const std::string sep = " ";
const std::string& errMsg = SecondoInterface::GetErrorMessage(rc);
std::cerr << sep << "Could not rebuild object. Error msg: "
<< errMsg << std::endl;
std::cerr << sep << "Maybe objects which are used for "
<< "the derived object were deleted!" << std::endl;
std::cerr << sep << "ValueExpr: " << (*it)->value << std::endl
<< std::endl;
SecondoSystem::AbortTransaction(true);
deleteObj((*it)->name,true);
} else {
SecondoSystem::CommitTransaction(true);
std::cout << "created." << std::endl;
}
}
}
void updateObj() {
/*
what happens when an Secondo object is updated. Currently nothing
(i) derived object => new creation expr and recreation of all dependent objects
(ii) regular object => recreation of all dependent objects
*/
}
int createObj(std::string& objName, ListExpr valueExpr) {
OpTree tree(0);
Word result(Address(0));
bool correct=false, evaluable=false, defined=false, isFunction=false;
ListExpr resultType = nl.TheEmptyList();
int rc = 0;
if ( ctlg.IsObjectName(objName) )
{
rc = ERR_IDENT_USED; // identifier is already used
}
else
{
try
{
qp.Construct( valueExpr, correct, evaluable, defined,
isFunction, tree, resultType );
}
catch (SI_Error err)
{
rc = err;
qp.Destroy( tree, true );
tree = 0;
correct = false;
}
if ( correct )
{
if ( evaluable || isFunction )
{
std::string typeName = "";
ctlg.CreateObject(objName, typeName, resultType, 0);
}
if ( evaluable )
{
qp.EvalP( tree, result, 1 );
if( IsRootObject( tree ) && !IsConstantObject( tree ) )
{
ctlg.CloneObject( objName, result );
qp.Destroy( tree, true );
tree = 0;
}
else
{
ctlg.UpdateObject( objName, result );
qp.Destroy( tree, false );
tree = 0;
}
}
else if ( isFunction ) // abstraction or function object
{
if ( nl.IsAtom( valueExpr ) ) // function object
{
ListExpr
functionList =
ctlg.GetObjectValue( nl.SymbolValue( valueExpr ) );
ctlg.UpdateObject( objName, SetWord( functionList ) );
}
else
{
ctlg.UpdateObject( objName, SetWord( valueExpr ) );
}
}
}
else
{
qp.Destroy( tree, true );
tree = 0;
}
}
return rc;
}
// return all derived object names
const std::set<std::string>& getObjNames() {
static std::set<std::string> nameSet;
nameSet.clear();
for ( std::map<std::string,int>::const_iterator it = derivedObjNames.begin();
it != derivedObjNames.end();
it++ )
{
nameSet.insert(it->first);
}
return nameSet;
}
private:
// the typeExpr needs to be stored in a string since ListExpr are only
// valid inside a call of SecondoInterface::Secondo(...)
ListExpr& typeExpr() {
nl.ReadFromString( derivedObjTypeStr, derivedObjTypeList );
return derivedObjTypeList;
}
// build a nested list from the memory structure
ListExpr MemoryToList() {
ListExpr result = nl.TheEmptyList();
ListExpr last = nl.TheEmptyList();
for ( std::vector<ObjRecord*>::const_iterator it = derivedObjRecords.begin();
it != derivedObjRecords.end();
it++ )
{
if ( !(*it)->deleted) { // omit deleted objects
// create list expr. for tuples
ListExpr stringAtom = nl.StringAtom( (*it)->name );
ListExpr textAtom = nl.TextAtom();
ListExpr textAtom2 = nl.TextAtom();
nl.AppendText( textAtom, (*it)->value );
std::string objListStr = "";
(*it)->depObjListStr(objListStr);
nl.AppendText( textAtom2, objListStr );
ListExpr newTuple = nl.ThreeElemList( stringAtom, textAtom, textAtom2 );
if ( result == nl.TheEmptyList() ) {
result = nl.Cons( newTuple, nl.TheEmptyList() );
last = result;
} else {
last = nl.Append( last, newTuple );
}
}
}
return result;
}
// initialize a memory representation from a nested list rep.
void ListToMemory(ListExpr list) {
// insert the tuple values into memory structures
while ( list != nl.TheEmptyList() ) {
ListExpr tuple = nl.First(list);
list = nl.Rest(list);
std::string name = nl.StringValue( nl.First(tuple) );
std::string value = "", depListStr = "";
nl.Text2String( nl.Second(tuple), value );
nl.Text2String( nl.Third(tuple), depListStr);
// convert a list stored in a textatom into a ListExpr
ListExpr depList = nl.TheEmptyList();
nl.ReadFromString(depListStr, depList);
ObjRecord* newObjRec = new ObjRecord( name, value );
while ( depList != nl.TheEmptyList() ) {
std::string symbol = nl.SymbolValue( nl.First(depList) );
newObjRec->addDepObj( symbol );
usedObjs.insert( symbol );
depList = nl.Rest(depList);
}
derivedObjNames[name] = derivedObjRecords.size();
derivedObjRecords.push_back( newObjRec );
}
}
// converts the memory representation into a nested list
// and updates the relation object.
void updateTable() {
Word result;
bool correct=false;
ListExpr newValue = MemoryToList();
const int errorPos = 0;
ListExpr errorInfo = 0;
result = ctlg.InObject( typeExpr(), newValue, errorPos,
errorInfo, correct );
assert( correct );
bool ok = false;
ok = ctlg.UpdateObject(derivedObjRelName, result );
assert( ok );
}
// Below a container for information about derived objects
// is declared.
class ObjRecord {
public:
std::string name;
std::string value;
std::set<std::string> depSet;
bool deleted;
ObjRecord(const std::string& nameStr, const std::string& val) :
name(nameStr), value(val), deleted(false) {
}
~ObjRecord(){
//std::cerr << "~ObjRecord() called!" << std::endl;
}
void addDepObj(std::string& symbol) { depSet.insert(symbol); }
void depObjListStr(std::string& listStr) {
listStr = "(";
for ( std::set<std::string>::const_iterator it = depSet.begin();
it != depSet.end(); it++ ) {
listStr += (" " + *it + " ");
}
listStr += ")";
}
};
bool tableExists;
Word derivedObjWord;
std::string derivedObjRelName;
std::string derivedObjTypeStr;
ListExpr derivedObjTypeList;
ListExpr derivedObjValueList;
// the map and vector below contain information
// about objects created with the derive command. They are
// used to create the system table SEC_DERIVED_OBJ.
std::vector<ObjRecord*> derivedObjRecords;
std::map<std::string,int> derivedObjNames;
std::set<std::string> usedObjs;
// references to global instances
NestedList& nl;
SecondoCatalog& ctlg;
QueryProcessor& qp;
};
#endif