Files
secondo/Algebras/PlugJoin/PlugJoinAlgebra.cpp

1970 lines
54 KiB
C++
Raw Normal View History

2026-01-23 17:03:45 +08:00
/*
//paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}]
//paragraph [10] Footnote: [{\footnote{] [}}]
//[TOC] [\tableofcontents]
//[->] [$\rightarrow $]
{\Large \bf Anhang G: Plug\&Join-Algorithmus }
[1] Implementation of PlugJoin-Algebra
October 2004, Herbert Schoenhammer
December 2005, Victor Almeida deleted the deprecated algebra levels
(~executable~, ~descriptive~, and ~hibrid~). Only the executable
level remains. Models are also removed from type constructors.
January 2006 Victor Almeida replaced the ~free~ tuples concept to
reference counters. There are reference counters on tuples and also
on attributes.
[TOC]
0 Overview
This algebra implements the Plug\&Join-Algorithm of Bercken, Schneider and
Seeger: ~Plug\&Join: An Easy-To-Use Generic Algorithm for Efficiently
Processing Equi and Non-equi Joins~. The algebra uses a main memory
representation of a R-Tree implemented in the PlugJoin.h file.
This file especially implements the managing functions (type mapping,
value mapping, ...) to correspond with the system frame of Secondo.
The value mapping function implements the Plug\&Join-Algorithm managing
the recursive algorithm of Plug\&Join in such a way, that requests of
following stream-operators may get the result-tuples one by one.
This algebra provides the operator ~spatialjoin~ which joins two input-
streams by a spatial attribute. The result is a output-stream containing
all tuples of the input-streams intersecting each other.
The main problem using input streams is, that the tuples in streams
do not know, from which relation they come from first. So it is necessary
to collect the tuples in a temporary relation (TupleBuffer) and give them
a new identificator (ID). The Plug\&Join-Algorithm uses this temporary
relations.
1 Defines and Includes
*/
#include <string.h>
#include <set>
#include "PlugJoinAlgebra.h"
#include "ListUtils.h"
#include "Symbols.h"
using namespace std;
extern NestedList* nl;
extern QueryProcessor* qp;
#define BBox Rectangle
#ifndef DOUBLE_MAX
#define DOUBLE_MAX (1.7E308)
#endif
/*
If ~PLUGJOIN\_VERBOSE\_MODE~ is set, some interesting things are reported.
This is statistical info for the initial R-Tree or the number of recursive
instances of the algorithm and the number of R-Trees built in the Plug\&Join-
Algorithm.
*/
//#define PLUGJOIN_VERBOSE_MODE
/*
If ~PLUGJOIN\_VERY\_VERBOSE\_MODE~ is set, further Information about the number
or R-Trees built in every recursive instance, the number of overflowed leaves,
the number of not inserted data entries and the number of
the number of not answerable window queries is reported for each recursive
instance.
Could be of interest if you want to study partitioning the data through the
R-Trees.
*/
//#define PLUGJOIN_VERY_VERBOSE_MODE
/*
2 Auxiliary Functions
*/
int myComparePnJ( const void* a, const void* b )
{
if( ((SortedArrayItem *) a)->pri < ((SortedArrayItem *) b)->pri )
return -1;
else if( ((SortedArrayItem *) a)->pri > ((SortedArrayItem *) b)->pri )
return 1;
else
return 0;
}
/*
3 Operator ~spatialjoin~
3.1 Overview
The operator ~spatialjoin~ has following inputs and semantic:
* First=left input stream: A stream of tuples which is collected in a
temporary relation. The temporary relation corresponding to the left
input stream is treated as the outer relation.
* Second=right input stream: A stream of tuples which is collected in another
temporary relation. The temporary relation corresponding to the right input
stream is treated as the inner relation.
* Outer Relation S: The outer relation S should have more tuples than the
inner relation R. For the inner relation R no index is needed, R is
sequential traversed. R must have at least one attribut of type ~rect~,
~rect3~, ~rect4~ or of ~spatialkind~. The name of this attribut is needed
using the operator.
* Inner Relation R: The inner relation of this operator should be the smaller
one of the two input relations. R must have at least one attribut of type
~rect~, ~rect3~, ~rect4~ or of ~spatialkind~. The name of this attribut is
needed using the operator.
* Name of joining attribute of the inner Relation R: must be of type ~rect~,
~rect3~, ~rect4~ or of ~spatialkind~.
* Name of joining attribute of the outer Relation S: must be of type ~rect~,
~rect3~, ~rect4~ or of ~spatialkind~.
3.2 Syntax and Signature
The operator ~spatialjoin~ has the following syntax:
outerStream innerStream spatialjoin [attrName\_outerRelation, attrName\_innerRelation]
The signature of ~spatialjoin~ is:
----
( (stream (tuple ((x1 t1)...(xn tn))))
(stream (tuple ((y1 yt1)...(yn ytn))))
rect||rect3||rect4||spatialtype
rect||rect3||rect4||spatialtype )
-> (stream (tuple ((x1 t1)...(xn tn)((y1 yt1)...(yn ytn)))))
----
A query-example is: query Rel1 feed Rel2 feed spatialjoin [attrNameRel1, attrNameRel2] consume;
*/
/*
3.3 Type mapping function of operator ~spatialjoin~
*/
ListExpr spatialjoinTypeMap(ListExpr args)
{
string err = "stream(tuple) x stream(tuple) x name1 x name2 expected";
if(nl->ListLength(args)!=4){
return listutils::typeError(err);
}
ListExpr stream1 = nl->First(args);
ListExpr stream2 = nl->Second(args);
ListExpr nameL1 = nl->Third(args);
ListExpr nameL2 = nl->Fourth(args);
if(!listutils::isTupleStream(stream1) ||
!listutils::isTupleStream(stream2) ||
!listutils::isSymbol(nameL1) ||
!listutils::isSymbol(nameL2)){
return listutils::typeError(err);
}
ListExpr al1 = nl->Second(nl->Second(stream1));
ListExpr al2 = nl->Second(nl->Second(stream2));
if(!listutils::disjointAttrNames(al1, al2)){
return listutils::typeError("conflicting type names");
}
ListExpr type1;
string name1 = nl->SymbolValue(nameL1);
int index1 = listutils::findAttribute(al1,name1,type1);
if(index1==0){
return listutils::typeError("attribute " + name1 + "not found");
}
ListExpr type2;
string name2 = nl->SymbolValue(nameL2);
int index2 = listutils::findAttribute(al2,name2,type2);
if(index2==0){
return listutils::typeError("attribute " + name2 + "not found");
}
// check for rect, rect3, rect4
set<string> r;
r.insert(Rectangle<2>::BasicType());
r.insert(Rectangle<3>::BasicType());
r.insert(Rectangle<4>::BasicType());
if(!listutils::isASymbolIn(type1,r) &&
!listutils::isKind(type1,Kind::SPATIAL2D())){
return listutils::typeError("attribute " + name1 +
" not supported by spatial join");
}
if(!listutils::isASymbolIn(type2,r) &&
!listutils::isKind(type1,Kind::SPATIAL2D())){
return listutils::typeError("attribute " + name2 + " not supported");
}
if(!listutils::isSymbol(type1) ||
!listutils::isSymbol(type2)){
return listutils::typeError("composite types not supported");
}
if(!nl->Equal(type1,type2)){
return listutils::typeError("different types");
}
ListExpr attrlist = listutils::concat(al1, al2);
return nl->ThreeElemList(
nl->SymbolAtom(Symbol::APPEND()),
nl->ThreeElemList(
nl->IntAtom(index1),
nl->IntAtom(index2),
nl->StringAtom(nl->SymbolValue(type1))),
nl->TwoElemList(
nl->SymbolAtom(Symbol::STREAM()),
nl->TwoElemList(
nl->SymbolAtom(Tuple::BasicType()),
attrlist)));
}
/*
3.4 Selection function of operator ~spatialjoin~
*/
int
spatialjoinSelection (ListExpr args)
{
/* find out type of key; similar to typemapping function */
/* Split argument in four parts */
ListExpr relDescriptionS = nl->First(args); //outerRelation
ListExpr attrNameS_LE = nl->Third(args); //attrName of outerRel
ListExpr tupleDescriptionS = nl->Second(relDescriptionS);
ListExpr attrListS = nl->Second(tupleDescriptionS);
/* handle attrName of outerRelation */
//int attrIndexS;
ListExpr attrTypeS;
string attrNameS = nl->SymbolValue(attrNameS_LE);
FindAttribute(attrListS, attrNameS, attrTypeS);
/* selection function */
ListExpr errorInfo = nl->OneElemList ( nl->SymbolAtom ("ERRORS"));
AlgebraManager* algMgr = SecondoSystem::GetAlgebraManager();
if ( (algMgr->CheckKind(Kind::SPATIAL2D(), attrTypeS, errorInfo)) ||
( nl->SymbolValue (attrTypeS) == Rectangle<2>::BasicType()) )
return 0; //two-dimensional objects to join
else if ( nl->SymbolValue (attrTypeS) == Rectangle<3>::BasicType())
return 1; //three-dimensiona objects to join
else if ( nl->SymbolValue (attrTypeS) == Rectangle<4>::BasicType())
return 2; //four-dimensional objects to join
else return -1; /* should not happen */
}
/*
3.5 ~SpatialJoinLocalInfo~: Auxiliary Class for operator ~spatialjoin~
*/
template<unsigned dim>
class SpatialJoinLocalInfo
{
/*
3.5.1 Private declarations
*/
private:
typedef multimap< ArrayIndex,R_TreeEntryPnJ<dim> > Partition;
/*
Type to store the partitions:
1 Multimap for creating the R.Partitions. Pairs (nodeNumber in
which the insertion would be done, the entry which should be inserted)
are gathered. This multimap is only a buffer: if pagesize is reached,
the content of the multimap is stored to a SMI-File.
2 Multimap for creating the S.Partitions. Pairs (nodeNumber of
overflowed leave, the searchWindow) are gathered. This multimap is only
a buffer: if pagesize is reached, the content of the multimap is stored
to a SMI-File.
*/
struct NodeInfo
{
unsigned int counter; //Number of overflows/queries
SmiRecordId firstRId; //First Record of overflows/queries
SmiRecordId actualRId; //Record to which next write will be done
NodeInfo() :
counter ( 0 ),
firstRId ( 0 ),
actualRId ( 0 )
{}
NodeInfo(unsigned int counter, SmiRecordId firstRId, SmiRecordId actualRId):
counter ( counter ), firstRId (firstRId), actualRId (actualRId)
{}
};
typedef map< ArrayIndex, NodeInfo > PartitionInfo;
typedef typename PartitionInfo::iterator PartInfoIter;
/*
Type to store some information about the partitions:
1 Map for counting the number of not inserted entries. Necessary for
flushing overflowed nodes to SMI-File. The RecordId of th first record
is stored (and a temporary RecordId only used for building the chained
list).
2 Map for counting the number of not answerable queries (because of
node overflow).The RecordId of th first record
is stored (and a temporary RecordId only used for building the chained
list).
*/
struct PartitionsInfo
{
PartitionInfo* innerInfo;
PartitionInfo* outerInfo;
};
/*
Information about partitions of one recursive instance of the Plug\&Join-
Algorithm.
1 Map for counting the number of not inserted entries. Necessary for
flushing overflowed nodes to SMI-File.
2 Map for counting the number of not answerable queries (because of
node overflow).
*/
struct Header
{
R_TreePnJ<dim>* rtree; //pointer to the R-Tree
ArrayIndex outerAttrIndex; //outerRelation: # of joining attribute
TupleBuffer *innerRelation; //the buffer corresponding to the right stream
TupleBuffer *outerRelation; //the buffer corresponding to the left stream
int recordHeaderSize; //size of record Header (# of partition, # of entries
//in record, nextRecordId);
int maxEntriesInRecord; //# of entries which can be written in one record
SmiRecordFile file; //SMI-file to store partitions of overflowed leaves
PartitionInfo* firstPartitionInfo; //PartitionInfo created by constructor
bool firstRTree; //controls for NextResultTuple
bool newPartitionsFromStack;
bool nextQueryInPartition;
bool nextTupleOuterRelation;
bool firstSearchForTuple;
bool searchForTuple;
bool lastPartition;
bool getEntryFromFirstRecord; //read first record of chained list
bool GetNextEntry_readFirst; //controls for GetNextEntry
bool GetNextEntry_read;
GenericRelationIterator *outerIter;
//iterates OuterRelation in NextResultTuple
SmiRecordId outerActualTupleId; //RecordId of actual Tuple in
//NextResultTuple
R_TreeEntryPnJ<dim> outerEntry;
R_TreeEntryPnJ<dim> foundEntry;
//Next Entry found by First/Next of the R-Tree
Partition* outerRelPart; //gathering overflows in one partition
PartitionInfo* outerRelInfo; //inserts of outer relation
PartitionsInfo* actualMaps; //all Partitions in one recursive level
PartInfoIter actualMapsIter;
ArrayIndex actualNodeNo;
Partition* innerRelPart; //gathering overflows in one partition
PartitionInfo* innerRelInfo; //queries of inner relation
Header ():
rtree ( 0 ),
outerAttrIndex ( 0 ),
innerRelation ( ),
outerRelation ( ),
recordHeaderSize ( 0 ),
maxEntriesInRecord ( 0 ),
file ( true, page_size),
firstPartitionInfo ( 0 ),
firstRTree ( true ),
newPartitionsFromStack ( true ),
nextQueryInPartition ( true ),
nextTupleOuterRelation ( true ),
firstSearchForTuple ( true ),
searchForTuple ( true ),
lastPartition ( false ),
getEntryFromFirstRecord (true ),
GetNextEntry_readFirst ( true ),
GetNextEntry_read ( true ),
outerIter ( 0 ),
outerActualTupleId ( 0 ),
outerEntry(),
outerRelPart ( 0 ),
outerRelInfo ( 0 ),
actualMaps ( 0 ),
actualMapsIter ( ),
actualNodeNo ( 0 ),
innerRelPart ( 0 ),
innerRelInfo ( 0 )
{}
} hdr;
/*
The header stores a lot of information needed to give information from
constructor ~SpatialJoinLocalInfo~ to ~NextResultTuple~. Further all
variables and data for several calls of ~NextResultTuple~ (initialized by
stream-REQUEST) are stored in the header.
*/
typedef stack <PartitionsInfo> AllPartitions;
AllPartitions Parts;
/*
The stack to gather all ParttionInfo in recursive order.
*/
#ifdef PLUGJOIN_VERBOSE_MODE
int rtreeCounter;
/*
Counts the number of built R-Trees.
*/
#endif
TupleBuffer* StreamBuffer (const Word stream);
/*
Creates a TupleBuffer and receives the tuples of the ~stream~ one by one.
They are appended to the buffer. If the stream is empty '0' is returned.
*/
int UseNodesInTree (const int noTuplesR, const int noTuplesS);
/*
Computes the nodes to use in the R-Tree of the next recursive instance of the
Plug\&Join-Algorithm. The formula is suggested by Bercken et al..
*/
void BufferInsert (multimap< ArrayIndex,R_TreeEntryPnJ<dim> >* mm,
map< ArrayIndex,NodeInfo >* m,
ArrayIndex& nodeNo, R_TreeEntryPnJ<dim>& entry);
/*
Inserts ~entry~ in partition and partitionInfo with key value ~nodeNo~:
1 Inserting the entry into innerRelPart ~mm~ and increment counter ~m~ for
overflowed leave.
2 Inserting the entry into outerRelPart ~mm~ and increment counter ~m~ for
not answerable query.
*/
void FlushAllLeavesToBuffer(Partition* innerRelPart,
PartitionInfo* innerRelInfo);
/*
Reads all Entries of overflowed Leaves and stores it in the innerRelPart
(R.Part).
*/
void FlushBufferToFile (Partition* mm,
PartitionInfo* m);
/*
For every nodeNo in ~m~ all entries in ~mm~ are stored to file.
*/
void FlushLeavesOverflowed(vector <ArrayIndex>& leavesOverflowed,
R_TreeEntryPnJ<dim>& entry,
Partition* outerRelPart,
PartitionInfo* outerRelCounter);
/*
Flush auxiliary vector with overflowed leaves (of not answerable queries)
and build S.Partition
*/
void Write (Partition* mm,
ArrayIndex& nodeNo,
SmiRecordId& recNo,
SmiRecordId& nextRecordId);
/*
Writes all entries for node ~nodeNo~ in multimap ~mm~ into record ~recNo~.
*/
void ReadFirstEntries (PartitionInfo* m, ArrayIndex& actualNodeNo,
SmiRecordId& nextRecordId,
vector < R_TreeEntryPnJ<dim> >& entriesOfRecord );
void ReadNextEntries ( SmiRecordId& NextRecordId,
vector < R_TreeEntryPnJ<dim> >&entriesOfRecord );
R_TreeEntryPnJ<dim>* GetNextEntry(PartitionInfo* m,
ArrayIndex& actualNodeNo);
/*
~ReadFirstEntries~ searches in ~m~ the entry with m[->]first == actualNodeNo.
Reads the record with m[->]second.firstRId == recordId. Interprets the record
and returns the entries in the vector ~entriesOfRecord~. The recordid of the next
record is returned in ~nextRecordId~. If there's no further next record, the
returned ~nextRecord~ is 0. (Note: The first recordid generated from the SMI is 1).
~ReadNextEntries~ reads the ~NextRecord~.
~GetNextEntry~ uses ~ReadFirstEntries~ and ~ReadNextEntries~ to get the first/
next entry from SMI-File.
~hdr.GetNextEntry\_readFirst~ and ~hdr.GetNextEntry\_read~ are needed for controlling
~GetNextEntry~ over a sequence of requests.
*/
Tuple* NextResultTupleFromStack();
/*
Mostly the same than the ~constructor~ and ~NextResultTuple~, but
generating R-Tree and queries from partitions generated by ~constructor~
and ~NextResultTuple~.
*/
Tuple* newResultTupleFromEntries (R_TreeEntryPnJ<dim>& outerEntry,
R_TreeEntryPnJ<dim>& foundEntry);
/*
Generates a new Tuple (that must be deleted somewhere), evaluating the
pointers of the entries and concatenating the tuples from inner and
outer relation.
*/
/*
3.5.2 Public declarations
*/
public:
SpatialJoinLocalInfo(Word innerStreamWord,
Word innerAttrIndexWord,
Word outerStreamWord,
Word outerAttrIndexWord);
/*
The constructor. It builds the initial R-Tree. Further the first
R.Partitions and S.Partitions are built. At this point of time
the Partitions are gathered in maps and Multimaps.
*/
Tuple* NextResultTuple ();
/*
Returns the next result tuple
*/
TupleType *resultTupleType;
/*
The tuple type of result tuples.
*/
~SpatialJoinLocalInfo()
/*
The destructor
*/
{
#ifdef PLUGJOIN_VERBOSE_MODE
cout << "__________________________________________________" << endl;
cout << endl << "R-Trees built in query : " << rtreeCounter << endl;
cout << "__________________________________________________" << endl;
#endif
//delete the SMI-File
hdr.file.Close();
hdr.file.Drop();
// delete the last R-Tree
delete hdr.rtree;
hdr.rtree = 0;
// delete the partitions and Infos
if (hdr.firstPartitionInfo){
delete hdr.firstPartitionInfo;
hdr.firstPartitionInfo=0;
}
if (hdr.outerRelPart){
delete hdr.outerRelPart;
hdr.outerRelPart=0;
}
if(hdr.outerRelInfo){
delete hdr.outerRelInfo;
hdr.outerRelInfo=0;
}
if (hdr.actualMaps){
if(hdr.actualMaps->innerInfo){
hdr.actualMaps->innerInfo->clear();
delete hdr.actualMaps->innerInfo;
}
if(hdr.actualMaps->outerInfo){
hdr.actualMaps->outerInfo->clear();
delete hdr.actualMaps->outerInfo;
}
delete hdr.actualMaps;
hdr.actualMaps=0;
}
if (hdr.innerRelPart){
delete hdr.innerRelPart;
hdr.innerRelPart=0;
}
if (hdr.innerRelInfo){
delete hdr.innerRelInfo;
hdr.innerRelInfo=0;
}
//delete the TupleBuffers
if ( hdr.innerRelation )
{
hdr.innerRelation->Clear();
delete hdr.innerRelation;
hdr.innerRelation = 0;
}
if ( hdr.outerRelation )
{
hdr.outerRelation->Clear();
delete hdr.outerRelation;
hdr.outerRelation=0;
}
};
};
/*
3.5.3 The constructor
*/
template <unsigned dim>
SpatialJoinLocalInfo<dim>::SpatialJoinLocalInfo
(Word innerStreamWord, Word innerAttrIndexWord,
Word outerStreamWord, Word outerAttrIndexWord)
{
/*
First buffer the input streams in TupleBuffers.
*/
hdr.innerRelation = StreamBuffer (innerStreamWord); //inserts
hdr.outerRelation = StreamBuffer (outerStreamWord); //queries
/*
Create the SMI-File for storing partitions.
*/
bool t1 = hdr.file.Create();
assert ( t1 );
assert ( hdr.file.IsOpen() );
/*
If one of the both input relations is empty, no further work is do be done.
~NextResultTuple~ proofs, if one Buffer is empty.
*/
if ( hdr.innerRelation && hdr.outerRelation )
{
/*
Do some initializing work.
*/
//the attribute-numbers of the joining attributes
hdr.outerAttrIndex = (((CcInt*)outerAttrIndexWord.addr)->GetIntval()) - 1;
ArrayIndex innerAttrIndexPnJ =
(((CcInt*)innerAttrIndexWord.addr)->GetIntval())-1;
//some sizes for storing entries in SMI-Records
hdr.recordHeaderSize = sizeof(ArrayIndex) //NodeNo of the following
//entries in Record
+ sizeof(int) //# of entries in Record
+ sizeof(SmiRecordId); //# of next Record in SMI-File
hdr.maxEntriesInRecord = (int)((page_size - hdr.recordHeaderSize) /
(sizeof(R_TreeEntryPnJ<dim>)));
Tuple* innerTuple;
ArrayIndex nodeNo; //No of the node in which the insertion would be
//done, if the leave overflows
/*
Structures for S.Partition and informations about S.Partition.
*/
Partition* innerRelPart = new Partition();
PartitionInfo* innerRelInfo = new PartitionInfo();
/*
Building the initial R-Tree and gathering overflowed entries in partiton.
*/
//iterator of the inner relation for building initial R-Tree
GenericRelationIterator *innerIter = hdr.innerRelation->MakeScan();
//computing the number of nodes to use for initial R-Tree
int nodesToUse = UseNodesInTree (hdr.outerRelation->GetNoTuples(),
hdr.innerRelation->GetNoTuples());
hdr.rtree = new R_TreePnJ<dim>( page_size,
default_entries_per_node,
min_entries_per_centage,
nodesToUse );
#ifdef PLUGJOIN_VERBOSE_MODE
rtreeCounter = 1;
#endif
while ( (innerTuple = innerIter->GetNextTuple()) != 0 )
{
BBox<dim> box = ((StandardSpatialAttribute<dim>*)innerTuple->
GetAttribute(innerAttrIndexPnJ))->BoundingBox();
if(box.IsDefined()){ // an undefined box cannot intersects anything
R_TreeEntryPnJ<dim> e( box, innerIter->GetTupleId() );
if ( !(hdr.rtree)->Insert( e, nodeNo ) )
{
BufferInsert( innerRelPart, innerRelInfo, nodeNo, e);
}
}
innerTuple->DeleteIfAllowed();
}
delete innerIter;
/*
Flushing all overflowed leaves to buffer.
*/
FlushAllLeavesToBuffer(innerRelPart, innerRelInfo);
/*
The buffers may contain some information not stored to SMI-File until now,
so flushes buffer to SMI-file.
*/
FlushBufferToFile (innerRelPart, innerRelInfo);
//partitionBuffer no longer needed
delete innerRelPart;
innerRelPart = 0; //safety first
/*
PartitionInfo needed in NextResultTuple for answering the queries.
*/
hdr.firstPartitionInfo = innerRelInfo;
innerRelInfo = 0;
#ifdef PLUGJOIN_VERBOSE_MODE
cout << "==========================================" << endl;
cout << "Some information about the initial R-Tree:" << endl;
hdr.rtree->Info();
cout << "==========================================" << endl;
#endif
/*
Setting controls for REQUEST / nextResultTuple(..).
*/
hdr.outerIter = hdr.outerRelation->MakeScan();
hdr.outerRelPart = new Partition();
hdr.outerRelInfo = new PartitionInfo();
} // if (hdr.innerRelation || hdr.outerRelation)
};
/*
3.5.3 Method NextResultTuple
*/
template <unsigned dim>
Tuple* SpatialJoinLocalInfo<dim>::NextResultTuple ()
{
/*
If one of the input relations is empty, there could be no result-tuples.
*/
if ( (hdr.innerRelation == 0) ||
(hdr.outerRelation == 0) )
{
return 0;
}
/*
Otherwise the join must be computed.
*/
bool nextResultTupleFound = false;
Tuple* outerTuple=0;
BBox<dim> SBox; //searchBox for window-query
R_TreeEntryPnJ<dim> foundEntry;
vector <ArrayIndex> leavesOverflowed; //if leaf is empty (overflow in
//constructor) the nodeNo for
//S.Partition
if (hdr.firstRTree )
{
/*
Building the queries.
*/
while ( !nextResultTupleFound )
{
if ( hdr.nextTupleOuterRelation )
{
if ( (outerTuple = hdr.outerIter->GetNextTuple()) != 0)
{
/*
Next tuple found in outer Relation.
*/
SBox = ((StandardSpatialAttribute<dim>*)outerTuple->
GetAttribute(hdr.outerAttrIndex))->BoundingBox();
hdr.outerActualTupleId = hdr.outerIter->GetTupleId();
hdr.outerEntry = R_TreeEntryPnJ<dim>(SBox, hdr.outerActualTupleId);
// outerTuple->DeleteIfAllowed(); //comment by jianqiu
}
else
{
/*
No further tuple in outer relation.
*/
delete hdr.outerIter;
hdr.outerIter = 0;
FlushBufferToFile ( hdr.outerRelPart, hdr.outerRelInfo );
if ( hdr.outerRelInfo->empty() )
{
//no overflow in initial R-Tree.
delete hdr.firstPartitionInfo;
hdr.firstPartitionInfo = 0;
delete hdr.outerRelPart;
hdr.outerRelPart = 0;
delete hdr.outerRelInfo;
hdr.outerRelInfo = 0;
return 0;
break;
}
else
{
/*
Overflows in initial R-Tree, so store gathered overflows and not answerable
queries into stack.
*/
PartitionsInfo InitialParts;
InitialParts.innerInfo = hdr.firstPartitionInfo;
InitialParts.outerInfo = hdr.outerRelInfo;
Parts.push( InitialParts );
//safety and deleting
hdr.firstPartitionInfo = 0;
hdr.outerRelInfo = 0;
delete hdr.outerRelPart;
hdr.outerRelPart = 0;
//initialize first call for NextResultTupleFromStack
hdr.firstRTree = false;
hdr.newPartitionsFromStack = true; //newMapFromStack
hdr.nextQueryInPartition = true; //nextPartitionInMap
hdr.nextTupleOuterRelation = true;
hdr.firstSearchForTuple = true;
hdr.searchForTuple = true;
hdr.lastPartition = false;
return NextResultTupleFromStack();
}
}
};
if ( hdr.firstSearchForTuple )
{
if ( hdr.rtree->First (SBox, foundEntry, leavesOverflowed) )
{
/*
First search for SBox in R-Tree finds an entry.
*/
hdr.firstSearchForTuple = false;
hdr.nextTupleOuterRelation = false;
nextResultTupleFound = true;
/*
Building S.Part of outerRelation.
*/
FlushLeavesOverflowed(leavesOverflowed, hdr.outerEntry,
hdr.outerRelPart, hdr.outerRelInfo);
Tuple* innerTuple =
((TupleBuffer*)hdr.innerRelation)->GetTuple(foundEntry.pointer,
false);
Tuple* resultTuple = new Tuple( resultTupleType );
Concat(outerTuple, innerTuple, resultTuple);
outerTuple->DeleteIfAllowed();
outerTuple = 0;
innerTuple->DeleteIfAllowed();
innerTuple = 0;
return resultTuple;
break;
}
else
{
/*
First search for SBox in R-Tree did not find an entry.
*/
hdr.firstSearchForTuple = true;
hdr.nextTupleOuterRelation = true;
nextResultTupleFound = false;
outerTuple->DeleteIfAllowed();
outerTuple = 0;
/*
Building S.Part of outerRelation.
*/
FlushLeavesOverflowed(leavesOverflowed, hdr.outerEntry,
hdr.outerRelPart, hdr.outerRelInfo);
}
}
else //if ( localInfo->firstSearchForTuple )
{
if ( hdr.rtree->Next (foundEntry, leavesOverflowed) )
{
/*
Next search for SBox finds an entry in the R-Tree.
*/
hdr.firstSearchForTuple = false;
hdr.nextTupleOuterRelation = false;
nextResultTupleFound = true;
/*
Building S.Part of outerRelation.
*/
FlushLeavesOverflowed(leavesOverflowed, hdr.outerEntry,
hdr.outerRelPart, hdr.outerRelInfo);
Tuple* innerTuple =
((TupleBuffer*)hdr.innerRelation)->GetTuple(foundEntry.pointer,
false);
Tuple* resultTuple = new Tuple( resultTupleType );
outerTuple =
((TupleBuffer*)hdr.outerRelation)->GetTuple(hdr.outerActualTupleId,
false);
Concat(outerTuple, innerTuple, resultTuple);
outerTuple->DeleteIfAllowed();
outerTuple = 0;
innerTuple->DeleteIfAllowed();
innerTuple = 0;
return resultTuple;
}
else
{
/*
Next search for SBox did not find an entry in the R-Tree.
*/
hdr.firstSearchForTuple = true;
hdr.nextTupleOuterRelation = true;
nextResultTupleFound = false;
/*
Building S.Part of outerRelation.
*/
FlushLeavesOverflowed(leavesOverflowed, hdr.outerEntry,
hdr.outerRelPart, hdr.outerRelInfo);
}
} //if ( localInfo->firstSearchForTuple )
} //while ( !nextResultTupleFound )
}
else
{
/*
Computing the recursive instances of Plug\_Join getting the entries from Stack
and temporary SMI-File. In this case the extracting of bounding boxes and
Tuple-Id is not necessary.
*/
return NextResultTupleFromStack();
}
assert( !nextResultTupleFound );
return 0;
};
/*
3.5.3 Method NextResultTupleFromStack
*/
template <unsigned dim>
Tuple* SpatialJoinLocalInfo<dim>::NextResultTupleFromStack()
{
vector <ArrayIndex> leavesOverflowed;
bool nextResultTupleFound = false;
//needed for computing the size of next R-Tree to build
int noEntriesOuterRelation, noEntriesInnerRelation, nodesToUse;
while ( !nextResultTupleFound )
{
if ( hdr.newPartitionsFromStack )
{
/*
Getting all information about the next recursive instance of the Plug\&Join-
Algorithm from the stack.
*/
if ( Parts.empty() )
{
// no further result tuples
hdr.actualMaps->innerInfo->clear();
delete (hdr.actualMaps)->innerInfo;
hdr.actualMaps->innerInfo = 0;
hdr.actualMaps->outerInfo->clear();
delete (hdr.actualMaps)->outerInfo;
hdr.actualMaps->outerInfo = 0;
return 0;
break;
}
else
{
/*
In every recursive instance of the Plug\&Join-Algorithm a R-Tree for each
overflowed leaf of a former recursive instance must be built. Now extract
the informations needed for building the R-Trees of this instance one by one.
*/
#ifdef PLUGJOIN_VERY_VERBOSE_MODE
cout << endl
<< "**********************************************************"
<< "***************************" << endl;
cout << "Stacksize = " << Parts.size()
<< " (=number of recursive instances in the stack)" << endl;
cout << "=========================================================="
<< "======" << endl;
#endif
if(hdr.actualMaps){ // delete old stuff
if(hdr.actualMaps->innerInfo){
delete hdr.actualMaps->innerInfo;
}
if(hdr.actualMaps->outerInfo){
delete hdr.actualMaps->outerInfo;
}
delete hdr.actualMaps;
}
hdr.actualMaps = new PartitionsInfo();
(hdr.actualMaps)->innerInfo = Parts.top().innerInfo;
(hdr.actualMaps)->outerInfo = Parts.top().outerInfo;
Parts.pop();
#ifdef PLUGJOIN_VERY_VERBOSE_MODE
cout << "Number partitions of outer relation: "
<< hdr.actualMaps->outerInfo->size() << endl;
cout << "Number partitions of inner relation: "
<< hdr.actualMaps->innerInfo->size() << endl;
cout << "************************************************************"
<< "*************************" << endl;
#endif
#ifdef PLUGJOIN_VERY_VERBOSE_MODE
cout << "============================================================="
<< "===" << endl;
cout << "Not inserted entries in the R-Tree " << endl;
cout << "(LeafNumber NumberOfNotInsertedEntries SmiRecordIDFirst "
<< "SmiRecordIdNext)" << endl;
typedef typename PartitionInfo::const_iterator CI;
for (CI p=hdr.actualMaps->innerInfo->begin();
p!=hdr.actualMaps->innerInfo->end(); ++p)
{
cout << p->first << " " << p->second.counter << " ";
cout <<p->second.firstRId << " " << p->second.actualRId << endl;
}
cout << "Number of Leaves with insertOverflow:"
<< hdr.actualMaps->innerInfo->size() << endl;
cout << "============================================================="
<< "===" << endl << endl;
cout << "============================================================="
<< "===" << endl;
cout << "Queries which touched a leaf with insertOverflow " << endl;
cout << "(LeafNumber NumberOfQueries SmiRecordIDFirst SmiRecordIdNext)"
<< endl;
typedef typename PartitionInfo::const_iterator CI;
for (CI p=hdr.actualMaps->outerInfo->begin();
p!=hdr.actualMapsi->outerInfo->end(); ++p)
{
cout << p->first << " " << p->second.counter << " ";
cout <<p->second.firstRId << " " << p->second.actualRId << endl;
}
cout << "Number of Leaves with touching queries:"
<< hdr.actualMaps->outerInfo->size() << endl;
cout << "================================================================"
<< endl << endl;
#endif
//Caution: actualMapsIter iterates the queries
//innerPartitons without queries need not to
//be considered
hdr.actualMapsIter = hdr.actualMaps->outerInfo->end();
hdr.newPartitionsFromStack = false;
hdr.searchForTuple = true;
}
}
/*
Information for one recursive instance of Plug\&Join is iterated leaf by leaf.
For each leaf a R-Tree is built and the corresponding entries of the inner relation
are inserted in the Tree.
*/
if ( hdr.nextQueryInPartition )
{
hdr.actualMapsIter--; //the algorithm guarantees at least one partition
hdr.actualNodeNo = hdr.actualMapsIter->first;
if ( hdr.actualMapsIter == hdr.actualMaps->outerInfo->begin() )
hdr.lastPartition = true;
else
hdr.lastPartition = false;
hdr.newPartitionsFromStack = false;
hdr.nextQueryInPartition = false;
hdr.firstSearchForTuple = true;
hdr.nextTupleOuterRelation = true;
hdr.getEntryFromFirstRecord = true;
//delete the old R-Tree
delete hdr.rtree;
hdr.rtree = 0;
//number of nodes to use in next R-Tree
noEntriesOuterRelation = hdr.actualMapsIter->second.counter;
NodeInfo innerInfo = ((*hdr.actualMaps->innerInfo) [hdr.actualNodeNo]);
noEntriesInnerRelation = innerInfo.counter;
nodesToUse = UseNodesInTree (noEntriesOuterRelation,
noEntriesInnerRelation);
hdr.rtree = new R_TreePnJ<dim>( page_size, //the R-Tree
default_entries_per_node,
min_entries_per_centage,
nodesToUse );
#ifdef PLUGJOIN_VERBOSE_MODE
rtreeCounter++;
#endif
R_TreeEntryPnJ<dim>* entry;
ArrayIndex nodeNoOverflow;
hdr.innerRelPart = new Partition;
hdr.innerRelInfo = new PartitionInfo;
hdr.outerRelPart = new Partition;
hdr.outerRelInfo = new PartitionInfo;
hdr.GetNextEntry_readFirst = true;
hdr.GetNextEntry_read = true;
while ( (entry = GetNextEntry ( hdr.actualMaps->innerInfo,
hdr.actualNodeNo)) != 0 )
{
if ( !hdr.rtree->Insert (*entry, nodeNoOverflow) )
{
BufferInsert (hdr.innerRelPart, hdr.innerRelInfo,
nodeNoOverflow, *entry);
}
delete entry;
}
FlushAllLeavesToBuffer (hdr.innerRelPart, hdr.innerRelInfo);
FlushBufferToFile (hdr.innerRelPart, hdr.innerRelInfo);
}
/*
Getting the queries and build resultTuples for stream-REQUEST.
*/
if ( hdr.nextTupleOuterRelation )
{
if ( hdr.getEntryFromFirstRecord )
{
hdr.GetNextEntry_readFirst = true;
hdr.GetNextEntry_read = true;
hdr.getEntryFromFirstRecord = false;
}
R_TreeEntryPnJ<dim>* outerEntry;
if ( (outerEntry =
GetNextEntry (hdr.actualMaps->outerInfo, hdr.actualNodeNo)) == 0)
{
//all queries of partition computed
hdr.searchForTuple = false;
hdr.nextQueryInPartition = true;
if ( hdr.lastPartition )
{
// initalize call for next map from Stack
hdr.newPartitionsFromStack = true;
hdr.nextQueryInPartition = true;
hdr.firstSearchForTuple = true;
hdr.nextTupleOuterRelation = true;
hdr.getEntryFromFirstRecord = true;
}
if ( ! hdr.outerRelInfo->empty() )
{
FlushBufferToFile ( hdr.outerRelPart, hdr.outerRelInfo);
PartitionsInfo newPartitionsInfo;
newPartitionsInfo.innerInfo = hdr.innerRelInfo;
newPartitionsInfo.outerInfo = hdr.outerRelInfo;
Parts.push( newPartitionsInfo);
hdr.innerRelInfo = 0;
hdr.outerRelInfo = 0;
delete hdr.outerRelPart;
hdr.outerRelPart = 0;
delete hdr.innerRelPart;
hdr.innerRelPart = 0;
}
else
{
delete hdr.innerRelInfo;
hdr.innerRelInfo = 0;
delete hdr.outerRelInfo;
hdr.outerRelInfo = 0;
delete hdr.outerRelPart;
hdr.outerRelPart = 0;
delete hdr.innerRelPart;
hdr.innerRelPart = 0;
}
}
else
{
hdr.outerEntry = *outerEntry;
delete outerEntry;
hdr.searchForTuple = true;
}
};
if ( hdr.searchForTuple )
{
if ( hdr.firstSearchForTuple )
{
if ( hdr.rtree->First (hdr.outerEntry.box,
hdr.foundEntry,
leavesOverflowed) )
{
hdr.firstSearchForTuple = false;
hdr.nextTupleOuterRelation = false;
nextResultTupleFound = true;
FlushLeavesOverflowed (leavesOverflowed, hdr.outerEntry,
hdr.outerRelPart, hdr.outerRelInfo );
return newResultTupleFromEntries (hdr.outerEntry, hdr.foundEntry);
break;
}
else
{
hdr.firstSearchForTuple = true;
hdr.nextTupleOuterRelation = true;
nextResultTupleFound = false;
FlushLeavesOverflowed (leavesOverflowed, hdr.outerEntry,
hdr.outerRelPart, hdr.outerRelInfo );
}
}
else // if ( firstSearchForTuple)
{
if ( hdr.rtree->Next (hdr.foundEntry, leavesOverflowed) )
{
hdr.firstSearchForTuple = false;
hdr.nextTupleOuterRelation = false;
nextResultTupleFound = true;
FlushLeavesOverflowed (leavesOverflowed, hdr.outerEntry,
hdr.outerRelPart, hdr.outerRelInfo);
return newResultTupleFromEntries (hdr.outerEntry, hdr.foundEntry);
break;
}
else
{
hdr.firstSearchForTuple = true;
hdr.nextTupleOuterRelation = true;
nextResultTupleFound = false;
FlushLeavesOverflowed (leavesOverflowed, hdr.outerEntry,
hdr.outerRelPart, hdr.outerRelInfo);
}
} // if ( firstSearchForTuple )
} // if ( nextTupleOuterRelation )
}// while (!nextResultTupleFound )
assert( !nextResultTupleFound );
return 0;
};
/*
3.5.3 Method BufferInsert
*/
template <unsigned dim>
void SpatialJoinLocalInfo<dim>::BufferInsert
(multimap< ArrayIndex,R_TreeEntryPnJ<dim> >* mm,
map< ArrayIndex,NodeInfo >* m,
ArrayIndex& nodeNo, R_TreeEntryPnJ<dim>& entry)
{
mm->insert(make_pair(nodeNo, entry));
typename map< ArrayIndex,NodeInfo >::iterator p;
p = m->find(nodeNo);
if ( p != m->end() ) //nodeNo found in map
{
p->second.counter = p->second.counter + 1;
if ( (p->second.counter % hdr.maxEntriesInRecord) == 0)
{
SmiRecordId nextRecno;
SmiRecord* nextRecord = new SmiRecord();
unsigned int RecordAppended =
hdr.file.AppendRecord( nextRecno, *nextRecord );
assert( RecordAppended );
Write ( mm, nodeNo, p->second.actualRId, nextRecno);
delete nextRecord;
p->second.actualRId = nextRecno;
}
}
else //nodeNo not found in map;create FirstRecord for leave
{
SmiRecordId firstRecno;
SmiRecord* firstRecord = new SmiRecord();
unsigned int RecordAppended =
hdr.file.AppendRecord( firstRecno, *firstRecord );
assert( RecordAppended );
delete firstRecord;
m->insert ( make_pair (nodeNo, NodeInfo(1, firstRecno, firstRecno) ) );
};
};
/*
3.5.3 Method FlushAllLeavesToBuffer
*/
template <unsigned dim>
void
SpatialJoinLocalInfo<dim>::FlushAllLeavesToBuffer(Partition* innerRelPart,
PartitionInfo* innerRelInfo)
{
typedef typename PartitionInfo::const_iterator CI;
ArrayIndex nodeNo;
for (CI p=(innerRelInfo)->begin(); p!=(innerRelInfo)->end(); ++p)
{
R_TreeNodePnJ<dim>* leave = hdr.rtree->FlushLeave(p->first);
for(int entryCount = 0; entryCount <= leave->EntryCount() - 1; entryCount++)
{
R_TreeEntryPnJ<dim> e ((*leave)[entryCount]);
nodeNo = p->first;
BufferInsert ( innerRelPart, innerRelInfo, nodeNo, e);
}
delete leave;
}
};
/*
3.5.3 Method FlushBufferToFile
*/
template <unsigned dim>
void SpatialJoinLocalInfo<dim>::FlushBufferToFile
(Partition* mm,
PartitionInfo* m)
{
typedef typename PartitionInfo::iterator CI;
for (CI p=m->begin(); p!=m->end(); ++p)
{
ArrayIndex i = p->first;
Write (mm, i, p->second.actualRId, p->second.actualRId);
}
};
/*
3.5.3 Method FlushLeavesOverflowed
*/
template <unsigned dim>
void SpatialJoinLocalInfo<dim>::FlushLeavesOverflowed
(vector <ArrayIndex>& leavesOverflowed,
R_TreeEntryPnJ<dim>& entry,
Partition* outerRelPart,
PartitionInfo* outerRelInfo)
{
for (int i = 0; i <= ((int)leavesOverflowed.size() - 1); i++)
{
BufferInsert ( outerRelPart, outerRelInfo, leavesOverflowed[i], entry);
}
leavesOverflowed.clear();
};
/*
3.5.3 Method Write
*/
template <unsigned dim>
void SpatialJoinLocalInfo<dim>::Write
(Partition* mm,
ArrayIndex& nodeNo,
SmiRecordId& recNo,
SmiRecordId& nextRecordId)
{
int ctr = mm->count(nodeNo);
int offset = 0;
int sizeOfRecord = hdr.recordHeaderSize + ctr * sizeof(R_TreeEntryPnJ<dim>);
char buffer[sizeOfRecord + 1]; // reserving space for buffer
memset ( buffer, 0, sizeOfRecord + 1); //initializes the buffer with 0
//writes record header
memcpy ( buffer + offset, &nodeNo, sizeof(nodeNo) );
offset += sizeof(nodeNo);
memcpy ( buffer + offset, &ctr, sizeof(ctr) );
offset += sizeof(ctr);
memcpy ( buffer + offset, &nextRecordId, sizeof(nextRecordId) );
offset += sizeof(nextRecordId);
//collecting all entries for node in buffer
typedef typename Partition::iterator MI;
pair <MI, MI> g = mm->equal_range (nodeNo);
for (MI p = g.first; p != g.second; ++p)
{
memcpy ( buffer + offset,(void*)&(p->second), sizeof(p->second) );
offset += sizeof(p->second);
}
//writing to SMI-file
SmiRecord* record = new SmiRecord();
bool t1 = hdr.file.SelectRecord ( recNo, *record, SmiFile::Update);
assert ( t1 );
t1 = record->Write( buffer, sizeOfRecord, 0);
assert ( t1 );
delete record;
//erase entries for node ~nodeNo~
mm->erase (g.first, g.second);
};
/*
3.5.3 Method ReadFirstEntries
*/
template <unsigned dim>
void SpatialJoinLocalInfo<dim>::ReadFirstEntries
(PartitionInfo* m,
ArrayIndex& actualNodeNo,
SmiRecordId& nextRecordId,
vector < R_TreeEntryPnJ<dim> >& entriesOfRecord )
{
//getting number of first record
typename PartitionInfo::const_iterator inner_iter;
inner_iter = m->find(actualNodeNo);
assert ( inner_iter != m->end() );
SmiRecordId firstRecordId = inner_iter->second.firstRId;
//interpreting the record
int sizeOfRecord = hdr.recordHeaderSize +
hdr.maxEntriesInRecord *
sizeof(R_TreeEntryPnJ<dim>);
char buffer[sizeOfRecord + 1];
memset ( buffer, 0, sizeOfRecord + 1);
int offset = 0;
ArrayIndex nodeNoInRecord;
int numberOfEntries;
//reading first record
SmiRecord* record = new SmiRecord();
bool t1 = hdr.file.SelectRecord (firstRecordId, *record);
assert ( t1 );
t1 = record->Read(buffer, sizeOfRecord, 0);
assert ( t1 );
delete record;
//reads nodeNo, number of entries and nextRecordId
memcpy (&nodeNoInRecord, buffer + offset, sizeof (nodeNoInRecord));
offset += sizeof (nodeNoInRecord);
memcpy (&numberOfEntries, buffer + offset, sizeof (numberOfEntries));
offset += sizeof (numberOfEntries);
memcpy (&nextRecordId, buffer + offset, sizeof (nextRecordId));
offset += sizeof (nextRecordId);
assert ( nodeNoInRecord == actualNodeNo );
//now read the entries and store in entriesOfRecord
for (int i = 1; i <= numberOfEntries; i++)
{
R_TreeEntryPnJ<dim> *e =
new ((void *)(buffer + offset)) R_TreeEntryPnJ<dim>;
entriesOfRecord.push_back(*e);
offset += sizeof (R_TreeEntryPnJ<dim>);
};
//no further nextRecord to read
if (nextRecordId == firstRecordId)
nextRecordId = 0;
};
/*
3.5.3 Method ReadNextEntries
*/
template <unsigned dim>
void SpatialJoinLocalInfo<dim>::ReadNextEntries ( SmiRecordId& nextRecordId,
vector < R_TreeEntryPnJ<dim> >&entriesOfRecord )
{
//interpreting the record
int sizeOfRecord =
hdr.recordHeaderSize + hdr.maxEntriesInRecord * sizeof(R_TreeEntryPnJ<dim>);
char buffer[sizeOfRecord + 1];
memset ( buffer, 0, sizeOfRecord + 1);
int offset = 0;
ArrayIndex nodeNoInRecord;
int numberOfEntries;
SmiRecordId oldNextRecordId = nextRecordId;
//reading first record
SmiRecord* record = new SmiRecord();
bool t1 = hdr.file.SelectRecord (oldNextRecordId, *record);
assert ( t1 );
t1 = record->Read(buffer, sizeOfRecord, 0);
assert ( t1 );
delete record;
//reads nodeNo, number of entries and nextRecordId
memcpy (&nodeNoInRecord, buffer + offset, sizeof (nodeNoInRecord));
offset += sizeof (nodeNoInRecord);
memcpy (&numberOfEntries, buffer + offset, sizeof (numberOfEntries));
offset += sizeof (numberOfEntries);
memcpy (&nextRecordId, buffer + offset, sizeof (nextRecordId));
offset += sizeof (nextRecordId);
//now read the entries and store in entriesOfRecord
for (int i = 1; i <= numberOfEntries; i++)
{
R_TreeEntryPnJ<dim> *e =
new ((void *)(buffer + offset)) R_TreeEntryPnJ<dim>;
entriesOfRecord.push_back(*e);
offset += sizeof (R_TreeEntryPnJ<dim>);
};
//no further nextRecord to read
if (nextRecordId == oldNextRecordId)
nextRecordId = 0;
};
/*
3.5.3 Method GetNextEntry
*/
template <unsigned dim>
R_TreeEntryPnJ<dim>* SpatialJoinLocalInfo<dim>::GetNextEntry
(PartitionInfo* m, ArrayIndex& actualNodeNo)
{
static SmiRecordId NextRecordId;
static int counter;
static vector < R_TreeEntryPnJ<dim> > entriesOfRecord;
if ( hdr.GetNextEntry_read && hdr.GetNextEntry_readFirst )
{
entriesOfRecord.clear();
ReadFirstEntries (m, actualNodeNo, NextRecordId, entriesOfRecord);
hdr.GetNextEntry_readFirst = false;
counter = entriesOfRecord.size() - 1;
if (NextRecordId == 0)
{ hdr.GetNextEntry_read = false; }
else
{ hdr.GetNextEntry_read = true; }
}
if ( counter >= 0 )
{
R_TreeEntryPnJ<dim>* entry =
new R_TreeEntryPnJ<dim> (entriesOfRecord[counter]);
counter--;
return entry;
}
else
{
if ( hdr.GetNextEntry_read )
{
entriesOfRecord.clear();
ReadNextEntries (NextRecordId, entriesOfRecord);
if ( NextRecordId == 0)
{ hdr.GetNextEntry_read = false; }
counter = entriesOfRecord.size() - 1;
if ( counter >= 0 )
{
R_TreeEntryPnJ<dim>* entry =
new R_TreeEntryPnJ<dim> (entriesOfRecord[counter]);
counter--;
return entry;
}
else
{ // could be the last read record has maxEntriesInRecord entries
// but the actual record is empty
return 0;
}
}
else
{ // no further entry in non-empty record
return 0;
}
}
}
/*
3.5.3 Method StreamBuffer
*/
template <unsigned dim>
TupleBuffer* SpatialJoinLocalInfo<dim>::StreamBuffer (const Word stream)
{
Word streamTupleWord;
TupleBuffer *streamBuffer;
//OPEN the stream
qp->Open (stream.addr);
//REQUQEST the stream
qp->Request (stream.addr, streamTupleWord);
if ( qp->Received ( stream.addr) )
{
streamBuffer = new TupleBuffer (); //StandardSize is 32 MB
}
else
{
streamBuffer = 0;
}
while (qp->Received (stream.addr) )
{
Tuple *t = (Tuple*)streamTupleWord.addr;
streamBuffer->AppendTuple ( t );
t->DeleteIfAllowed();
qp->Request ( stream.addr, streamTupleWord);
}
//CLOSE the stream
qp->Close (stream.addr);
return streamBuffer;
}
/*
3.5.3 Method newResultTupleFromEntries
*/
template <unsigned dim>
Tuple* SpatialJoinLocalInfo<dim>::newResultTupleFromEntries (
R_TreeEntryPnJ<dim>& outerEntry,
R_TreeEntryPnJ<dim>& foundEntry)
{
Tuple* innerTuple =
((TupleBuffer*)hdr.innerRelation)->GetTuple(foundEntry.pointer, false);
Tuple* outerTuple =
((TupleBuffer*)hdr.outerRelation)->GetTuple(outerEntry.pointer, false);
Tuple* resultTuple = new Tuple (resultTupleType);
Concat (outerTuple, innerTuple, resultTuple);
innerTuple->DeleteIfAllowed();
outerTuple->DeleteIfAllowed();
return resultTuple;
}
/*
3.5.3 Method UseNodesInTree
*/
template <unsigned dim>
int SpatialJoinLocalInfo<dim>::UseNodesInTree (const int noTuplesR,
const int noTuplesS)
{
int nodesToUse = int (scalingFactor * (noTuplesR + noTuplesS) /
(default_entries_per_node * max_leaves_of_rtree) +1.0);
/*
Only the maximum number of nodes may be used, but the minimum number of nodes
must be used.
*/
return max (min (nodesToUse, max_leaves_of_rtree), min_leaves_of_rtree);
}
/*
3.5 Value mapping function of operator ~spatialjoin~
*/
template<int D>
int
spatialjoinValueMapping(Word* args, Word& result, int message,
Word& local, Supplier s)
{
SpatialJoinLocalInfo<D> *localInfo;
switch (message)
{
case OPEN:
{
Word leftStreamWord,
rightStreamWord,
leftAttrIndexWord,
rightAttrIndexWord;
leftStreamWord = args[0];
rightStreamWord = args[1];
leftAttrIndexWord = args[4]; //APPENDED - Value no 1
rightAttrIndexWord = args[5]; //APPENDED - Value no 2
localInfo = new SpatialJoinLocalInfo<D>(rightStreamWord,
rightAttrIndexWord,
leftStreamWord,
leftAttrIndexWord);
ListExpr resultType = GetTupleResultType( s );
localInfo->resultTupleType = new TupleType( nl->Second( resultType ) );
local.setAddr(localInfo);
return 0;
}
case REQUEST:
{
localInfo = (SpatialJoinLocalInfo<D>*)local.addr;
result.setAddr(localInfo->NextResultTuple());
return result.addr != 0 ? YIELD : CANCEL;
}
case CLOSE:
{
if(local.addr)
{
localInfo = (SpatialJoinLocalInfo<D>*)local.addr;
//the TupleBuffers are deleted in the destructor of localInfo
localInfo->resultTupleType->DeleteIfAllowed();
delete localInfo;
local.setAddr(0);
}
return 0;
}
}
return 0;
};
/*
3.7 Definition of value mapping vectors
*/
ValueMapping spatialjoinMap [] = {spatialjoinValueMapping<2>,
spatialjoinValueMapping<3>,
spatialjoinValueMapping<4> };
/*
3.8 Specification of operator ~spatialjoin~
*/
const string spatialjoinSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\""
" \"Example\" )"
"( <text>( (stream (tuple ((x1 t1)...(xn tn)))) "
"(stream (tuple ((y1 yt1)...(yn ytn))))"
" rect||rect3||rect4||SpatialType rect||rect3||rect4||SpatialType) -> "
"(stream (tuple ((x1 t1)...(xn tn)((y1 yt1)...(yn ytn)))))</text--->"
"<text>outerStream innerStream spatialjoin0 "
"[outerAttr, innerAttr]</text--->"
"<text>Uses the Plug&Join-Algorithm to find all pairs of intersecting"
" tuples in the given relations. The joining tuples are reported."
"</text--->"
"<text>query trees feed streets feed spatialjoin0 [pos_trees, pos_streets]"
" consume; the joining attributes must be of the same dimension."
"</text--->"
") )";
/*
3.9 Definition of operator ~spatialjoin~
*/
Operator spatialjoin (
"spatialjoin0", // name
spatialjoinSpec, // specification
3, // number of overloaded functions
spatialjoinMap, // value mapping
spatialjoinSelection, // trivial selection function
spatialjoinTypeMap // type mapping
);
/*
4 Definition and initialization of ~PlugJoin~ algebra
*/
class PlugJoinAlgebra : public Algebra
{
public:
PlugJoinAlgebra() : Algebra()
{
AddOperator(&spatialjoin);
}
~PlugJoinAlgebra() {};
};
extern "C"
Algebra*
InitializePlugJoinAlgebra( NestedList* nlRef, QueryProcessor* qpRef )
{
nl = nlRef;
qp = qpRef;
return (new PlugJoinAlgebra());
}