/* //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 #include #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 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 class SpatialJoinLocalInfo { /* 3.5.1 Private declarations */ private: typedef multimap< ArrayIndex,R_TreeEntryPnJ > 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* 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 outerEntry; R_TreeEntryPnJ 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 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 >* mm, map< ArrayIndex,NodeInfo >* m, ArrayIndex& nodeNo, R_TreeEntryPnJ& 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 & leavesOverflowed, R_TreeEntryPnJ& 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 >& entriesOfRecord ); void ReadNextEntries ( SmiRecordId& NextRecordId, vector < R_TreeEntryPnJ >&entriesOfRecord ); R_TreeEntryPnJ* 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& outerEntry, R_TreeEntryPnJ& 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 SpatialJoinLocalInfo::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))); 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( page_size, default_entries_per_node, min_entries_per_centage, nodesToUse ); #ifdef PLUGJOIN_VERBOSE_MODE rtreeCounter = 1; #endif while ( (innerTuple = innerIter->GetNextTuple()) != 0 ) { BBox box = ((StandardSpatialAttribute*)innerTuple-> GetAttribute(innerAttrIndexPnJ))->BoundingBox(); if(box.IsDefined()){ // an undefined box cannot intersects anything R_TreeEntryPnJ 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 Tuple* SpatialJoinLocalInfo::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 SBox; //searchBox for window-query R_TreeEntryPnJ foundEntry; vector 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*)outerTuple-> GetAttribute(hdr.outerAttrIndex))->BoundingBox(); hdr.outerActualTupleId = hdr.outerIter->GetTupleId(); hdr.outerEntry = R_TreeEntryPnJ(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 Tuple* SpatialJoinLocalInfo::NextResultTupleFromStack() { vector 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 <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 <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( page_size, //the R-Tree default_entries_per_node, min_entries_per_centage, nodesToUse ); #ifdef PLUGJOIN_VERBOSE_MODE rtreeCounter++; #endif R_TreeEntryPnJ* 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* 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 void SpatialJoinLocalInfo::BufferInsert (multimap< ArrayIndex,R_TreeEntryPnJ >* mm, map< ArrayIndex,NodeInfo >* m, ArrayIndex& nodeNo, R_TreeEntryPnJ& 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 void SpatialJoinLocalInfo::FlushAllLeavesToBuffer(Partition* innerRelPart, PartitionInfo* innerRelInfo) { typedef typename PartitionInfo::const_iterator CI; ArrayIndex nodeNo; for (CI p=(innerRelInfo)->begin(); p!=(innerRelInfo)->end(); ++p) { R_TreeNodePnJ* leave = hdr.rtree->FlushLeave(p->first); for(int entryCount = 0; entryCount <= leave->EntryCount() - 1; entryCount++) { R_TreeEntryPnJ e ((*leave)[entryCount]); nodeNo = p->first; BufferInsert ( innerRelPart, innerRelInfo, nodeNo, e); } delete leave; } }; /* 3.5.3 Method FlushBufferToFile */ template void SpatialJoinLocalInfo::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 void SpatialJoinLocalInfo::FlushLeavesOverflowed (vector & leavesOverflowed, R_TreeEntryPnJ& 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 void SpatialJoinLocalInfo::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); 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 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 void SpatialJoinLocalInfo::ReadFirstEntries (PartitionInfo* m, ArrayIndex& actualNodeNo, SmiRecordId& nextRecordId, vector < R_TreeEntryPnJ >& 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); 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 *e = new ((void *)(buffer + offset)) R_TreeEntryPnJ; entriesOfRecord.push_back(*e); offset += sizeof (R_TreeEntryPnJ); }; //no further nextRecord to read if (nextRecordId == firstRecordId) nextRecordId = 0; }; /* 3.5.3 Method ReadNextEntries */ template void SpatialJoinLocalInfo::ReadNextEntries ( SmiRecordId& nextRecordId, vector < R_TreeEntryPnJ >&entriesOfRecord ) { //interpreting the record int sizeOfRecord = hdr.recordHeaderSize + hdr.maxEntriesInRecord * sizeof(R_TreeEntryPnJ); 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 *e = new ((void *)(buffer + offset)) R_TreeEntryPnJ; entriesOfRecord.push_back(*e); offset += sizeof (R_TreeEntryPnJ); }; //no further nextRecord to read if (nextRecordId == oldNextRecordId) nextRecordId = 0; }; /* 3.5.3 Method GetNextEntry */ template R_TreeEntryPnJ* SpatialJoinLocalInfo::GetNextEntry (PartitionInfo* m, ArrayIndex& actualNodeNo) { static SmiRecordId NextRecordId; static int counter; static vector < R_TreeEntryPnJ > 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* entry = new R_TreeEntryPnJ (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* entry = new R_TreeEntryPnJ (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 TupleBuffer* SpatialJoinLocalInfo::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 Tuple* SpatialJoinLocalInfo::newResultTupleFromEntries ( R_TreeEntryPnJ& outerEntry, R_TreeEntryPnJ& 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 int SpatialJoinLocalInfo::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 spatialjoinValueMapping(Word* args, Word& result, int message, Word& local, Supplier s) { SpatialJoinLocalInfo *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(rightStreamWord, rightAttrIndexWord, leftStreamWord, leftAttrIndexWord); ListExpr resultType = GetTupleResultType( s ); localInfo->resultTupleType = new TupleType( nl->Second( resultType ) ); local.setAddr(localInfo); return 0; } case REQUEST: { localInfo = (SpatialJoinLocalInfo*)local.addr; result.setAddr(localInfo->NextResultTuple()); return result.addr != 0 ? YIELD : CANCEL; } case CLOSE: { if(local.addr) { localInfo = (SpatialJoinLocalInfo*)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\" )" "( ( (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)))))" "outerStream innerStream spatialjoin0 " "[outerAttr, innerAttr]" "Uses the Plug&Join-Algorithm to find all pairs of intersecting" " tuples in the given relations. The joining tuples are reported." "" "query trees feed streets feed spatialjoin0 [pos_trees, pos_streets]" " consume; the joining attributes must be of the same dimension." "" ") )"; /* 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()); }