/* ---- This file is part of SECONDO. Copyright (C) 2004-2008, University in Hagen, 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 ---- //paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}] //paragraph [10] Footnote: [{\footnote{] [}}] //[TOC] [\tableofcontents] //[_] [\_] //[x] [\ensuremath{\times}] //[->] [\ensuremath{\rightarrow}] //[>] [\ensuremath{>}] //[<] [\ensuremath{<}] 1.1 Implementation of Outerjoin for Module Extended Relation Algebra 1.1 Using Storage Manager Berkeley DB January 2008, B. Poneleit 1.1.1 Includes and defines */ #include #include #include #include //#define TRACE_ON #undef TRACE_ON #include "LogMsg.h" #define TRACE_OFF #include "SortByLocalInfo.h" #include "StandardTypes.h" #include "Algebras/Relation-C++/RelationAlgebra.h" #include "CPUTimeMeasurer.h" #include "QueryProcessor.h" #include "SecondoInterface.h" #include "StopWatch.h" #include "Counter.h" #include "Progress.h" #include "RTuple.h" #include "Tupleorder.h" #include "ListUtils.h" #include "Algebras/Hash/HashAlgebra.h" extern NestedList* nl; extern QueryProcessor* qp; /* 2.2.1 Operator ~smouterjoin~ This operator computes the equijoin of two streams. It uses a text book algorithm as outlined in A. Silberschatz, H. F. Korth, S. Sudarshan, McGraw-Hill, 3rd. Edition, 1997. 2.2.1.1 Auxiliary definitions for value mapping function of operator ~smouterjoin~ */ #ifndef USE_PROGRESS /* 2.2.2.1 Value mapping function of operator ~mergeouterjoin~ */ //-- begin standard version --// class MergeOuterjoinLocalInfo { private: // buffer limits size_t MAX_MEMORY; size_t MAX_TUPLES_IN_MEMORY; // buffer related members TupleBuffer *grpB; GenericRelationIterator *iter; // members needed for sorting the input streams LocalInfo* liA; SortByLocalInfo* sliA; LocalInfo* liB; SortByLocalInfo* sliB; Word streamA; Word streamB; // the current pair of tuples Word resultA; Word resultB; RTuple ptA; RTuple ptB; RTuple tmpB; // the last comparison result int cmp; // the indexes of the attributes which will // be merged and the result type int attrIndexA; int attrIndexB; TupleType *resultTupleType; // a flag which indicates if sorting is needed bool expectSorted; // switch trace messages on/off const bool traceFlag; // a flag needed in function NextTuple which tells // if the merge with grpB has been finished bool continueMerge; template int CompareTuples(Tuple* t1, Tuple* t2) { Attribute* a = 0; if (BOTH_B) a = static_cast( t1->GetAttribute(attrIndexB) ); else a = static_cast( t1->GetAttribute(attrIndexA) ); Attribute* b = static_cast( t2->GetAttribute(attrIndexB) ); /* tuples with NULL-Values in the join attributes are never matched with other tuples. */ if( !a->IsDefined() ) { return -1; } if( !b->IsDefined() ) { return 1; } int cmp = a->Compare(b); if (traceFlag) { cmsg.info() << "CompareTuples:" << endl << " BOTH_B = " << BOTH_B << endl << " tuple_1 = " << *t1 << endl << " tuple_2 = " << *t2 << endl << " cmp(t1,t2) = " << cmp << endl; cmsg.send(); } return cmp; } inline int CompareTuplesB(Tuple* t1, Tuple* t2) { return CompareTuples(t1, t2); } inline int CompareTuples(Tuple* t1, Tuple* t2) { return CompareTuples(t1, t2); } inline Tuple* NextTuple(Word stream, SortByLocalInfo* sli) { bool yield = false; Word result( Address(0) ); if(!expectSorted) return sli->NextResultTuple(); qp->Request(stream.addr, result); yield = qp->Received(stream.addr); if(yield) { return static_cast( result.addr ); } else { result.addr = 0; return static_cast( result.addr ); } } inline Tuple* NextTupleA() { return NextTuple(streamA, sliA); } inline Tuple* NextTupleB() { return NextTuple(streamB, sliB); } inline Tuple* NextUndefinedA() { Tuple* result = 0; progress->readFirst++; if (undefA == 0) { // create tuple with undefined values result = new Tuple( tupleTypeA ); for (int i = 0; i < result->GetNoAttributes(); i++) { int algId = tupleTypeA->GetAttributeType(i).algId; int typeId = tupleTypeA->GetAttributeType(i).typeId; // create an instance of the specified type, which gives // us an instance of a subclass of class Attribute. Attribute* attr = static_cast( am->CreateObj(algId, typeId)(0).addr ); attr->SetDefined( false ); result->PutAttribute( i, attr ); } undefA = result; } else { result = undefA; } return result; } inline Tuple* NextUndefinedB() { Tuple* result = 0; progress->readSecond++; if (undefB == 0) { // create tuple with undefined values result = new Tuple( tupleTypeB ); for (int i = 0; i < result->GetNoAttributes(); i++) { int algId = tupleTypeB->GetAttributeType(i).algId; int typeId = tupleTypeB->GetAttributeType(i).typeId; // create an instance of the specified type, which gives // us an instance of a subclass of class Attribute. Attribute* attr = static_cast( am->CreateObj(algId, typeId)(0).addr ); attr->SetDefined( false ); result->PutAttribute( i, attr ); } undefB = result; } else { result = undefB; } return result; } public: MergeOuterjoinLocalInfo( Word _streamA, Word wAttrIndexA, Word _streamB, Word wAttrIndexB, bool _expectSorted, Supplier s ) : traceFlag( RTFlag::isActive("ERA:TraceMergeOuterjoin") ) { expectSorted = _expectSorted; streamA = _streamA; streamB = _streamB; attrIndexA = StdTypes::GetInt( wAttrIndexA ) - 1; attrIndexB = StdTypes::GetInt( wAttrIndexB ) - 1; MAX_MEMORY = 0; sliA = 0; sliB = 0; if( !expectSorted ) { // sort the input streams SortOrderSpecification specA; SortOrderSpecification specB; specA.push_back( std::pair(attrIndexA + 1, true) ); specB.push_back( std::pair(attrIndexB + 1, true) ); void* tupleCmpA = new TupleCompareBy( specA ); void* tupleCmpB = new TupleCompareBy( specB ); sliA = new SortByLocalInfo( streamA, false, tupleCmpA ); sliB = new SortByLocalInfo( streamB, false, tupleCmpB ); } ListExpr resultType = qp->GetNumType(s); resultTupleType = new TupleType( nl->Second( resultType ) ); // read in the first tuple of both input streams ptA = RTuple( NextTupleA() ); ptB = RTuple( NextTupleB() ); // initialize the status for the result // set iteration tmpB = 0; cmp = 0; continueMerge = false; MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024); grpB = new TupleBuffer( MAX_MEMORY ); cmsg.info("ERA:ShowMemInfo") << "MergeOuterjoin.MAX_MEMORY (" << MAX_MEMORY/1024 << " kb)" << endl; cmsg.send(); } ~MergeOuterjoinLocalInfo() { if( !expectSorted ) { // delete the objects instantiated for sorting delete sliA; delete sliB; } delete grpB; resultTupleType->DeleteIfAllowed(); } Tuple* NextResultTuple() { Tuple* resultTuple = 0; while ( ptA != 0 || ptB != 0 ) { if (ptA != 0 && (ptB != 0 || tmpB != 0)) { if (tmpB != 0) { cmp = CompareTuples( ptA.tuple, tmpB.tuple ); } else cmp = CompareTuples( ptA.tuple, ptB.tuple ); /*if ( !outerJoin && tmpB == 0 ) { // create initial group for inner join if (!continueMerge && ptB != 0) { //save ptB in tmpB tmpB = ptB; grpB->AppendTuple(tmpB.tuple); // advance the tuple pointer ptB.setTuple( NextTupleB() ); // collect a group of tuples from B which // have the same attribute value bool done = false; while ( !done && ptB != 0 ) { int cmp = CompareTuplesB( tmpB.tuple, ptB.tuple ); if ( cmp == 0) { // append equal tuples to group grpB->AppendTuple(ptB.tuple); // release tuple of input B ptB.setTuple( NextTupleB() ); } else { done = true; } } // end collect group cmp = CompareTuples( ptA.tuple, tmpB.tuple ); } }*/ if ( cmp < 0 ) { // ptA < ptB //if ( outerJoin ) { continueMerge = false; resultTuple = NextConcatB(); ptA.setTuple( NextTupleA() ); if (resultTuple) { return resultTuple; } //} /*else { ptA.setTuple( NextTupleA() ); cmp = CompareTuples( ptA.tuple, tmpB.tuple ); while ( ptA != 0 && cmp < 0 ) { // skip tuples from A while they are smaller than the // value of the tuples in grpB ptA.setTuple( NextTupleA() ); if (ptA != 0) cmp = CompareTuples( ptA.tuple, tmpB.tuple ); } }*/ } else if ( cmp == 0 ) { if (!continueMerge && tmpB == 0) { //save ptB in tmpB tmpB = ptB; grpB->AppendTuple(tmpB.tuple); // advance the tuple pointer ptB.setTuple( NextTupleB() ); // collect a group of tuples from B which // have the same attribute value bool done = false; while ( !done && ptB != 0 ) { //cout << "grpB" << endl; //logTuples(); int cmp = CompareTuplesB( tmpB.tuple, ptB.tuple ); if ( cmp == 0) { // append equal tuples to group grpB->AppendTuple(ptB.tuple); // release tuple of input B ptB.setTuple( NextTupleB() ); } else { done = true; } } // end collect group } // continue or start merge if (!continueMerge) { iter = grpB->MakeScan(); continueMerge = true; resultTuple = NextConcat(); if (resultTuple) { return resultTuple; } } else { //continue merging, create next result tuple resultTuple = NextConcat(); if (resultTuple) { return resultTuple; } else { continueMerge = false; delete iter; iter = 0; ptA.setTuple( NextTupleA() ); if (ptA != 0) { cmp = CompareTuples( ptA.tuple, tmpB.tuple ); if (/*outerJoin && */cmp != 0) { tmpB = 0; grpB->Clear(); } } } } } // end of merge else /*if ( outerJoin )*/ { // ptA > ptB continueMerge = false; resultTuple = NextConcatA(); ptB.setTuple( NextTupleB() ); if (resultTuple) { return resultTuple; } } /*else { if ( ptB == 0 ) // short exit return 0; grpB->Clear(); tmpB = 0; }*/ } // end of else if ( /*outerJoin && */ptA != 0 ) { // ptB == 0 resultTuple = NextConcatB(); ptA.setTuple( NextTupleA() ); if (resultTuple) { return resultTuple; } } else /*if ( outerJoin ) / * ptB != 0 */{ // ptA == 0 resultTuple = NextConcatA(); ptB.setTuple( NextTupleB() ); if (resultTuple) { return resultTuple; } } else { // short exit return 0; } } // end of main loop return 0; } inline Tuple* NextConcat() { Tuple* t = iter->GetNextTuple(); if( t != 0 ) { Tuple* result = new Tuple( resultTupleType ); Concat( ptA.tuple, t, result ); return result; } return 0; } inline Tuple* NextConcatB() { Tuple* result = new Tuple( resultTupleType ); Concat( ptA.tuple, NextUndefinedB(), result ); return result; } inline Tuple* NextConcatA() { Tuple* result = new Tuple( resultTupleType ); Concat( NextUndefinedA(), ptB.tuple, result ); return result; } }; /* 2.2.2 Value mapping function of operator ~mergeOuterjoin~ */ //CPUTimeMeasurer mergeMeasurer; template int smouterjoin_vm(Word* args, Word& result, int message, Word& local, Supplier s) { MergeOuterjoinLocalInfo* localInfo; switch(message) { case OPEN: qp->Open(args[0].addr); qp->Open(args[1].addr); localInfo = new MergeOuterjoinLocalInfo (args[0], args[4], args[1], args[5], expectSorted, s); local.setAddr(localInfo); return 0; case REQUEST: //mergeMeasurer.Enter(); localInfo = (MergeOuterjoinLocalInfo*)local.addr; result.setAddr(localInfo->NextResultTuple()); //mergeMeasurer.Exit(); return result.addr != 0 ? YIELD : CANCEL; case CLOSE: //mergeMeasurer.PrintCPUTimeAndReset("CPU Time for Merging Tuples : "); qp->Close(args[0].addr); qp->Close(args[1].addr); localInfo = (MergeOuterjoinLocalInfo*)local.addr; delete localInfo; local.addr = 0; return 0; } return 0; } //-- end standard version --// #else //-- begin progress version --// class MergeOuterjoinLocalInfo: protected ProgressWrapper { private: // buffer limits size_t MAX_MEMORY; // buffer related members TupleBuffer *grpB; GenericRelationIterator *iter; // members needed for sorting the input streams LocalInfo* liA; SortByLocalInfo* sliA; LocalInfo* liB; SortByLocalInfo* sliB; Word streamA; Word streamB; // the current pair of tuples Word resultA; Word resultB; RTuple ptA; RTuple ptB; RTuple tmpB; RTuple lastB; Tuple* undefA; Tuple* undefB; // the last comparison result int cmp; // the indexes of the attributes which will // be merged and the result type int attrIndexA; int attrIndexB; TupleType *resultTupleType; TupleType *tupleTypeB; TupleType *tupleTypeA; // a flag which indicates if sorting is needed bool expectSorted; // switch trace messages on/off const bool traceFlag; // a flag needed in function NextTuple which tells // if the merge with grpB has been finished bool continueMerge; template int CompareTuples(Tuple* t1, Tuple* t2) { Attribute* a = 0; if (BOTH_B) a = static_cast( t1->GetAttribute(attrIndexB) ); else a = static_cast( t1->GetAttribute(attrIndexA) ); Attribute* b = static_cast( t2->GetAttribute(attrIndexB) ); /* tuples with NULL-Values in the join attributes are never matched with other tuples. */ if( !a->IsDefined() ) { return -1; } if( !b->IsDefined() ) { return 1; } int cmp = a->Compare(b); if (traceFlag) { cmsg.info() << "CompareTuples:" << endl << " BOTH_B = " << BOTH_B << endl << " tuple_1 = " << *t1 << endl << " tuple_2 = " << *t2 << endl << " cmp(t1,t2) = " << cmp << endl; cmsg.send(); } return cmp; } inline int CompareTuplesB(Tuple* t1, Tuple* t2) { return CompareTuples(t1, t2); } inline int CompareTuples(Tuple* t1, Tuple* t2) { return CompareTuples(t1, t2); } inline Tuple* NextTuple(Word stream, SortByLocalInfo* sli) { bool yield = false; Word result( Address(0) ); if(!expectSorted) return sli->NextResultTuple(); qp->Request(stream.addr, result); yield = qp->Received(stream.addr); if(yield) { return static_cast( result.addr ); } else { result.addr = 0; return static_cast( result.addr ); } } inline Tuple* NextTupleA() { progress->readFirst++; return NextTuple(streamA, sliA); } inline Tuple* NextTupleB() { progress->readSecond++; return NextTuple(streamB, sliB); } inline Tuple* NextUndefinedA() { Tuple* result = 0; progress->readFirst++; if (undefA == 0) { // create tuple with undefined values result = new Tuple( tupleTypeA ); for (int i = 0; i < result->GetNoAttributes(); i++) { int algId = tupleTypeA->GetAttributeType(i).algId; int typeId = tupleTypeA->GetAttributeType(i).typeId; // create an instance of the specified type, which gives // us an instance of a subclass of class Attribute. Attribute* attr = static_cast( am->CreateObj(algId, typeId)(0).addr ); attr->SetDefined( false ); result->PutAttribute( i, attr ); } undefA = result; } else { result = undefA; } return result; } inline Tuple* NextUndefinedB() { Tuple* result = 0; progress->readSecond++; if (undefB == 0) { // create tuple with undefined values result = new Tuple( tupleTypeB ); for (int i = 0; i < result->GetNoAttributes(); i++) { int algId = tupleTypeB->GetAttributeType(i).algId; int typeId = tupleTypeB->GetAttributeType(i).typeId; // create an instance of the specified type, which gives // us an instance of a subclass of class Attribute. Attribute* attr = static_cast( am->CreateObj(algId, typeId)(0).addr ); attr->SetDefined( false ); result->PutAttribute( i, attr ); } undefB = result; } else { result = undefB; } return result; } public: MergeOuterjoinLocalInfo( Word _streamA, Word wAttrIndexA, Word _streamB, Word wAttrIndexB, bool _expectSorted, Supplier s, ProgressLocalInfo* p ) : ProgressWrapper(p), traceFlag( RTFlag::isActive("ERA:TraceMergeOuterjoin") ) { expectSorted = _expectSorted; streamA = _streamA; streamB = _streamB; attrIndexA = StdTypes::GetInt( wAttrIndexA ) - 1; attrIndexB = StdTypes::GetInt( wAttrIndexB ) - 1; MAX_MEMORY = 0; liA = 0; sliA = 0; liB = 0; sliB = 0; if( !expectSorted ) { // sort the input streams SortOrderSpecification specA; SortOrderSpecification specB; specA.push_back( std::pair(attrIndexA + 1, true) ); specB.push_back( std::pair(attrIndexB + 1, true) ); void* tupleCmpA = new TupleCompareBy( specA ); void* tupleCmpB = new TupleCompareBy( specB ); liA = new LocalInfo(); progress->firstLocalInfo = liA; sliA = new SortByLocalInfo( streamA, false, tupleCmpA, liA, s); liB = new LocalInfo(); progress->secondLocalInfo = liB; sliB = new SortByLocalInfo( streamB, false, tupleCmpB, liB, s ); } ListExpr resultType = qp->GetNumType(s); resultTupleType = new TupleType( nl->Second( resultType ) ); // read in the first tuple of both input streams ptA.setTuple( NextTupleA() ); ptB.setTuple( NextTupleB() ); ListExpr typeA = qp->GetNumType(streamA.addr); ListExpr typeB = qp->GetNumType(streamB.addr); tupleTypeA = new TupleType( nl->Second( typeA ) ); tupleTypeB = new TupleType( nl->Second( typeB ) ); undefA = 0; undefB = 0; // initialize the status for the result // set iteration tmpB = 0; cmp = 0; lastB = 0; continueMerge = false; MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024); grpB = new TupleBuffer( MAX_MEMORY ); cmsg.info("ERA:ShowMemInfo") << "MergeOuterjoin.MAX_MEMORY (" << MAX_MEMORY/1024 << " kb)" << endl; cmsg.send(); } ~MergeOuterjoinLocalInfo() { if ( undefA != 0 ) undefA->DeleteIfAllowed(); if ( undefB != 0 ) undefB->DeleteIfAllowed(); if( !expectSorted ) { // delete the objects instantiated for sorting delete sliA; delete sliB; delete liA; delete liB; } tupleTypeA->DeleteIfAllowed(); tupleTypeB->DeleteIfAllowed(); delete grpB; resultTupleType->DeleteIfAllowed(); } Tuple* NextResultTuple() { Tuple* resultTuple = 0; while ( ptA != 0 || ptB != 0 ) { if (ptA != 0 && (ptB != 0 || tmpB != 0)) { if (tmpB != 0) { cmp = CompareTuples( ptA.tuple, tmpB.tuple ); } else cmp = CompareTuples( ptA.tuple, ptB.tuple ); /*if ( !outerJoin && tmpB == 0 ) { // create initial group for inner join if (!continueMerge && ptB != 0) { //save ptB in tmpB tmpB = ptB; grpB->AppendTuple(tmpB.tuple); // advance the tuple pointer ptB.setTuple( NextTupleB() ); // collect a group of tuples from B which // have the same attribute value bool done = false; while ( !done && ptB != 0 ) { int cmp = CompareTuplesB( tmpB.tuple, ptB.tuple ); if ( cmp == 0) { // append equal tuples to group grpB->AppendTuple(ptB.tuple); // release tuple of input B ptB.setTuple( NextTupleB() ); } else { done = true; } } // end collect group cmp = CompareTuples( ptA.tuple, tmpB.tuple ); } }*/ if ( cmp < 0 ) { // ptA < ptB //if ( outerJoin ) { continueMerge = false; resultTuple = NextConcatB(); ptA.setTuple( NextTupleA() ); if (resultTuple) { return resultTuple; } /*} else { ptA.setTuple( NextTupleA() ); cmp = CompareTuples( ptA.tuple, tmpB.tuple ); while ( ptA != 0 && cmp < 0 ) { // skip tuples from A while they are smaller than the // value of the tuples in grpB ptA.setTuple( NextTupleA() ); if (ptA != 0) cmp = CompareTuples( ptA.tuple, tmpB.tuple ); } }*/ } else if ( cmp == 0 ) { if (!continueMerge && tmpB == 0) { //save ptB in tmpB tmpB = ptB; grpB->AppendTuple(tmpB.tuple); // advance the tuple pointer ptB.setTuple( NextTupleB() ); // collect a group of tuples from B which // have the same attribute value bool done = false; while ( !done && ptB != 0 ) { //cout << "grpB" << endl; //logTuples(); int cmp = CompareTuplesB( tmpB.tuple, ptB.tuple ); if ( cmp == 0) { // append equal tuples to group grpB->AppendTuple(ptB.tuple); // release tuple of input B ptB.setTuple( NextTupleB() ); } else { done = true; } } // end collect group } // continue or start merge if (!continueMerge) { iter = grpB->MakeScan(); continueMerge = true; resultTuple = NextConcat(); if (resultTuple) { return resultTuple; } } else { //continue merging, create next result tuple resultTuple = NextConcat(); if (resultTuple) { return resultTuple; } else { continueMerge = false; delete iter; iter = 0; ptA.setTuple( NextTupleA() ); if (ptA != 0) { cmp = CompareTuples( ptA.tuple, tmpB.tuple ); if (/*outerJoin && */cmp != 0) { tmpB = 0; grpB->Clear(); } } } } } // end of merge else /*if ( outerJoin )*/ { // ptA > ptB continueMerge = false; resultTuple = NextConcatA(); ptB.setTuple( NextTupleB() ); if (resultTuple) { return resultTuple; } } /*else { if ( ptB == 0 ) // short exit return 0; grpB->Clear(); tmpB = 0; }*/ } // end of else if ( /*outerJoin && */ptA != 0 ) { // ptB == 0 resultTuple = NextConcatB(); ptA.setTuple( NextTupleA() ); if (resultTuple) { return resultTuple; } } else /*if ( outerJoin ) / * ptB != 0 */{ // ptA == 0 resultTuple = NextConcatA(); ptB.setTuple( NextTupleB() ); if (resultTuple) { return resultTuple; } } /*else { // short exit return 0; }*/ } // end of main loop return 0; } inline Tuple* NextConcat() { Tuple* t = iter->GetNextTuple(); if( t != 0 ) { Tuple* result = new Tuple( resultTupleType ); Concat( ptA.tuple, t, result ); t->DeleteIfAllowed(); return result; } return 0; } inline Tuple* NextConcatB() { Tuple* result = new Tuple( resultTupleType ); Concat( ptA.tuple, NextUndefinedB(), result ); return result; } inline Tuple* NextConcatA() { Tuple* result = new Tuple( resultTupleType ); Concat( NextUndefinedA(), ptB.tuple, result ); return result; } }; /* 2.2.2 Value mapping function of operator ~mergeouterjoin~ */ //CPUTimeMeasurer mergeMeasurer; template int smouterjoin_vm(Word* args, Word& result, int message, Word& local, Supplier s) { typedef LocalInfo LocalType; LocalType* li = static_cast( local.addr ); switch(message) { case OPEN: if ( li ) { delete li; } li = new LocalType(); local.addr = li; qp->Open(args[0].addr); qp->Open(args[1].addr); li->ptr = 0; return 0; case REQUEST: { //mergeMeasurer.Enter(); if ( li->ptr == 0 ) //first request; //constructor put here to avoid delays in OPEN //which are a problem for progress estimation { li->ptr = new MergeOuterjoinLocalInfo (args[0], args[4], args[1], args[5], expectSorted, s, li); } MergeOuterjoinLocalInfo* mli = li->ptr; result.addr = mli->NextResultTuple(); li->returned++; //mergeMeasurer.Exit(); return result.addr != 0 ? YIELD : CANCEL; } case CLOSE: //mergeMeasurer.PrintCPUTimeAndReset("CPU Time for Merging Tuples : "); qp->Close(args[0].addr); qp->Close(args[1].addr); //nothing is deleted on close because the substructures are still //needed for progress estimation. Instead, everything is deleted on //(repeated) OPEN and on CLOSEPROGRESS return 0; case CLOSEPROGRESS: if (li) { delete li; local.addr = 0; } return 0; case REQUESTPROGRESS: { ProgressInfo p1, p2; ProgressInfo* pRes = static_cast( result.addr ); const double uSortBy = 0.00043; //millisecs per byte read in sort step const double uMergeOuterjoin = 0.0008077; //millisecs per tuple read //in merge step (merge) const double wMergeOuterjoin = 0.0001738; //millisecs per byte read in //merge step (sortmerge) const double xMergeOuterjoin = 0.0012058; //millisecs per result tuple in //merge step const double yMergeOuterjoin = 0.0001072; //millisecs per result //attribute in merge step //see file ConstantsSortmergeouterjoin.txt LocalInfo* liFirst; LocalInfo* liSecond; if( !li ) return CANCEL; else { liFirst = static_cast*> (li->firstLocalInfo); liSecond = static_cast*> (li->secondLocalInfo); if (qp->RequestProgress(args[0].addr, &p1) && qp->RequestProgress(args[1].addr, &p2)) { li->SetJoinSizes(p1, p2); pRes->CopySizes(li); if (li->returned > enoughSuccessesJoin ) // stable state { pRes->Card = ((double) li->returned) * p1.Card / ((double) li->readFirst); } else { pRes->Card = p1.Card * p2.Card * qp->GetSelectivity(s); } if ( expectSorted ) { pRes->Time = p1.Time + p2.Time + (p1.Card + p2.Card) * uMergeOuterjoin + pRes->Card * (xMergeOuterjoin + pRes->noAttrs * yMergeOuterjoin); pRes->Progress = (p1.Progress * p1.Time + p2.Progress * p2.Time + (((double) li->readFirst) + ((double) li->readSecond)) * uMergeOuterjoin + ((double) li->returned) * (xMergeOuterjoin + pRes->noAttrs * yMergeOuterjoin)) / pRes->Time; pRes->CopyBlocking(p1, p2); //non-blocking in this case } else { pRes->Time = p1.Time + p2.Time + p1.Card * p1.Size * uSortBy + p2.Card * p2.Size * uSortBy + (p1.Card * p1.Size + p2.Card * p2.Size) * wMergeOuterjoin + pRes->Card * (xMergeOuterjoin + pRes->noAttrs * yMergeOuterjoin); long readFirst = (liFirst ? liFirst->read : 0); long readSecond = (liSecond ? liSecond->read : 0); pRes->Progress = (p1.Progress * p1.Time + p2.Progress * p2.Time + ((double) readFirst) * p1.Size * uSortBy + ((double) readSecond) * p2.Size * uSortBy + (((double) li->readFirst) * p1.Size + ((double) li->readSecond) * p2.Size) * wMergeOuterjoin + ((double) li->returned) * (xMergeOuterjoin + pRes->noAttrs * yMergeOuterjoin)) / pRes->Time; pRes->BTime = p1.Time + p2.Time + p1.Card * p1.Size * uSortBy + p2.Card * p2.Size * uSortBy; pRes->BProgress = (p1.Progress * p1.Time + p2.Progress * p2.Time + ((double) readFirst) * p1.Size * uSortBy + ((double) readSecond) * p2.Size * uSortBy) / pRes->BTime; } return YIELD; } else return CANCEL; } } } return 0; } //-- end progress version --// #endif #ifndef USE_PROGRESS /* 2.2.2.1 Value mapping function of operator ~symmouterjoin~ */ // standard version struct SymmOuterJoinLocalInfo { SymmOuterJoinLocalInfo(Word _streamRight, Word _streamLeft) { streamRight = _streamRight; streamLeft = _streamLeft; ListExpr typeRight = qp->GetNumType(streamRight.addr); tupleTypeRight = new TupleType( nl->Second( typeRight ) ); ListExpr typeLeft = qp->GetNumType(streamLeft.addr); tupleTypeLeft = new TupleType( nl->Second( typeLeft ) ); undefRight = 0; undefLeft = 0; } TupleType *resultTupleType; TupleBuffer *rightRel; GenericRelationIterator *rightIter; TupleBuffer *leftRel; GenericRelationIterator *leftIter; bool right; Tuple *currTuple; bool rightFinished; bool leftFinished; Hash *rightHash; Hash *leftHash; Word streamRight; Word streamLeft; TupleType *tupleTypeRight; TupleType *tupleTypeLeft; bool nullTuples; TupleBuffer *rightRel2; TupleBuffer *leftRel2; Tuple *undefRight; Tuple *undefLeft; inline Tuple* NextUndefinedRight() { Tuple* result = 0; readFirst++; if (undefRight == 0) { // create tuple with undefined values result = new Tuple( tupleTypeRight ); for (int i = 0; i < result->GetNoAttributes(); i++) { int algId = tupleTypeRight->GetAttributeType(i).algId; int typeId = tupleTypeRight->GetAttributeType(i).typeId; // create an instance of the specified type, which gives // us an instance of a subclass of class Attribute. Attribute* attr = static_cast( am->CreateObj(algId, typeId)(0).addr ); attr->SetDefined( false ); result->PutAttribute( i, attr ); } undefRight = result; } else { result = undefRight; } return result; } inline Tuple* NextUndefinedLeft() { Tuple* result = 0; readSecond++; if (undefLeft == 0) { // create tuple with undefined values result = new Tuple( tupleTypeLeft ); for (int i = 0; i < result->GetNoAttributes(); i++) { int algId = tupleTypeLeft->GetAttributeType(i).algId; int typeId = tupleTypeLeft->GetAttributeType(i).typeId; // create an instance of the specified type, which gives // us an instance of a subclass of class Attribute. Attribute* attr = static_cast( am->CreateObj(algId, typeId)(0).addr ); attr->SetDefined( false ); result->PutAttribute( i, attr ); } undefLeft = result; } else { result = undefLeft; } return result; } }; template int symmouterjoin_vm(Word* args, Word& result, int message, Word& local, Supplier s) { Word r, l; SymmOuterJoinLocalInfo* pli; switch (message) { case OPEN : { long MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024); cmsg.info("ERA:ShowMemInfo") << "SymmOuterJoin.MAX_MEMORY (" << MAX_MEMORY/1024 << " kB): " << endl; cmsg.send(); pli = new SymmOuterJoinLocalInfo(args[0],args[1]); pli->rightRel = new TupleBuffer( MAX_MEMORY / 2 ); pli->rightIter = 0; pli->leftRel = new TupleBuffer( MAX_MEMORY / 2 ); pli->leftIter = 0; pli->right = true; pli->currTuple = 0; pli->rightFinished = false; pli->leftFinished = false; pli->rightHash = new Hash( INT, true ); pli->leftHash = new Hash( INT, true ); pli->nullTuples = false; pli->rightRel2 = new TupleBuffer( MAX_MEMORY / 2 ); pli->leftRel2 = new TupleBuffer( MAX_MEMORY / 2 ); ListExpr resultType = GetTupleResultType( s ); pli->resultTupleType = new TupleType( nl->Second( resultType ) ); qp->Open(args[0].addr); qp->Open(args[1].addr); local.setAddr(pli); return 0; } case REQUEST : { pli = (SymmOuterJoinLocalInfo*)local.addr; while( 1 ) // This loop will end in some of the returns. { if ( pli->nullTuples ) { // find all unmatched tuples from right relation if ( pli->rightIter == 0 ) { pli->rightIter = pli->rightRel2->MakeScan(); } Tuple *rightOuterTuple = pli->rightIter->GetNextTuple(); if ( rightOuterTuple != 0 ) { SmiKeyedFile *file = pli->rightHash->GetFile(); SmiRecord record; while ( rightOuterTuple != 0 && file->SelectRecord( SmiKey(rightOuterTuple->GetTupleId()), record )) { // if we find the tupleid in the hash file, //the tuple is already matched, // so we can ignore it. // curiosly, record size is 0 when we find the tuple //id in the hashfile if ( record.Size() == 0 ) { rightOuterTuple->DeleteIfAllowed(); rightOuterTuple = 0; rightOuterTuple = pli->rightIter->GetNextTuple(); } else break; } // create a tuple with undefined values for the // attributes of the left relation if ( rightOuterTuple != 0 ) { Tuple *resultTuple = new Tuple( pli->resultTupleType ); Concat( rightOuterTuple, pli->NextUndefinedLeft(), resultTuple ); rightOuterTuple->DeleteIfAllowed(); rightOuterTuple = 0; result.setAddr( resultTuple ); return YIELD; } } if ( pli->leftIter == 0 ) { pli->leftIter = pli->leftRel2->MakeScan(); } Tuple *leftOuterTuple = pli->leftIter->GetNextTuple(); if ( leftOuterTuple != 0 ) { SmiKeyedFile *file = pli->leftHash->GetFile(); SmiRecord record; while ( leftOuterTuple != 0 && file->SelectRecord( SmiKey(leftOuterTuple->GetTupleId()), record )) { // if we find the tupleid in the hash file, // the tuple is already matched, // so we can ignore it. // curiosly, record size is 0 when we find // the tuple id in the hashfile if ( record.Size() == 0 ) { leftOuterTuple->DeleteIfAllowed(); leftOuterTuple = 0; leftOuterTuple = pli->leftIter->GetNextTuple(); } else break; } // create a tuple with undefined values for the //attributes of the right relation if ( leftOuterTuple != 0 ) { Tuple *resultTuple = new Tuple( pli->resultTupleType ); Concat( pli->NextUndefinedRight(), leftOuterTuple, resultTuple ); leftOuterTuple->DeleteIfAllowed(); leftOuterTuple = 0; result.setAddr( resultTuple ); return YIELD; } } // we're finished return CANCEL; } if( pli->right ) // Get the tuple from the right stream and match it with the // left stored buffer { if( pli->currTuple == 0 ) { qp->Request(args[0].addr, r); if( qp->Received( args[0].addr ) ) { pli->currTuple = (Tuple*)r.addr; pli->leftIter = pli->leftRel->MakeScan(); pli->rightRel2->AppendTuple( pli->currTuple ); } else { pli->rightFinished = true; if( pli->leftFinished ) { pli->nullTuples = true; // output null-tuples continue; } else { pli->right = false; continue; // Go back to the loop } } } // Now we have a tuple from the right stream in currTuple // and an open iterator on the left stored buffer. Tuple *leftTuple = pli->leftIter->GetNextTuple(); if( leftTuple == 0 ) // There are no more tuples in the left iterator. We then // store the current tuple in the right buffer and close the // left iterator. { if( !pli->leftFinished ) // We only need to keep track of the right tuples if the // left stream is not finished. { pli->rightRel->AppendTuple( pli->currTuple ); pli->right = false; } pli->currTuple->DeleteIfAllowed(); pli->currTuple = 0; delete pli->leftIter; pli->leftIter = 0; continue; // Go back to the loop } else // We match the tuples. { ArgVectorPointer funArgs = qp->Argument(args[2].addr); ((*funArgs)[0]).setAddr( pli->currTuple ); ((*funArgs)[1]).setAddr( leftTuple ); Word funResult; qp->Request(args[2].addr, funResult); CcBool *boolFunResult = (CcBool*)funResult.addr; if( boolFunResult->IsDefined() && boolFunResult->GetBoolval() ) { Tuple *resultTuple = new Tuple( pli->resultTupleType ); Concat( pli->currTuple, leftTuple, resultTuple ); pli->leftHash->Append( SmiKey(leftTuple->GetTupleId()), (SmiRecordId)leftTuple->GetTupleId()); pli->rightHash->Append( SmiKey(pli->currTuple->GetTupleId()), (SmiRecordId)pli->currTuple->GetTupleId() ); leftTuple->DeleteIfAllowed(); leftTuple = 0; result.setAddr( resultTuple ); return YIELD; } else { leftTuple->DeleteIfAllowed(); leftTuple = 0; continue; // Go back to the loop } } } else // Get the tuple from the left stream and match it with the // right stored buffer { if( pli->currTuple == 0 ) { qp->Request(args[1].addr, l); if( qp->Received( args[1].addr ) ) { pli->currTuple = (Tuple*)l.addr; pli->rightIter = pli->rightRel->MakeScan(); pli->leftRel2->AppendTuple( pli->currTuple ); } else { pli->leftFinished = true; if( pli->rightFinished ) { pli->nullTuples = true; continue; } else { pli->right = true; continue; // Go back to the loop } } } // Now we have a tuple from the left stream in currTuple and // an open iterator on the right stored buffer. Tuple *rightTuple = pli->rightIter->GetNextTuple(); if( rightTuple == 0 ) // There are no more tuples in the right iterator. We then // store the current tuple in the left buffer and close // the right iterator. { if( !pli->rightFinished ) // We only need to keep track of the left tuples if the // right stream is not finished. { pli->leftRel->AppendTuple( pli->currTuple ); pli->right = true; } pli->currTuple->DeleteIfAllowed(); pli->currTuple = 0; delete pli->rightIter; pli->rightIter = 0; continue; // Go back to the loop } else // We match the tuples. { ArgVectorPointer funArgs = qp->Argument(args[2].addr); ((*funArgs)[0]).setAddr( rightTuple ); ((*funArgs)[1]).setAddr( pli->currTuple ); Word funResult; qp->Request(args[2].addr, funResult); CcBool *boolFunResult = (CcBool*)funResult.addr; if( boolFunResult->IsDefined() && boolFunResult->GetBoolval() ) { Tuple *resultTuple = new Tuple( pli->resultTupleType ); Concat( rightTuple, pli->currTuple, resultTuple ); pli->rightHash->Append( SmiKey(rightTuple->GetTupleId()), (SmiRecordId)rightTuple->GetTupleId() ); pli->leftHash->Append( SmiKey(pli->currTuple->GetTupleId()), (SmiRecordId)pli->currTuple->GetTupleId() ); rightTuple->DeleteIfAllowed(); rightTuple = 0; result.setAddr( resultTuple ); return YIELD; } else { rightTuple->DeleteIfAllowed(); rightTuple = 0; continue; // Go back to the loop } } } } } case CLOSE : { pli = (SymmOuterJoinLocalInfo*)local.addr; if(pli) { if( pli->currTuple != 0 ) pli->currTuple->DeleteIfAllowed(); delete pli->leftIter; delete pli->rightIter; if( pli->resultTupleType != 0 ) pli->resultTupleType->DeleteIfAllowed(); if( pli->rightRel != 0 ) { pli->rightRel->Clear(); delete pli->rightRel; } if( pli->leftRel != 0 ) { pli->leftRel->Clear(); delete pli->leftRel; } if ( pli->rightHash != 0 ) { pli->rightHash->DeleteFile(); delete pli->rightHash; pli->rightHash = 0; } if ( pli->leftHash != 0 ) { pli->leftHash->DeleteFile(); delete pli->leftHash; pli->leftHash = 0; } if( pli->rightRel2 != 0 ) { pli->rightRel2->Clear(); delete pli->rightRel2; pli->rightRel2=0; } if( pli->leftRel2 != 0 ) { pli->leftRel2->Clear(); delete pli->leftRel2; pli->leftRel2=0; } delete pli; local.setAddr(0); } qp->Close(args[0].addr); qp->Close(args[1].addr); return 0; } } return 0; } #else // with support for progress queries class SymmOuterJoinLocalInfo: public ProgressLocalInfo { public: SymmOuterJoinLocalInfo(Word _streamRight, Word _streamLeft) { streamRight = _streamRight; streamLeft = _streamLeft; ListExpr typeRight = qp->GetNumType(streamRight.addr); tupleTypeRight = new TupleType( nl->Second( typeRight ) ); ListExpr typeLeft = qp->GetNumType(streamLeft.addr); tupleTypeLeft = new TupleType( nl->Second( typeLeft ) ); undefRight = 0; undefLeft = 0; } ~SymmOuterJoinLocalInfo(){ if(undefRight){ undefRight->DeleteIfAllowed(); undefRight=0; } if(undefLeft){ undefLeft->DeleteIfAllowed(); undefLeft=0; } tupleTypeRight->DeleteIfAllowed(); tupleTypeLeft->DeleteIfAllowed(); } Word streamRight; Word streamLeft; TupleType *resultTupleType; TupleType *tupleTypeRight; TupleType *tupleTypeLeft; TupleBuffer *rightRel; GenericRelationIterator *rightIter; TupleBuffer *leftRel; GenericRelationIterator *leftIter; bool right; Tuple *currTuple; bool rightFinished; bool leftFinished; Hash *rightHash; Hash *leftHash; bool nullTuples; TupleBuffer *rightRel2; TupleBuffer *leftRel2; Tuple *undefRight; Tuple *undefLeft; inline Tuple* NextUndefinedRight() { Tuple* result = 0; readFirst++; if (undefRight == 0) { // create tuple with undefined values result = new Tuple( tupleTypeRight ); for (int i = 0; i < result->GetNoAttributes(); i++) { int algId = tupleTypeRight->GetAttributeType(i).algId; int typeId = tupleTypeRight->GetAttributeType(i).typeId; // create an instance of the specified type, which gives // us an instance of a subclass of class Attribute. Attribute* attr = static_cast( am->CreateObj(algId, typeId)(0).addr ); attr->SetDefined( false ); result->PutAttribute( i, attr ); } undefRight = result; } else { result = undefRight; } return result; } inline Tuple* NextUndefinedLeft() { Tuple* result = 0; readSecond++; if (undefLeft == 0) { // create tuple with undefined values result = new Tuple( tupleTypeLeft ); for (int i = 0; i < result->GetNoAttributes(); i++) { int algId = tupleTypeLeft->GetAttributeType(i).algId; int typeId = tupleTypeLeft->GetAttributeType(i).typeId; // create an instance of the specified type, which gives // us an instance of a subclass of class Attribute. Attribute* attr = static_cast( am->CreateObj(algId, typeId)(0).addr ); attr->SetDefined( false ); result->PutAttribute( i, attr ); } undefLeft = result; } else { result = undefLeft; } return result; } }; template int symmouterjoin_vm(Word* args, Word& result, int message, Word& local, Supplier s) { Word r, l; SymmOuterJoinLocalInfo* pli; pli = (SymmOuterJoinLocalInfo*) local.addr; switch (message) { case OPEN : { long MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024); cmsg.info("ERA:ShowMemInfo") << "SymmOuterJoin.MAX_MEMORY (" << MAX_MEMORY/1024 << " kB): " << endl; cmsg.send(); if ( pli ) delete pli; pli = new SymmOuterJoinLocalInfo(args[0], args[1]); pli->rightRel = new TupleBuffer( MAX_MEMORY / 2 ); pli->rightIter = 0; pli->leftRel = new TupleBuffer( MAX_MEMORY / 2 ); pli->leftIter = 0; pli->right = true; pli->currTuple = 0; pli->rightFinished = false; pli->leftFinished = false; SmiKey::KeyDataType keyType = sizeof(int32_t) == sizeof(TupleId) ?SmiKey::Integer :SmiKey::Longint; pli->rightHash = new Hash( keyType, true ); pli->leftHash = new Hash( keyType, true ); pli->nullTuples = false; pli->rightRel2 = new TupleBuffer( MAX_MEMORY / 2 ); pli->leftRel2 = new TupleBuffer( MAX_MEMORY / 2 ); ListExpr resultType = GetTupleResultType( s ); pli->resultTupleType = new TupleType( nl->Second( resultType ) ); qp->Open(args[0].addr); qp->Open(args[1].addr); pli->readFirst = 0; pli->readSecond = 0; pli->returned = 0; local.setAddr(pli); return 0; } case REQUEST : { while( 1 ) // This loop will end in some of the returns. { if ( pli->nullTuples ) { // find all unmatched tuples from right relation if ( pli->rightIter == 0 ) { pli->rightIter = pli->rightRel2->MakeScan(); } Tuple *rightOuterTuple = pli->rightIter->GetNextTuple(); if ( rightOuterTuple != 0 ) { SmiKeyedFile *file = pli->rightHash->GetFile(); SmiRecord record; while ( rightOuterTuple != 0 && file->SelectRecord( SmiKey(rightOuterTuple->GetTupleId()), record )) { // if we find the tupleid in the hash file, //the tuple is already matched, // so we can ignore it. rightOuterTuple->DeleteIfAllowed(); rightOuterTuple = 0; rightOuterTuple = pli->rightIter->GetNextTuple(); } // create a tuple with undefined values for the // attributes of the left relation if ( rightOuterTuple != 0 ) { Tuple *resultTuple = new Tuple( pli->resultTupleType ); Tuple* nextUndefinedLeft = pli->NextUndefinedLeft(); Concat( rightOuterTuple, nextUndefinedLeft, resultTuple ); rightOuterTuple->DeleteIfAllowed(); rightOuterTuple = 0; result.setAddr( resultTuple ); pli->returned++; return YIELD; } } if ( pli->leftIter == 0 ) { pli->leftIter = pli->leftRel2->MakeScan(); } Tuple *leftOuterTuple = pli->leftIter->GetNextTuple(); if ( leftOuterTuple != 0 ) { SmiKeyedFile *file = pli->leftHash->GetFile(); SmiRecord record; while ( leftOuterTuple != 0 && file->SelectRecord( SmiKey(leftOuterTuple->GetTupleId()), record )) { // if we find the tupleid in the hash file, // the tuple is already matched, // so we can ignore it. leftOuterTuple->DeleteIfAllowed(); leftOuterTuple = 0; leftOuterTuple = pli->leftIter->GetNextTuple(); } // create a tuple with undefined values for the //attributes of the right relation if ( leftOuterTuple != 0 ) { Tuple *resultTuple = new Tuple( pli->resultTupleType ); Tuple* nextUndefinedRight = pli->NextUndefinedRight(); Concat( nextUndefinedRight , leftOuterTuple, resultTuple ); leftOuterTuple->DeleteIfAllowed(); leftOuterTuple = 0; result.setAddr( resultTuple ); pli->returned++; return YIELD; } } // we're finished return CANCEL; } if( pli->right ) // Get the tuple from the right stream and match it with the // left stored buffer { if( pli->currTuple == 0 ) { qp->Request(args[0].addr, r); if( qp->Received( args[0].addr ) ) { pli->currTuple = (Tuple*)r.addr; pli->leftIter = pli->leftRel->MakeScan(); pli->readFirst++; pli->rightRel2->AppendTuple( pli->currTuple ); } else { pli->rightFinished = true; if( pli->leftFinished ) { pli->nullTuples = true; continue; } else { pli->right = false; continue; // Go back to the loop } } } // Now we have a tuple from the right stream in currTuple // and an open iterator on the left stored buffer. Tuple *leftTuple = pli->leftIter->GetNextTuple(); if( leftTuple == 0 ) // There are no more tuples in the left iterator. We then // store the current tuple in the right buffer and close the // left iterator. { if( !pli->leftFinished ) // We only need to keep track of the right tuples if the // left stream is not finished. { pli->rightRel->AppendTuple( pli->currTuple ); pli->right = false; } pli->currTuple->DeleteIfAllowed(); pli->currTuple = 0; delete pli->leftIter; pli->leftIter = 0; continue; // Go back to the loop } else // We match the tuples. { ArgVectorPointer funArgs = qp->Argument(args[2].addr); ((*funArgs)[0]).setAddr( pli->currTuple ); ((*funArgs)[1]).setAddr( leftTuple ); Word funResult; qp->Request(args[2].addr, funResult); CcBool *boolFunResult = (CcBool*)funResult.addr; if( boolFunResult->IsDefined() && boolFunResult->GetBoolval() ) { Tuple *resultTuple = new Tuple( pli->resultTupleType ); Concat( pli->currTuple, leftTuple, resultTuple ); SmiKey key(leftTuple->GetTupleId()); pli->leftHash->Append( key, (SmiRecordId)leftTuple->GetTupleId()); pli->rightHash->Append( SmiKey(pli->currTuple->GetTupleId()), (SmiRecordId)pli->currTuple->GetTupleId() ); leftTuple->DeleteIfAllowed(); leftTuple = 0; result.setAddr( resultTuple ); pli->returned++; return YIELD; } else { leftTuple->DeleteIfAllowed(); leftTuple = 0; continue; // Go back to the loop } } } else // Get the tuple from the left stream and match it with the // right stored buffer { if( pli->currTuple == 0 ) { qp->Request(args[1].addr, l); if( qp->Received( args[1].addr ) ) { pli->currTuple = (Tuple*)l.addr; pli->rightIter = pli->rightRel->MakeScan(); pli->readSecond++; pli->leftRel2->AppendTuple( pli->currTuple ); } else { pli->leftFinished = true; if( pli->rightFinished ) { pli->nullTuples = true; continue; } else { pli->right = true; continue; // Go back to the loop } } } // Now we have a tuple from the left stream in currTuple and // an open iterator on the right stored buffer. Tuple *rightTuple = pli->rightIter->GetNextTuple(); if( rightTuple == 0 ) // There are no more tuples in the right iterator. We then // store the current tuple in the left buffer and close // the right iterator. { if( !pli->rightFinished ) // We only need to keep track of the left tuples if the // right stream is not finished. { pli->leftRel->AppendTuple( pli->currTuple ); pli->right = true; } pli->currTuple->DeleteIfAllowed(); pli->currTuple = 0; delete pli->rightIter; pli->rightIter = 0; continue; // Go back to the loop } else // We match the tuples. { ArgVectorPointer funArgs = qp->Argument(args[2].addr); ((*funArgs)[0]).setAddr( rightTuple ); ((*funArgs)[1]).setAddr( pli->currTuple ); Word funResult; qp->Request(args[2].addr, funResult); CcBool *boolFunResult = (CcBool*)funResult.addr; if( boolFunResult->IsDefined() && boolFunResult->GetBoolval() ) { Tuple *resultTuple = new Tuple( pli->resultTupleType ); Concat( rightTuple, pli->currTuple, resultTuple ); pli->rightHash->Append( SmiKey(rightTuple->GetTupleId()), (SmiRecordId)rightTuple->GetTupleId() ); pli->leftHash->Append( SmiKey(pli->currTuple->GetTupleId()), (SmiRecordId)pli->currTuple->GetTupleId() ); rightTuple->DeleteIfAllowed(); rightTuple = 0; result.setAddr( resultTuple ); pli->returned++; return YIELD; } else { rightTuple->DeleteIfAllowed(); rightTuple = 0; continue; // Go back to the loop } } } } } case CLOSE : { if(pli) { if( pli->currTuple != 0 ){ pli->currTuple->DeleteIfAllowed(); pli->currTuple=0; } delete pli->leftIter; delete pli->rightIter; if( pli->resultTupleType != 0 ){ pli->resultTupleType->DeleteIfAllowed(); pli->resultTupleType=0; } if( pli->rightRel != 0 ) { pli->rightRel->Clear(); delete pli->rightRel; pli->rightRel=0; } if( pli->leftRel != 0 ) { pli->leftRel->Clear(); delete pli->leftRel; pli->leftRel=0; } if ( pli->rightHash != 0 ) { pli->rightHash->DeleteFile(); delete pli->rightHash; pli->rightHash = 0; } if ( pli->leftHash != 0 ) { pli->leftHash->DeleteFile(); delete pli->leftHash; pli->leftHash = 0; } if( pli->rightRel2 != 0 ) { pli->rightRel2->Clear(); delete pli->rightRel2; pli->rightRel2=0; } if( pli->leftRel2 != 0 ) { pli->leftRel2->Clear(); delete pli->leftRel2; pli->leftRel2=0; } } qp->Close(args[0].addr); qp->Close(args[1].addr); return 0; } case CLOSEPROGRESS: if ( pli ) { delete pli; local.setAddr(0); } return 0; case REQUESTPROGRESS : { ProgressInfo p1, p2; ProgressInfo *pRes; const double uSymmOuterJoin = 0.2; //millisecs per tuple pair pRes = (ProgressInfo*) result.addr; if (!pli) return CANCEL; if (qp->RequestProgress(args[0].addr, &p1) && qp->RequestProgress(args[1].addr, &p2)) { pli->SetJoinSizes(p1, p2); pRes->CopySizes(pli); double predCost = (qp->GetPredCost(s) == 0.1 ? 0.004 : qp->GetPredCost(s)); //the default value of 0.1 is only suitable for selections pRes->Time = p1.Time + p2.Time + p1.Card * p2.Card * predCost * uSymmOuterJoin; pRes->Progress = (p1.Progress * p1.Time + p2.Progress * p2.Time + pli->readFirst * pli->readSecond * predCost * uSymmOuterJoin) / pRes->Time; if (pli->returned > enoughSuccessesJoin ) // stable state assumed now { pRes->Card = p1.Card * p2.Card * ((double) pli->returned / (double) (pli->readFirst * pli->readSecond)); } else { pRes->Card = p1.Card * p2.Card * qp->GetSelectivity(s); } pRes->CopyBlocking(p1, p2); //non-blocking oprator return YIELD; } else { return CANCEL; } } } return 0; } #endif template int smouterjoin_vm(Word* args, Word& result, int message, Word& local, Supplier s); template int symmouterjoin_vm<1>(Word* args, Word& result, int message, Word& local, Supplier s);