/*
* This file is part of rasdaman community.
*
* Rasdaman community 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 3 of the License, or
* (at your option) any later version.
*
* Rasdaman community 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 rasdaman community. If not, see .
*
* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann /
rasdaman GmbH.
*
* For more information please see
* or contact Peter Baumann via .
*/
/*************************************************************
*
*
* PURPOSE:
*
*
* COMMENTS:
*
************************************************************/
static const char rcsid[] = "@(#)qlparser, QtUpdate: $Header: /home/rasdev/CVS-repository/rasdaman/qlparser/qtupdate.cc,v 1.28 2003/12/27 20:51:28 rasdev Exp $";
#include "raslib/dlist.hh"
#include "qlparser/qtupdate.hh"
#include "qlparser/qtdata.hh"
#include "qlparser/qtmdd.hh"
#include "qlparser/qtmintervaldata.hh"
#include "tilemgr/tile.hh"
#include "tilemgr/tiler.hh"
#include "mddmgr/mddobj.hh"
#include
#include
const QtNode::QtNodeType QtUpdate::nodeType = QtNode::QT_UPDATE;
QtUpdate::QtUpdate( QtOperation* initUpdateTarget, QtOperation* initUpdateDomain, QtOperation* initUpdateSource )
: QtExecute(), input(NULL),
updateTarget( initUpdateTarget ),
updateDomain( initUpdateDomain ),
updateSource( initUpdateSource )
{
if( updateTarget ) updateTarget->setParent( this );
if( updateDomain ) updateDomain->setParent( this );
if( updateSource ) updateSource->setParent( this );
}
QtUpdate::~QtUpdate()
{
if( updateTarget )
{
delete updateTarget;
updateTarget=NULL;
}
if( updateDomain )
{
delete updateDomain;
updateDomain=NULL;
}
if( updateSource )
{
delete updateSource;
updateSource=NULL;
}
if( input )
{
delete input;
input=NULL;
}
}
int
QtUpdate::evaluate()
{
RMDBCLASS( "QtUpdate", "evaluate()", "qlparser", __FILE__, __LINE__ )
// Test, if all necessary operands are available.
if( updateTarget && updateSource && input )
{
QtNode::QtDataList* nextTupel;
// open input stream
try
{
input->open();
}
catch( ... )
{
input->close();
throw;
}
try
{
while( (nextTupel = input->next()) )
{
// get obligatory operands
QtData* target = updateTarget->evaluate( nextTupel );
QtData* source = updateSource->evaluate( nextTupel );
// Test, if the operands are valid.
if( target && source )
{
// check update target
if( target->getDataType() != QT_MDD )
{
RMInit::logOut << "Error: QtUpdate::evaluate() - update target must be an iterator variable." << endl;
// delete tupel vector received by next()
for( vector::iterator dataIter=nextTupel->begin();
dataIter!=nextTupel->end(); dataIter++ )
if( *dataIter ) (*dataIter)->deleteRef();
delete nextTupel;
nextTupel=NULL;
// delete the operands
if( target ) target->deleteRef();
if( source ) source->deleteRef();
parseInfo.setErrorNo(950);
throw parseInfo;
}
// check update source
if( source->getDataType() != QT_MDD )
{
RMInit::logOut << "Error: QtUpdate::evaluate() - update source must be an expression resulting in an MDD" << endl;
// delete tupel vector received by next()
for( vector::iterator dataIter=nextTupel->begin();
dataIter!=nextTupel->end(); dataIter++ )
if( *dataIter ) (*dataIter)->deleteRef();
delete nextTupel;
nextTupel=NULL;
// delete the operands
if( target ) target->deleteRef();
if( source ) source->deleteRef();
parseInfo.setErrorNo(951);
throw parseInfo;
}
QtMDD* targetMDD = (QtMDD*) target;
QtMDD* sourceMDD = (QtMDD*) source;
MDDObj* targetObj = targetMDD->getMDDObject();
MDDObj* sourceObj = sourceMDD->getMDDObject();
// test, if target is a persistent object
if( !targetObj->isPersistent() )
{
RMInit::logOut << "Error: QtUpdate::evaluate() - result of target expression must be an assignable value (l-value)." << endl;
// delete tupel vector received by next()
for( vector::iterator dataIter=nextTupel->begin();
dataIter!=nextTupel->end(); dataIter++ )
if( *dataIter ) (*dataIter)->deleteRef();
delete nextTupel;
nextTupel=NULL;
// delete the operands
if( target ) target->deleteRef();
if( source ) source->deleteRef();
parseInfo.setErrorNo(954);
throw parseInfo;
}
// get optional domain
QtData* domainData = NULL;
r_Minterval domain;
if( updateDomain )
{
domainData = updateDomain->evaluate( nextTupel );
if( domainData )
domain = ((QtMintervalData*)domainData)->getMintervalData();
}
RMDBGIF( 1, RMDebug::module_qlparser, "QtUpdate", \
if( domainData ) \
RMInit::dbgOut << endl << " target MDD, domain " << domain << endl; \
else \
RMInit::dbgOut << endl << " target MDD" << endl; \
targetMDD->printStatus( RMInit::dbgOut ); \
RMInit::dbgOut << endl << " source MDD" << endl; \
sourceMDD->printStatus( RMInit::dbgOut ); \
RMInit::dbgOut << endl; \
)
// 1st update strategy:
//
// 1. All cell values of the source domain which are already defined are
// updated with the corresponding new values.
// 2. If a source tile does not intersect with any tile of the target
// object, it is inserted.
// In case of update domain existence, test for compatibility.
if( domainData )
{
// Dimensionality of the udate domain specification has to be equal to
// the target MDD dimensionality.
if( domain.dimension() != targetMDD->getLoadDomain().dimension() )
{
RMInit::logOut << "Error: QtUpdate::evaluate() - Update domain dimensionality must match target MDD dimensionaltiy." << endl;
// delete tupel vector received by next()
for( vector::iterator dataIter=nextTupel->begin();
dataIter!=nextTupel->end(); dataIter++ )
if( *dataIter ) (*dataIter)->deleteRef();
delete nextTupel;
nextTupel=NULL;
// delete the operands
if( target ) target->deleteRef();
if( domainData ) domainData->deleteRef();
if( source ) source->deleteRef();
parseInfo.setErrorNo(963);
throw parseInfo;
}
// The number of interval dimension of the update domain has to be
// equal to the number of dimensions of the source domain.
int updateIntervals = 0;
const vector* trimFlags = ((QtMintervalData*)domainData)->getTrimFlags();
for( int i=0; isize(); i++ )
if( (*trimFlags)[i] )
updateIntervals++;
if( updateIntervals != sourceMDD->getLoadDomain().dimension() )
{
RMInit::logOut << "Error: QtUpdate::evaluate() - Number of update intervals must match source dimensionality." << endl;
// delete tupel vector received by next()
for( vector::iterator dataIter=nextTupel->begin();
dataIter!=nextTupel->end(); dataIter++ )
if( *dataIter ) (*dataIter)->deleteRef();
delete nextTupel;
nextTupel=NULL;
// delete the operands
if( target ) target->deleteRef();
if( domainData ) domainData->deleteRef();
if( source ) source->deleteRef();
parseInfo.setErrorNo(962);
throw parseInfo;
}
// Warning: Fixed bounds in update domain specifications are ignored.
}
r_Minterval sourceMDDDomain( sourceMDD->getLoadDomain() );
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "source mdd domain " << sourceMDDDomain )
// compute source MDD domain taking into account update domain
if( domainData )
{
const vector* trimFlags = ((QtMintervalData*)domainData)->getTrimFlags();
r_Minterval newSourceMDDDomain( targetMDD->getLoadDomain().dimension() );
for( int i=0, j=0; isize(); i++ )
if( (*trimFlags)[i] )
newSourceMDDDomain << sourceMDDDomain[j++];
else
newSourceMDDDomain << domain[i];
sourceMDDDomain = newSourceMDDDomain;
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "source update domain " << sourceMDDDomain )
}
// check if you may update that are ( you should not go out of bounds for mdddomaintype )
if (!targetObj->getMDDBaseType()->compatibleWithDomain(&sourceMDDDomain))
{
RMInit::logOut << "Error: QtUpdate::evaluate() - The update domain is outside the allowed domain of the target mdd." << endl;
// delete tupel vector received by next()
for( vector::iterator dataIter=nextTupel->begin();
dataIter!=nextTupel->end(); dataIter++ )
if( *dataIter ) (*dataIter)->deleteRef();
delete nextTupel;
nextTupel=NULL;
// delete the operands
if( target ) target->deleteRef();
if( domainData ) domainData->deleteRef();
if( source ) source->deleteRef();
parseInfo.setErrorNo(953);
throw parseInfo;
}
//
// get all source tiles
//
vector* sourceTiles = sourceObj->getTiles();
RMDBGIF(1, RMDebug::module_qlparser, "QtUpdate", \
if (sourceTiles) \
RMDBGMIDDLE(1, RMDebug::module_qlparser, "QtUpdate", "there are " << sourceTiles->size() << " source tiles") \
else \
RMDBGMIDDLE(1, RMDebug::module_qlparser, "QtUpdate", "there are no source tiles") )
//
// get all target tiles in the relevant area
//
vector* targetTiles = NULL;
//this is not a very good idea
// try{
targetTiles = targetObj->intersect( sourceMDDDomain );
/* }
catch(...)
{
RMInit::logOut << "BUG of MDDObj::intersect" << endl;
targetTiles = new vector;
}
*/
if( !targetTiles )
{
targetTiles = new vector;
RMDBGMIDDLE(1, RMDebug::module_qlparser, "QtUpdate", "found no target tiles")
}
else {
RMDBGMIDDLE(1, RMDebug::module_qlparser, "QtUpdate", "found " << targetTiles->size() << " target tiles in mdd")
}
//
// iterate over source tiles
//
unsigned long targetTileArea = 0;
unsigned long sourceTileArea = 0;
unsigned long targetTileDomain = 0;
unsigned long updatedArea = 0;
bool computed = false;
vector insertedDomains;
vector sourceDomains;
vector targetDomains;
vector::iterator domIt;
vector::iterator intervalIt;
vector retval;
vector::iterator retvalIt;
vector::iterator sourceIt;
vector::iterator targetIt;
sourceIt = sourceTiles->begin();
//this lives here because we don't want memory leaks because of exceptions
//of course we seldom use this operation ( as we use copy tile most of the time )
UnaryOp* tempOp = NULL;
if (sourceIt != sourceTiles->end())
tempOp = Ops::getUnaryOp(Ops::OP_IDENTITY, targetObj->getCellType(), (*sourceIt)->getType(), 0, 0);
std::auto_ptr myOp(tempOp);
for( ; sourceIt != sourceTiles->end(); sourceIt++ )
{
// calculate relevant area of source tile
r_Minterval sourceTileDomain = (*sourceIt)->getDomain().create_intersection( sourceMDD->getLoadDomain() );
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "original source tile domain " << sourceTileDomain )
// compute update source tile domain taking into account update domain
r_Minterval updateSourceTileDomain;
if( domainData )
{
updateSourceTileDomain = r_Minterval( targetMDD->getLoadDomain().dimension() );
const vector* trimFlags = ((QtMintervalData*)domainData)->getTrimFlags();
for( int i=0, j=0; isize(); i++ )
if( (*trimFlags)[i] )
updateSourceTileDomain << sourceTileDomain[j++];
else
updateSourceTileDomain << domain[i];
}
else
updateSourceTileDomain = sourceTileDomain;
//calculate number of cells in this area
sourceTileArea = sourceTileArea + sourceTileDomain.cell_count();
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "update source tile domain " << updateSourceTileDomain )
bool intersection = false;
for( targetIt = targetTiles->begin(); targetIt != targetTiles->end(); targetIt++ )
{
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "target tile domain " << (*targetIt)->getDomain())
if (!computed)
{
targetTileArea = targetTileArea + sourceMDDDomain.create_intersection((*targetIt)->getDomain()).cell_count();
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "target area sum " << targetTileArea)
}
// if tiles are intersecting
if( updateSourceTileDomain.intersects_with( (*targetIt)->getDomain() ) )
{
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "source tile domain " << updateSourceTileDomain << " intersects with target domain " << (*targetIt)->getDomain())
intersection = true;
// get intersecting updateSourceTileDomain
r_Minterval intersectUpdateSourceTileDomain = updateSourceTileDomain.create_intersection( (*targetIt)->getDomain() );
// compute corresponding sourceTileDomain
r_Minterval intersectSourceTileDomain = intersectUpdateSourceTileDomain;
if( domainData )
{
const vector* trimFlags = ((QtMintervalData*)domainData)->getTrimFlags();
for( int i=0, j=0; isize(); i++ )
if( !((*trimFlags)[i]) )
intersectSourceTileDomain.delete_dimension( j );
else
j++;
}
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "update domains: target tile " << (*targetIt)->getDomain() << " update target at " << intersectUpdateSourceTileDomain << ", source tile " << (*sourceIt)->getDomain() << " update with data at " << intersectSourceTileDomain )
//(*targetIt)->execUnaryOp( Ops::OP_IDENTITY, intersectUpdateSourceTileDomain, *sourceIt, intersectSourceTileDomain );
(*targetIt)->copyTile(intersectUpdateSourceTileDomain, *sourceIt, intersectSourceTileDomain);
updatedArea = updatedArea + intersectUpdateSourceTileDomain.cell_count();
}
}
computed = true;
// insert the tile
if (!intersection)
{
// Create a new persistent tile, copy the transient data,
// and insert it into the target mdd object.
Tile* newPersTile = new Tile(updateSourceTileDomain, targetObj->getCellType(), (*sourceIt)->getDataFormat());
if (updateSourceTileDomain.dimension() == sourceTileDomain.dimension())
newPersTile->copyTile( updateSourceTileDomain, *sourceIt, sourceTileDomain );
else
newPersTile->execUnaryOp( &(*myOp), updateSourceTileDomain, *sourceIt, sourceTileDomain );
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "update domains: target tile " << newPersTile->getDomain() << " update target at " << updateSourceTileDomain << ", source tile " << (*sourceIt)->getDomain() << " update with data at " << sourceTileDomain )
// this will make a crash in updateset3 because of a strange triming doman
//newPersTile->copyTile(updateSourceTileDomain, *sourceIt, sourceTileDomain);
targetObj->insertTile( newPersTile );
updatedArea = updatedArea + updateSourceTileDomain.cell_count();
insertedDomains.push_back((*sourceIt)->getDomain());
}
}//for is done
if (sourceTileArea > updatedArea)
{
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "the source area was not updated completely, tiling the rest")
while (!insertedDomains.empty())
{
intervalIt = insertedDomains.begin();
for (sourceIt = sourceTiles->begin(); sourceIt != sourceTiles->end(); sourceIt++)
if ((*sourceIt)->getDomain() == *intervalIt)
{
sourceTiles->erase(sourceIt);
break;
}
insertedDomains.erase(intervalIt);
}
for (sourceIt = sourceTiles->begin(); sourceIt != sourceTiles->end(); sourceIt++)
sourceDomains.push_back((*sourceIt)->getDomain());
for (targetIt = targetTiles->begin(); targetIt != targetTiles->end(); targetIt++)
targetDomains.push_back((*targetIt)->getDomain());
r_Tiler t(sourceDomains, targetDomains);
t.split();
t.removeDoubleDomains();
t.removeCoveredDomains();
t.mergeDomains();
retval = t.generateTiles(*sourceTiles);
for (retvalIt = retval.begin(); retvalIt != retval.end(); retvalIt++)
{
targetObj->insertTile((Tile*)(*retvalIt));
}
RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QtUpdate", "insertion of the rest is done")
}
// delete tile vectors
delete sourceTiles;
sourceTiles=NULL;
delete targetTiles;
targetTiles=NULL;
// delete optional operand
if( domainData ) domainData->deleteRef();
}
else
RMInit::logOut << "Error: QtUpdate::evaluate() - target or source is not provided." << endl;
// delete the operands
if( target ) target->deleteRef();
if( source ) source->deleteRef();
// delete tupel vector received by next()
for( vector::iterator dataIter=nextTupel->begin();
dataIter!=nextTupel->end(); dataIter++ )
if( *dataIter ) (*dataIter)->deleteRef();
delete nextTupel;
nextTupel=NULL;
} // while
}
catch( ... )
{
input->close();
throw;
}
input->close();
}
else
RMInit::logOut << "Error: QtUpdate::evaluate() - at least one operand branch is invalid." << endl;
return 0;
}
QtNode::QtNodeList*
QtUpdate::getChilds( QtChildType flag )
{
RMDBCLASS( "QtUpdate", "getChilds( QtChildType flag )", "qlparser", __FILE__, __LINE__ )
QtNodeList* resultList=NULL;
// allocate resultList
resultList = new QtNodeList();
if( flag == QT_LEAF_NODES || flag == QT_ALL_NODES )
{
if( input )
{
QtNodeList* subList = input->getChilds( flag );
// remove all elements in subList and insert them at the beginning in resultList
resultList->splice( resultList->begin(), *subList );
// delete temporary subList
delete subList;
subList=NULL;
}
if( updateTarget )
{
QtNodeList* subList = updateTarget->getChilds( flag );
// remove all elements in subList and insert them at the beginning in resultList
resultList->splice( resultList->begin(), *subList );
// delete temporary subList
delete subList;
subList=NULL;
}
if( updateDomain )
{
QtNodeList* subList = updateDomain->getChilds( flag );
// remove all elements in subList and insert them at the beginning in resultList
resultList->splice( resultList->begin(), *subList );
// delete temporary subList
delete subList;
subList=NULL;
}
if( updateSource )
{
QtNodeList* subList = updateSource->getChilds( flag );
// remove all elements in subList and insert them at the beginning in resultList
resultList->splice( resultList->begin(), *subList );
// delete temporary subList
delete subList;
subList=NULL;
}
};
// add the nodes of the current level
if( flag == QT_DIRECT_CHILDS || flag == QT_ALL_NODES )
{
resultList->push_back( input );
resultList->push_back( updateTarget );
resultList->push_back( updateDomain );
resultList->push_back( updateSource );
}
return resultList;
}
void
QtUpdate::printTree( int tab, ostream& s, QtChildType mode )
{
s << SPACE_STR(tab).c_str() << "QtUpdate Object" << endl;
if( mode != QtNode::QT_DIRECT_CHILDS )
{
if( updateTarget )
{
s << SPACE_STR(tab).c_str() << "target: " << endl;
updateTarget->printTree( tab + 2, s );
if( updateDomain )
{
s << SPACE_STR(tab+2).c_str() << "domain: " << endl;
updateDomain->printTree( tab + 4, s );
}
else
s << SPACE_STR(tab+2).c_str() << "no domain" << endl;
}
else
s << SPACE_STR(tab).c_str() << "no target" << endl;
if( updateSource )
{
s << SPACE_STR(tab).c_str() << "source: " << endl;
updateSource->printTree( tab + 2, s );
}
else
s << SPACE_STR(tab).c_str() << "no source" << endl;
if( input )
{
s << SPACE_STR(tab).c_str() << "input: " << endl;
input->printTree( tab+2, s, mode );
}
else
s << SPACE_STR(tab).c_str() << "no input" << endl;
s << endl;
}
}
void
QtUpdate::printAlgebraicExpression( ostream& s )
{
s << "update<" << std::flush;
if( updateTarget )
updateTarget->printAlgebraicExpression( s );
else
s << "";
if( updateDomain )
updateDomain->printAlgebraicExpression( s );
s << "," << std::flush;
if( updateSource )
updateSource->printAlgebraicExpression( s );
else
s << "";
s << ">" << std::flush;
if( input )
{
s << "( ";
input->printAlgebraicExpression( s );
s << " )";
}
else
s << "()" << std::flush;
}
void
QtUpdate::setStreamInput( QtONCStream* newInput )
{
input = newInput;
input->setParent( this );
};
QtOperation*
QtUpdate::getUpdateTarget()
{
return updateTarget;
}
QtOperation*
QtUpdate::getUpdateDomain()
{
return updateDomain;
}
QtOperation*
QtUpdate::getUpdateSource()
{
return updateSource;
}
QtONCStream*
QtUpdate::getInput()
{
return input;
}
/*
void
QtUpdate::preOptimize()
{
if( updateTarget )
updateTarget->optimizeLoad( new QtNode::QtTrimList );
if( updateDomain )
updateDomain->optimizeLoad( new QtNode::QtTrimList );
if( updateSource )
updateSource->optimizeLoad( new QtNode::QtTrimList );
if( input )
input->preOptimize();
}
*/
void
QtUpdate::checkType()
{
RMDBCLASS( "QtUpdate", "checkType()", "qlparser", __FILE__, __LINE__ )
// check operand branches
if( updateTarget && updateSource && input )
{
// get input type
QtTypeTuple inputType = input->checkType();
// check target
const QtTypeElement& targetType = updateTarget->checkType( &inputType );
if( targetType.getDataType() != QT_MDD )
{
RMInit::logOut << "Error: QtUpdate::checkType() - update target must be an iterator variable." << endl;
parseInfo.setErrorNo(950);
throw parseInfo;
}
// check domain
if( updateDomain )
{
const QtTypeElement& domainType = updateDomain->checkType( &inputType );
if( domainType.getDataType() != QT_MINTERVAL )
{
RMInit::logOut << "Error: QtUpdate::checkType() - update domain must be of type Minterval." << endl;
parseInfo.setErrorNo(961);
throw parseInfo;
}
}
// check source
const QtTypeElement& sourceType = updateSource->checkType( &inputType );
if( sourceType.getDataType() != QT_MDD )
{
RMInit::logOut << "Error: QtUpdate::checkType() - update source must be an expression resulting in an MDD." << endl;
parseInfo.setErrorNo(951);
throw parseInfo;
}
// test for compatible base types
bool compatible = false;
char* type1 = ((MDDBaseType*)(targetType.getType()))->getBaseType()->getTypeStructure();
char* type2 = ((MDDBaseType*)(sourceType.getType()))->getBaseType()->getTypeStructure();
compatible = (strcmp(type1, type2) == 0);
free(type1);
free(type2);
type1 = NULL;
type2 = NULL;
if( !compatible )
{
RMInit::logOut << "Error: QtUpdate::checkType() - update base type does not match mdd base type." << endl;
parseInfo.setErrorNo(952);
throw parseInfo;
}
}
else
RMInit::logOut << "Error: QtUpdate::checkType() - operand branch invalid." << endl;
}