/* * 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 . */ /************************************************************* * * SOURCE: querytree.cc * * MODULE: qlparser * CLASS: QueryTree * * PURPOSE: * * COMMENTS: * ************************************************************/ static const char rcsid[] = "@(#)qlparser, QueryTree: $Id: querytree.cc,v 1.52 2005/06/28 08:42:13 rasdev Exp $"; #ifndef CPPSTDLIB #include // STL #else #include using namespace std; #endif #include #include #include "raslib/rmdebug.hh" #include "globals.hh" #include "qlparser/querytree.hh" #include "qlparser/qtnode.hh" #include "qlparser/qtoperationiterator.hh" #include "qlparser/qtselectioniterator.hh" #include "qlparser/qtjoiniterator.hh" #include "qlparser/qtvariable.hh" #include "qlparser/qtmdd.hh" #include "qlparser/qtdomainoperation.hh" #include "qlparser/qtexecute.hh" #include "catalogmgr/typefactory.hh" #include "relcatalogif/mdddomaintype.hh" #include "relcatalogif/settype.hh" void (*QueryTree::optimizationFnc)(unsigned int, QtNode*) = NULL; unsigned int QueryTree::nextCSENo = 0; SymbolTable QueryTree::symtab; QueryTree::QueryTree() : rootNode(NULL), optimizationLevel(SUBEXPRESSIONS) { } QueryTree::QueryTree( QtNode* root ) : rootNode( root ), optimizationLevel(SUBEXPRESSIONS) { } QueryTree::~QueryTree() { if( rootNode ) { delete rootNode; rootNode=NULL; } releaseDynamicObjects(); } void QueryTree::checkSemantics() { RMDBCLASS( "QueryTree", "checkSemantics()", "qlparser", __FILE__, __LINE__ ) switch( rootNode->getNodeType() ) { case QtNode::QT_MDD_ACCESS: case QtNode::QT_OPERATION_ITERATOR: case QtNode::QT_JOIN_ITERATOR: case QtNode::QT_SELECTION_ITERATOR: { const QtTypeTuple& resultType = ((QtONCStream*)rootNode)->checkType(); // RMInit::logOut << "result type: " << flush; // resultType.printStatus( RMInit::logOut ); } break; case QtNode::QT_UPDATE: case QtNode::QT_INSERT: case QtNode::QT_DELETE: case QtNode::QT_COMMAND: case QtNode::QT_PYRAMID: ((QtExecute*)rootNode)->checkType(); break; default: { const QtTypeElement& resultType = ((QtOperation*)rootNode)->checkType(); // RMInit::logOut << "result type: " << flush; // resultType.printStatus( RMInit::logOut ); } break; } } void QueryTree::optimize( unsigned int currentOptimizationLevel ) { RMDBCLASS( "QueryTree", "optimize( unsigned int )", "qlparser", __FILE__, __LINE__ ) if (optimizationFnc == NULL) { char *dir = CONFDIR; char libName[255]; sprintf(libName,"%s/lib/libqloptimizer.so", dir); if (access(libName, X_OK | R_OK) == 0) { void *handle = dlopen(libName, RTLD_NOW); if (handle == NULL) { RMInit::logOut << "Optimization library found, however could not be loaded" << endl; printf("DLERROR = %s\n", dlerror()); RMInit::logOut << dlerror() << endl; return; } *(void **)(&optimizationFnc) = dlsym(handle, "runOptimizations"); if (optimizationFnc == NULL) { RMInit::logOut << "Optimization library found, however the entry point was not found" << endl; return; } } else { RMInit::logOut << "No optimization library found" << endl; return; } } if (isValidOptimizationLevel(currentOptimizationLevel) == false) throw r_Error(r_Error::r_Error_InvalidOptimizationLevel); optimizationFnc(currentOptimizationLevel, rootNode); RMDBGIF( 1, RMDebug::module_qlparser, "QueryTree", \ RMInit::logOut << endl << " "; \ rootNode->printAlgebraicExpression( RMInit::logOut ); \ RMInit::logOut << endl << endl; \ rootNode->printTree( 2, RMInit::logOut ); \ RMInit::logOut << endl; \ ) } vector* QueryTree::evaluateRetrieval() throw (r_Error, ParseInfo) { vector* returnValue=NULL; if( rootNode ) { if( rootNode->getNodeType() != QtNode::QT_MDD_ACCESS && rootNode->getNodeType() != QtNode::QT_OPERATION_ITERATOR && rootNode->getNodeType() != QtNode::QT_JOIN_ITERATOR && rootNode->getNodeType() != QtNode::QT_SELECTION_ITERATOR ) { RMInit::logOut << "QueryTree::evaluateRetrieval() - Retrieval query must start with an ONC node." << endl; ParseInfo errorInfo = rootNode->getParseInfo(); errorInfo.setErrorNo(371); throw errorInfo; } QtNode::QtDataList* dataList=NULL; QtNode::QtDataList::iterator dataIter; QtONCStream* oncRootNode = (QtONCStream*)rootNode; try { oncRootNode->open(); } catch( ... ) { oncRootNode->close(); RMInit::logOut << "QueryTree::evaluateRetrieval() - rethrow exception from oncRootNode->open()." << endl; throw; } // removed to have uniform, compact log output -- PB 2003-nov-20 // RMInit::logOut << endl; // create result collection vector* resultData = new vector(); try { while( (dataList = oncRootNode->next()) ) { if( dataList->size() > 1 || (*dataList)[0] == NULL ) { // Delete the tupel vector received by next(). Just tupel elements which are not // further referenced are deleted. for( dataIter=dataList->begin(); dataIter!=dataList->end(); dataIter++ ) if( *dataIter ) (*dataIter)->deleteRef(); delete dataList; dataList=NULL; if( resultData ) { // Delete the result vector for( dataIter=resultData->begin(); dataIter!=resultData->end(); dataIter++ ) if( *dataIter ) (*dataIter)->deleteRef(); delete resultData; resultData = NULL; } RMInit::logOut << "QueryTree::evaluateTree() - multiple query targets are not supported." << endl; ParseInfo errorInfo = oncRootNode->getParseInfo(); errorInfo.setErrorNo(361); throw errorInfo; } QtData* resultElement = (*dataList)[0]; // take the data element as result data and reset it in the tupel vector resultData->push_back( resultElement ); (*dataList)[0] = NULL; RMDBGMIDDLE( 2, RMDebug::module_qlparser, "QueryTree", endl << endl << "NEXT RESULT ITEM OF THE QUERY INSERTED" << endl << endl ) // Delete the tupel vector received by next(). Just tupel elements which are not // set to zero and which are not further referenced are deleted. for( dataIter=dataList->begin(); dataIter!=dataList->end(); dataIter++ ) if( *dataIter ) (*dataIter)->deleteRef(); // delete the tuple vector delete dataList; dataList=NULL; } } catch(r_Error& myErr) { RMInit::logOut << endl << "Caught BAD exception when evaluating query! " << endl; RMInit::logOut << myErr.what() << endl; if( resultData ) { // Delete the result vector for( dataIter=resultData->begin(); dataIter!=resultData->end(); dataIter++ ) if( *dataIter ) (*dataIter)->deleteRef(); delete resultData; resultData = NULL; } oncRootNode->close(); RMInit::logOut << "QueryTree::evaluateTree() - rethrow exception." << endl; throw; } catch( ... ) { if( resultData ) { // Delete the result vector for( dataIter=resultData->begin(); dataIter!=resultData->end(); dataIter++ ) if( *dataIter ) (*dataIter)->deleteRef(); delete resultData; resultData = NULL; } oncRootNode->close(); RMInit::logOut << "QueryTree::evaluateTree() - rethrow exception." << endl; throw; } oncRootNode->close(); returnValue = resultData; } return returnValue; } void QueryTree::evaluateUpdate() throw (r_Error,ParseInfo) { if( rootNode ) { if( rootNode->getNodeType() != QtNode::QT_UPDATE && rootNode->getNodeType() != QtNode::QT_INSERT && rootNode->getNodeType() != QtNode::QT_DELETE && rootNode->getNodeType() != QtNode::QT_COMMAND && rootNode->getNodeType() != QtNode::QT_PYRAMID ) { RMInit::logOut << "QueryTree::evaluateUpdate() - update query must start with an INSERT, UPDATE, DELETE, DROP or CREATE statement." << endl; ParseInfo errorInfo = rootNode->getParseInfo(); errorInfo.setErrorNo(372); throw errorInfo; } QtExecute* executeNode = (QtExecute*) rootNode; // evaluate the update query executeNode->evaluate(); } } void QueryTree::printTree( int tab, ostream& s ) { if ( rootNode ) { s << SPACE_STR(tab).c_str() << "QueryTree:" << endl; rootNode->printTree( tab + 2, s ); } else s << SPACE_STR(tab).c_str() << "QueryTree: Qt has no root node." << endl; } void QueryTree::addDynamicObject( QtNode *node ) { qtNodeList.push_back( node ); } void QueryTree::removeDynamicObject( QtNode *node ) { qtNodeList.remove( node ); } void QueryTree::addDynamicObject( QtData *node ) { qtDataList.push_back( node ); } void QueryTree::removeDynamicObject( QtData *node ) { qtDataList.remove( node ); } void QueryTree::addDynamicObject( ParseInfo *node ) { parseInfoList.push_back( node ); } void QueryTree::removeDynamicObject( ParseInfo *node ) { parseInfoList.remove( node ); } void QueryTree::addDynamicObject( vector *node ) { vectorList.push_back( node ); } void QueryTree::removeDynamicObject( vector *node ) { vectorList.remove( node ); } void QueryTree::releaseDynamicObjects() { list::iterator iter1; for( iter1 = qtNodeList.begin(); iter1 != qtNodeList.end(); iter1++ ) { delete *iter1; *iter1=NULL; } list::iterator iter2; for( iter2 = qtDataList.begin(); iter2 != qtDataList.end(); iter2++ ) { delete *iter2; *iter2=NULL; } list::iterator iter3; for( iter3 = parseInfoList.begin(); iter3 != parseInfoList.end(); iter3++ ) { delete *iter3; *iter3=NULL; } list*>::iterator iter4; for( iter4 = vectorList.begin(); iter4 != vectorList.end(); iter4++ ) { delete *iter4; *iter4=NULL; } list::iterator iter5; for( iter5 = lexedCStringList.begin(); iter5 != lexedCStringList.end(); iter5++ ) { free(*iter5); *iter5=NULL; } } void QueryTree::addDomainObject( QtDomainOperation *dop ) { dopList.push_back( dop ); } void QueryTree::removeDomainObject( QtDomainOperation *dop ) { dopList.remove( dop ); } void QueryTree::printDomainObjects() { list::iterator iter; for( iter = dopList.begin(); iter != dopList.end(); iter++ ) { cout << endl; (*iter)->printTree( 2 ); } } void QueryTree::releaseDomainObjects() { list::iterator iter; for( iter = dopList.begin(); iter != dopList.end(); iter++ ) { delete *iter; *iter=NULL; } } void QueryTree::rewriteDomainObjects(r_Minterval *greatDomain, string *greatIterator, QtMarrayOp2::mddIntervalListType *greatList) { RMDBGENTER( 2, RMDebug::module_qlparser, "QueryTree", endl << "QueryTree: Iterator: <" << *greatIterator << "> Domain: " << *greatDomain << endl ) list::iterator iter; for( iter = dopList.begin(); iter != dopList.end(); iter++ ) { // 1. get var name from iter QtVariable *qtVar = ((QtVariable *)((*iter)->getInput())); string stVar = qtVar->getIteratorName(); const char *varname = stVar.c_str(); // 2. get position of varname in varList bool bcond = false; QtMarrayOp2::mddIntervalListType *varList = greatList; QtMarrayOp2::mddIntervalListType::iterator varIter; r_Long varpos = 0; for (varIter = varList->begin(); varIter != varList->end(); varIter++) { if (!strcmp(varname, varIter->variable.c_str())) { bcond = true; break; }; QtData *data = varIter->tree->evaluate(0); r_Dimension dimension = ((QtMintervalData*)data)->getMintervalData().dimension(); varpos = varpos+dimension; }; // postcond: bcond == false: varname not found in list. else varpos gives the position. if (bcond) { // 3. set domain expression to old one incremented by varpos QtNode::QtOperationList *lop = new QtNode::QtOperationList(1); (*lop)[0] = new QtPlus( (*iter)->getMintervalOp(), new QtConst( new QtAtomicData( varpos, sizeof(varpos) ) ) ); (*iter)->setMintervalOp( new QtPointOp( lop ) ); // 4. set varname to greatIterator QtVariable *var1 = new QtVariable( string(*greatIterator) ); (*iter)->setInput(var1); } else { // TODO: insert some error notify code here! RMDBGMIDDLE( 1, RMDebug::module_qlparser, "QueryTree", " variable name not found in list " ) } } } void QueryTree::addCString( char *str ) { lexedCStringList.push_back( str ); } bool QueryTree::isValidOptimizationLevel( int level ) { switch (level) { case NO_OPTIMIZATIONS: case STANDARDIZATION: case SIMPLIFICATION: case SUBEXPRESSIONS: return true; default: return false; } }