Files
secondo/Optimizer/Subqueries/Outerjoin.cpp
2026-01-23 17:03:45 +08:00

2963 lines
85 KiB
C++

/*
----
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 <vector>
#include <list>
#include <set>
#include <queue>
//#define TRACE_ON
#undef TRACE_ON
#include "LogMsg.h"
#define TRACE_OFF
#include "StandardTypes.h"
#include "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 "HashAlgebra.h"
extern NestedList* nl;
extern QueryProcessor* qp;
#ifndef USE_PROGRESS
//-- begin standard version --//
class SortByLocalInfo
{
public:
SortByLocalInfo( Word stream,
const bool lexicographic,
void *tupleCmp ):
stream( stream ),
currentIndex( 0 ),
lexiTupleCmp( lexicographic ?
(LexicographicalTupleCompare*)tupleCmp :
0 ),
tupleCmpBy( lexicographic ? 0 : (TupleCompareBy*)tupleCmp ),
lexicographic( lexicographic )
{
// Note: Is is not possible to define a Cmp object using the
// constructor
// mergeTuples( PairTupleCompareBy( tupleCmpBy )).
// It does only work if mergeTuples is a local variable which
// does not help us in this case. Is it a Compiler bug or C++ feature?
// Hence a new class TupleAndRelPos was defined which implements
// the comparison operator '<'.
TupleQueue* currentRun = &queue[0];
TupleQueue* nextRun = &queue[1];
Word wTuple(Address(0));
size_t c = 0, i = 0, a = 0, n = 0, m = 0, r = 0; // counter variables
bool newRelation = true;
MAX_MEMORY = qp->MemoryAvailableForOperator();
cmsg.info("ERA:ShowMemInfo")
<< "Sortby.MAX_MEMORY (" << MAX_MEMORY/1024 << " kb)" << endl;
cmsg.send();
TupleBuffer *rel=0;
TupleAndRelPos lastTuple(0, tupleCmpBy);
qp->Request(stream.addr, wTuple);
TupleAndRelPos minTuple(0, tupleCmpBy);
while(qp->Received(stream.addr)) // consume the stream completely
{
c++; // tuple counter;
Tuple* t = static_cast<Tuple*>( wTuple.addr );
TupleAndRelPos nextTuple(t, tupleCmpBy);
if( MAX_MEMORY > (size_t)t->GetSize() )
{
nextTuple.tuple->IncReference();
currentRun->push(nextTuple);
i++; // increment Tuples in memory counter
MAX_MEMORY -= t->GetSize();
}
else
{ // memory is completely used
if ( newRelation )
{ // create new relation
r++;
rel = new TupleBuffer( 0 );
GenericRelationIterator *iter = 0;
relations.push_back( make_pair( rel, iter ) );
newRelation = false;
// get first tuple and store it in an relation
nextTuple.tuple->IncReference();
currentRun->push(nextTuple);
minTuple = currentRun->top();
//minTuple.tuple->DecReference();
rel->AppendTuple( minTuple.tuple );
lastTuple = minTuple;
currentRun->pop();
}
else
{ // check if nextTuple can be saved in current relation
TupleAndRelPos copyOfLast = lastTuple;
if ( nextTuple < lastTuple )
{ // nextTuple is in order
// Push the next tuple int the heap and append the minimum to
// the current relation and push
nextTuple.tuple->IncReference();
currentRun->push(nextTuple);
minTuple = currentRun->top();
//minTuple.tuple->DecReference();
rel->AppendTuple( minTuple.tuple );
lastTuple = minTuple;
currentRun->pop();
m++;
}
else
{ // nextTuple is smaller, save it for the next relation
nextTuple.tuple->IncReference();
nextRun->push(nextTuple);
n++;
if ( !currentRun->empty() )
{
// Append the minimum to the current relation
minTuple = currentRun->top();
//minTuple.tuple->DecReference();
rel->AppendTuple( minTuple.tuple );
lastTuple = minTuple;
currentRun->pop();
}
else
{ //create a new run
newRelation = true;
// swap queues
TupleQueue *helpRun = currentRun;
currentRun = nextRun;
nextRun = helpRun;
ShowPartitionInfo(c,a,n,m,r,rel);
i=n;
a=0;
n=0;
m=0;
} // end new run
} // end next tuple is smaller
// delete last tuple if saved to relation and
// not referenced by minTuple
if ( copyOfLast.tuple && (copyOfLast.tuple != minTuple.tuple) )
{
copyOfLast.tuple->DeleteIfAllowed();
}
} // check if nextTuple can be saved in current relation
}// memory is completely used
qp->Request(stream.addr, wTuple);
}
ShowPartitionInfo(c,a,n,m,r,rel);
// delete lastTuple and minTuple if allowed
if ( lastTuple.tuple )
{
lastTuple.tuple->DeleteIfAllowed();
}
if ( (minTuple.tuple != lastTuple.tuple) )
{
minTuple.tuple->DeleteIfAllowed();
}
// the lastRun and NextRun runs in memory having
// less than MAX_TUPLE elements
if( !queue[0].empty() )
{
Tuple* t = queue[0].top().tuple;
queue[0].pop();
mergeTuples.push( TupleAndRelPos(t, tupleCmpBy, -2) );
}
if( !queue[1].empty() )
{
Tuple* t = queue[1].top().tuple;
queue[1].pop();
mergeTuples.push( TupleAndRelPos(t, tupleCmpBy, -1) );
}
// Get next tuple from each relation and push it into the heap.
for( size_t i = 0; i < relations.size(); i++ )
{
relations[i].second = relations[i].first->MakeScan();
Tuple *t = relations[i].second->GetNextTuple();
if( t != 0 )
{
t->IncReference();
mergeTuples.push( TupleAndRelPos(t, tupleCmpBy, i+1) );
}
}
Counter::getRef("Sortby:ExternPartitions") = relations.size();
}
/*
It may happen, that the localinfo object will be destroyed
before all internal buffered tuples are delivered stream
upwards, e.g. queries which use a ~head~ operator.
In this case we need to delete also all tuples stored in memory.
*/
~SortByLocalInfo()
{
while( !mergeTuples.empty() )
{
mergeTuples.top().tuple->DeleteIfAllowed();
mergeTuples.pop();
}
for( int i = 0; i < 2; i++ )
{
while( !queue[i].empty() )
{
queue[i].top().tuple->DeleteIfAllowed();
queue[i].pop();
}
}
// delete information about sorted runs
for( size_t i = 0; i < relations.size(); i++ )
{
delete relations[i].second;
relations[i].second = 0;
delete relations[i].first;
relations[i].first = 0;
}
delete lexiTupleCmp;
lexiTupleCmp = 0;
delete tupleCmpBy;
tupleCmpBy = 0;
}
Tuple *NextResultTuple()
{
if( mergeTuples.empty() ) // stream finished
return 0;
else
{
// Take the first one.
TupleAndRelPos p = mergeTuples.top();
//p.tuple->DecReference();
mergeTuples.pop();
Tuple *result = p.tuple;
Tuple *t = 0;
if (p.pos > 0)
t = relations[p.pos-1].second->GetNextTuple();
else
{
int idx = p.pos+2;
if ( !queue[idx].empty() )
{
t = queue[idx].top().tuple;
//t->DecReference();
queue[idx].pop();
}
else
t = 0;
}
if( t != 0 )
{ // run not finished
p.tuple = t;
t->IncReference();
mergeTuples.push( p );
}
return result;
}
}
private:
void ShowPartitionInfo( int c, int a, int n,
int m, int r, GenericRelation* rel )
{
int rs = (rel != 0) ? rel->GetNoTuples() : 0;
if ( RTFlag::isActive("ERA:Sort:PartitionInfo") )
{
cmsg.info() << "Current run finished: "
<< " processed tuples=" << c
<< ", append minimum=" << m
<< ", append next=" << n << endl
<< " materialized runs=" << r
<< ", last partition's tuples=" << rs << endl
<< " Runs in memory: queue1= " << queue[0].size()
<< ", queue2= " << queue[1].size() << endl;
cmsg.send();
}
}
Word stream;
size_t currentIndex;
// tuple information
LexicographicalTupleCompare *lexiTupleCmp;
TupleCompareBy *tupleCmpBy;
bool lexicographic;
// sorted runs created by in memory heap filtering
size_t MAX_MEMORY;
typedef pair<TupleBuffer*, GenericRelationIterator*> SortedRun;
vector< SortedRun > relations;
typedef priority_queue<TupleAndRelPos> TupleQueue;
TupleQueue queue[2];
TupleQueue mergeTuples;
};
//-- end standard version --//
#else
//-- begin progress version --//
class SortByLocalInfo : protected ProgressWrapper
{
public:
SortByLocalInfo( Word stream, const bool lexicographic,
void *tupleCmp, ProgressLocalInfo* p ):
ProgressWrapper(p),
stream( stream ),
currentIndex( 0 ),
lexiTupleCmp( lexicographic ?
(LexicographicalTupleSmaller*)tupleCmp :
0 ),
tupleCmpBy( lexicographic ? 0 : (TupleCompareBy*)tupleCmp ),
lexicographic( lexicographic )
{
// Note: It is not possible to define a Cmp object using the
// constructor
// mergeTuples( PairTupleCompareBy( tupleCmpBy )).
// It does only work if mergeTuples is a local variable which
// does not help us in this case. Is it a Compiler bug or C++ feature?
// Hence a new class TupleAndRelPos was defined which implements
// the comparison operator '<'.
Heap* currentRun = &queue[0];
Heap* nextRun = &queue[1];
Word wTuple(Address(0));
size_t c = 0, i = 0, a = 0, n = 0, m = 0, r = 0; // counter variables
bool newRelation = true;
MAX_MEMORY = qp->MemoryAvailableForOperator();
cmsg.info("ERA:ShowMemInfo")
<< "Sortby.MAX_MEMORY (" << MAX_MEMORY/1024 << " kb)" << endl;
cmsg.send();
TupleBuffer *rel=0;
RTuple lastTuple;
qp->Request(stream.addr, wTuple);
RTuple minTuple;
while(qp->Received(stream.addr)) // consume the stream completely
{
progress->read++;
c++; // tuple counter;
Tuple *t = static_cast<Tuple*>( wTuple.addr );
TupleAndRelPos nextTuple(t, tupleCmpBy);
if( MAX_MEMORY > (size_t)t->GetSize() )
{
//nextTuple.tuple->IncReference();
currentRun->push(nextTuple);
i++; // increment Tuples in memory counter
MAX_MEMORY -= t->GetSize();
}
else
{ // memory is completely used
progress->state = 1;
if ( newRelation )
{ // create new relation
r++;
rel = new TupleBuffer( 0 );
GenericRelationIterator *iter = 0;
relations.push_back( make_pair( rel, iter ) );
newRelation = false;
// get first tuple and store it in an relation
//nextTuple.ref.tuple->IncReference();
currentRun->push(nextTuple);
minTuple = currentRun->topTuple();
rel->AppendTuple( minTuple.tuple );
lastTuple = minTuple;
minTuple.tuple->DeleteIfAllowed(); // remove from input stream
currentRun->pop();
}
else
{ // check if nextTuple can be saved in current relation
if ( nextTuple < TupleAndRelPos(lastTuple.tuple, tupleCmpBy) )
{ // nextTuple is in order
// Push the next tuple int the heap and append the minimum to
// the current relation and push
//##nextTuple.ref.tuple->IncReference();
currentRun->push(nextTuple);
minTuple = currentRun->topTuple();
rel->AppendTuple( minTuple.tuple );
minTuple.tuple->DeleteIfAllowed();
lastTuple = minTuple;
currentRun->pop();
m++;
}
else
{ // nextTuple is smaller, save it for the next relation
//##nextTuple.ref.tuple->IncReference();
nextRun->push(nextTuple);
n++;
if ( !currentRun->empty() )
{
// Append the minimum to the current relation
minTuple = currentRun->topTuple();
rel->AppendTuple( minTuple.tuple );
minTuple.tuple->DeleteIfAllowed();
lastTuple = minTuple;
currentRun->pop();
}
else
{ //create a new run
newRelation = true;
// swap queues
TupleQueue *helpRun = currentRun;
currentRun = nextRun;
nextRun = helpRun;
ShowPartitionInfo(c,a,n,m,r,rel);
i=n;
a=0;
n=0;
m=0;
} // end new run
} // end next tuple is smaller
// delete last tuple if saved to relation and
// not referenced by minTuple
/*if ( copyOfLast.tuple && (copyOfLast.tuple != minTuple.tuple) )
{
copyOfLast.tuple->DeleteIfAllowed();
}*/
} // check if nextTuple can be saved in current relation
}// memory is completely used
qp->Request(stream.addr, wTuple);
}
ShowPartitionInfo(c,a,n,m,r,rel);
// delete lastTuple and minTuple if allowed
/*##if ( lastTuple.tuple )
{
lastTuple.tuple->DeleteIfAllowed();
}
if ( (minTuple.tuple != lastTuple.tuple) )
{
minTuple.tuple->DeleteIfAllowed();
}*/
// the lastRun and NextRun runs in memory having
// less than MAX_TUPLE elements
if( !queue[0].empty() )
{
Tuple* t = queue[0].topTuple();
queue[0].pop();
mergeTuples.push( TupleAndRelPos(t, tupleCmpBy, -2) );
}
if( !queue[1].empty() )
{
Tuple* t = queue[1].topTuple();
queue[1].pop();
mergeTuples.push( TupleAndRelPos(t, tupleCmpBy, -1) );
}
// Get next tuple from each relation and push it into the heap.
for( size_t i = 0; i < relations.size(); i++ )
{
relations[i].second = relations[i].first->MakeScan();
Tuple *t = relations[i].second->GetNextTuple();
if( t != 0 )
{
//t->IncReference();
mergeTuples.push( TupleAndRelPos(t, tupleCmpBy, i+1) );
}
}
Counter::getRef("Sortby:ExternPartitions") = relations.size();
}
/*
It may happen, that the localinfo object will be destroyed
before all internal buffered tuples are delivered stream
upwards, e.g. queries which use a ~head~ operator.
In this case we need to delete also all tuples stored in memory.
*/
~SortByLocalInfo()
{
while( !mergeTuples.empty() )
{
mergeTuples.topTuple()->DeleteIfAllowed();
mergeTuples.pop();
}
for( int i = 0; i < 2; i++ )
{
while( !queue[i].empty() )
{
queue[i].topTuple()->DeleteIfAllowed();
queue[i].pop();
}
}
// delete information about sorted runs
for( size_t i = 0; i < relations.size(); i++ )
{
delete relations[i].second;
relations[i].second = 0;
delete relations[i].first;
relations[i].first = 0;
}
delete lexiTupleCmp;
lexiTupleCmp = 0;
delete tupleCmpBy;
tupleCmpBy = 0;
}
Tuple *NextResultTuple()
{
if( mergeTuples.empty() ) // stream finished
return 0;
else
{
// Take the first one.
TupleAndRelPos p = mergeTuples.top();
//p.tuple->DecReference();
mergeTuples.pop();
Tuple* result = p.tuple();
Tuple* t = 0;
if (p.pos > 0)
t = relations[p.pos-1].second->GetNextTuple();
else
{
int idx = p.pos+2;
if ( !queue[idx].empty() )
{
t = queue[idx].topTuple();
//t->DecReference();
queue[idx].pop();
}
else
t = 0;
}
if( t != 0 )
{ // run not finished
p.ref = t;
//t->IncReference();
mergeTuples.push( p );
}
return result;
}
}
private:
void ShowPartitionInfo( int c, int a, int n,
int m, int r, GenericRelation* rel )
{
int rs = (rel != 0) ? rel->GetNoTuples() : 0;
if ( RTFlag::isActive("ERA:Sort:PartitionInfo") )
{
cmsg.info() << "Current run finished: "
<< " processed tuples=" << c
<< ", append minimum=" << m
<< ", append next=" << n << endl
<< " materialized runs=" << r
<< ", last partition's tuples=" << rs << endl
<< " Runs in memory: queue1= " << queue[0].size()
<< ", queue2= " << queue[1].size() << endl;
cmsg.send();
}
}
Word stream;
size_t currentIndex;
// tuple information
LexicographicalTupleSmaller *lexiTupleCmp;
TupleCompareBy *tupleCmpBy;
bool lexicographic;
// sorted runs created by in memory heap filtering
size_t MAX_MEMORY;
typedef pair<TupleBuffer*, GenericRelationIterator*> SortedRun;
vector< SortedRun > relations;
typedef TupleQueue Heap;
Heap queue[2];
Heap mergeTuples;
};
//-- end progress version --//
#endif
/*
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<SortByLocalInfo>* liA;
SortByLocalInfo* sliA;
LocalInfo<SortByLocalInfo>* 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<bool BOTH_B>
int CompareTuples(Tuple* t1, Tuple* t2)
{
Attribute* a = 0;
if (BOTH_B)
a = static_cast<Attribute*>( t1->GetAttribute(attrIndexB) );
else
a = static_cast<Attribute*>( t1->GetAttribute(attrIndexA) );
Attribute* b = static_cast<Attribute*>( 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<true>(t1, t2);
}
inline int CompareTuples(Tuple* t1, Tuple* t2)
{
return CompareTuples<false>(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<Tuple*>( result.addr );
}
else
{
result.addr = 0;
return static_cast<Tuple*>( 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<Attribute*>(
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<Attribute*>(
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( pair<int, bool>(attrIndexA + 1, true) );
specB.push_back( pair<int, bool>(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 =
SecondoSystem::GetCatalog()->NumericType( qp->GetType( 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->MemoryAvailableForOperator();
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<bool expectSorted> 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;
size_t MAX_TUPLES_IN_MEMORY;
// buffer related members
TupleBuffer *grpB;
GenericRelationIterator *iter;
// members needed for sorting the input streams
LocalInfo<SortByLocalInfo>* liA;
SortByLocalInfo* sliA;
LocalInfo<SortByLocalInfo>* 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;
bool continueUndefB;
bool continueUndefA;
template<bool BOTH_B>
int CompareTuples(Tuple* t1, Tuple* t2)
{
Attribute* a = 0;
if (BOTH_B)
a = static_cast<Attribute*>( t1->GetAttribute(attrIndexB) );
else
a = static_cast<Attribute*>( t1->GetAttribute(attrIndexA) );
Attribute* b = static_cast<Attribute*>( 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<true>(t1, t2);
}
inline int CompareTuples(Tuple* t1, Tuple* t2)
{
return CompareTuples<false>(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<Tuple*>( result.addr );
}
else
{
result.addr = 0;
return static_cast<Tuple*>( 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<Attribute*>(
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<Attribute*>(
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( pair<int, bool>(attrIndexA + 1, true) );
specB.push_back( pair<int, bool>(attrIndexB + 1, true) );
void* tupleCmpA = new TupleCompareBy( specA );
void* tupleCmpB = new TupleCompareBy( specB );
liA = new LocalInfo<SortByLocalInfo>();
progress->firstLocalInfo = liA;
sliA = new SortByLocalInfo( streamA,
false,
tupleCmpA, liA );
liB = new LocalInfo<SortByLocalInfo>();
progress->secondLocalInfo = liB;
sliB = new SortByLocalInfo( streamB,
false,
tupleCmpB, liB );
}
ListExpr resultType =
SecondoSystem::GetCatalog()->NumericType( qp->GetType( s ) );
resultTupleType = new TupleType( nl->Second( resultType ) );
// read in the first tuple of both input streams
ptA.setTuple( NextTupleA() );
ptB.setTuple( NextTupleB() );
ListExpr typeA =
SecondoSystem::GetCatalog()->NumericType(
qp->GetType( streamA.addr ) );
ListExpr typeB =
SecondoSystem::GetCatalog()->NumericType(
qp->GetType( 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->MemoryAvailableForOperator();
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;
}
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<bool expectSorted> int
smouterjoin_vm(Word* args, Word& result,
int message, Word& local, Supplier s)
{
typedef LocalInfo<MergeOuterjoinLocalInfo> LocalType;
LocalType* li = static_cast<LocalType*>( 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<ProgressInfo*>( 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<SortByLocalInfo>* liFirst;
LocalInfo<SortByLocalInfo>* liSecond;
if( !li ) return CANCEL;
else
{
liFirst = static_cast<LocalInfo<SortByLocalInfo>*>
(li->firstLocalInfo);
liSecond = static_cast<LocalInfo<SortByLocalInfo>*>
(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 =
SecondoSystem::GetCatalog()->NumericType(
qp->GetType( streamRight.addr ) );
tupleTypeRight = new TupleType( nl->Second( typeRight ) );
ListExpr typeLeft =
SecondoSystem::GetCatalog()->NumericType(
qp->GetType( 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;
SmiKeyedFileIterator *smiIter;
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<Attribute*>(
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<Attribute*>(
am->CreateObj(algId, typeId)(0).addr );
attr->SetDefined( false );
result->PutAttribute( i, attr );
}
undefLeft = result;
}
else {
result = undefLeft;
}
return result;
}
};
template<int dummy>
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->MemoryAvailableForOperator();
cmsg.info("ERA:ShowMemInfo") << "SymmOuterJoin.MAX_MEMORY ("
<< MAX_MEMORY/1024 << " MB): " << 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->smiIter = new SmiKeyedFileIterator( true );
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 = new SmiRecord();
while ( rightOuterTuple != 0 &&
file->SelectRecord(
SmiKey((long)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 = new SmiRecord();
while ( leftOuterTuple != 0 &&
file->SelectRecord(
SmiKey((long)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((long)leftTuple->GetTupleId()),
(SmiRecordId)leftTuple->GetTupleId());
pli->rightHash->Append(
SmiKey((long)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((long)rightTuple->GetTupleId()),
(SmiRecordId)rightTuple->GetTupleId() );
pli->leftHash->Append(
SmiKey((long)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 =
SecondoSystem::GetCatalog()->NumericType(
qp->GetType( streamRight.addr ) );
tupleTypeRight = new TupleType( nl->Second( typeRight ) );
ListExpr typeLeft =
SecondoSystem::GetCatalog()->NumericType(
qp->GetType( streamLeft.addr ) );
tupleTypeLeft = new TupleType( nl->Second( typeLeft ) );
undefRight = 0;
undefLeft = 0;
}
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;
SmiKeyedFileIterator *smiIter;
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<Attribute*>(
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<Attribute*>(
am->CreateObj(algId, typeId)(0).addr );
attr->SetDefined( false );
result->PutAttribute( i, attr );
}
undefLeft = result;
}
else {
result = undefLeft;
}
return result;
}
};
template<int dummy>
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->MemoryAvailableForOperator();
cmsg.info("ERA:ShowMemInfo") << "SymmOuterJoin.MAX_MEMORY ("
<< MAX_MEMORY/1024 << " MB): " << 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;
pli->rightHash = new Hash( SmiKey::Integer, true );
pli->leftHash = new Hash( SmiKey::Integer, true );
pli->nullTuples = false;
pli->smiIter = new SmiKeyedFileIterator( true );
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 = new SmiRecord();
while ( rightOuterTuple != 0 &&
file->SelectRecord(
SmiKey((long)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 );
Concat( rightOuterTuple, pli->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 = new SmiRecord();
while ( leftOuterTuple != 0 &&
file->SelectRecord(
SmiKey((long)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 );
Concat( pli->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 );
pli->leftHash->Append( SmiKey((long)leftTuple->GetTupleId()),
(SmiRecordId)leftTuple->GetTupleId());
pli->rightHash->Append(
SmiKey((long)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((long)rightTuple->GetTupleId()),
(SmiRecordId)rightTuple->GetTupleId() );
pli->leftHash->Append(
SmiKey((long)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<false>(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);