/* ---- This file is part of SECONDO. Copyright (C) 2004, University in Hagen, Department of Computer Science, Database Systems for New Applications. SECONDO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SECONDO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- //paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}] //paragraph [10] Footnote: [{\footnote{] [}}] //[TOC] [\tableofcontents] [1] Implementation of Module Relation Algebra Main Memory [1] Using Storage Manager Berkeley DB June 1996 Claudia Freundorfer May 2002 Frank Hoffmann port to C++ November 7, 2002 RHG Corrected the type mapping of ~tcount~. November 30, 2002 RHG Introduced a function ~RelPersistValue~ instead of ~DefaultPersistValue~ which keeps relations that have been built in memory in a small cache, so that they need not be rebuilt from then on. 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. [TOC] 2 Auxilary Functions 1.2 Function ~TypeOfRelAlgSymbol~ Transforms a list expression ~symbol~ into one of the values of type ~RelationType~. ~Symbol~ is allowed to be any list. If it is not one of these symbols, then the value ~error~ is returned. */ #include #include #include "OldRelationAlgebra.h" #include "Algebras/Relation-C++/RelationAlgebra.h" #include "CPUTimeMeasurer.h" #include "QueryProcessor.h" #include "LogMsg.h" #include "ListUtils.h" #include "Symbols.h" extern NestedList* nl; extern QueryProcessor* qp; extern int ccTuplesCreated; extern int ccTuplesDeleted; extern TypeConstructor ccreltuple; extern TypeConstructor ccrelprel; // type mapping functions implemented in ExtRelationAlgebra.cpp extern ListExpr GroupByTypeMap2(ListExpr args, const bool memoryImpl = false); using namespace std; namespace oldrelation{ static CcRelationType CcTypeOfRelAlgSymbol (ListExpr symbol) { string s; if (nl->AtomType(symbol) == SymbolType) { s = nl->SymbolValue(symbol); if (s == CcRel::BasicType() ) return mrel; if (s == CcTuple::BasicType() ) return mtuple; if (s == Symbol::STREAM() ) return mstream; if (s == Symbol::MAP() ) return mmap; if (s == CcBool::BasicType() ) return mbool; } return merror; } /* 5.6 Function ~FindAttribute~ Here ~list~ should be a list of pairs of the form (~name~,~datatype~). The function ~FindAttribute~ determines whether ~attrname~ occurs as one of the names in this list. If so, the index in the list (counting from 1) is returned and the corresponding datatype is returned in ~attrtype~. Otherwise 0 is returned. Used in operator ~attr~. */ int CcFindAttribute( ListExpr list, string attrname, ListExpr& attrtype, NestedList* nl) { ListExpr first, rest; int j; string name; if (nl->IsAtom(list)) return 0; rest = list; j = 1; while (!nl->IsEmpty(rest)) { first = nl->First(rest); rest = nl->Rest(rest); if ((nl->ListLength(first) == 2) && (nl->AtomType(nl->First(first)) == SymbolType)) { name = nl->SymbolValue(nl->First(first)); if (name == attrname) { attrtype = nl->Second(first); return j; } } else return 0; // typeerror j++; } return 0; // attrname not found } /* 5.6 Function ~ConcatLists~ Concatenates two lists. */ ListExpr CcConcatLists( ListExpr list1, ListExpr list2) { if (nl->IsEmpty(list1)) { return list2; } else { return nl->Cons(nl->First(list1), CcConcatLists(nl->Rest(list1), list2)); } } /* 5.6 Function ~IsTupleDescription~ Checks wether a ListExpression is of the form ((a1 t1) ... (ai ti)). */ bool CcIsTupleDescription(ListExpr a, NestedList* nl) { ListExpr rest = a; ListExpr current; while(!nl->IsEmpty(rest)) { current = nl->First(rest); rest = nl->Rest(rest); if((nl->ListLength(current) == 2) && (nl->IsAtom(nl->First(current))) && (nl->AtomType(nl->First(current)) == SymbolType) && (nl->IsAtom(nl->Second(current))) && (nl->AtomType(nl->Second(current)) == SymbolType)) { } else { return false; } } return true; } /* 5.6 Function ~AttributesAreDisjoint~ Checks wether two ListExpressions are of the form ((a1 t1) ... (ai ti)) and ((b1 d1) ... (bj dj)) and wether the ai and the bi are disjoint. */ bool CcAttributesAreDisjoint(ListExpr a, ListExpr b) { set aNames; ListExpr rest = a; ListExpr current; while(!nl->IsEmpty(rest)) { current = nl->First(rest); rest = nl->Rest(rest); if((nl->ListLength(current) == 2) && (nl->IsAtom(nl->First(current))) && (nl->AtomType(nl->First(current)) == SymbolType) && (nl->IsAtom(nl->Second(current))) && (nl->AtomType(nl->Second(current)) == SymbolType)) { aNames.insert(nl->SymbolValue(nl->First(current))); } else { return false; } } rest = b; while(!nl->IsEmpty(rest)) { ListExpr current = nl->First(rest); rest = nl->Rest(rest); if((nl->ListLength(current) == 2) && (nl->IsAtom(nl->First(current))) && (nl->AtomType(nl->First(current)) == SymbolType) && (nl->IsAtom(nl->Second(current))) && (nl->AtomType(nl->Second(current)) == SymbolType)) { if(aNames.find(nl->SymbolValue(nl->First(current))) != aNames.end()) { return false; } } else { return false; } } return true; } /* 4 Operators 4.2 Selection function for type operators The selection function of a type operator always returns -1. */ static int CcTypeOperatorSelect(ListExpr args) { return -1; } /* 6.1 Type Operator ~TUPLE~ Type operators are used only for inferring argument types of parameter functions. They have a type mapping but no evaluation function. 6.1.1 Type mapping function of operator ~TUPLE~ Extract tuple type from a stream or relation type given as the first argument. ---- ((stream x) ...) -> x ((mrel x) ...) -> x ---- */ ListExpr CcTUPLETypeMap(ListExpr args) { ListExpr first; if(nl->ListLength(args) >= 1) { first = nl->First(args); if(nl->ListLength(first) == 2 ) { if ((CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) || (CcTypeOfRelAlgSymbol(nl->First(first)) == mrel)) return nl->Second(first); } } return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.3 Specification of operator ~TUPLE~ */ const string CcTUPLESpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Remarks\" ) " "( ((stream x)...) -> x, ((mrel x)...) -> " "x" "type operator" "Extract tuple type from a stream or " "relation type given as the first argument." "" "not for use with sos-syntax" " ) )"; /* 4.1.3 Definition of operator ~TUPLE~ */ Operator ccrelTUPLE ( "TUPLE", // name CcTUPLESpec, // specification 0, // no value mapping CcTypeOperatorSelect, // trivial selection function CcTUPLETypeMap // type mapping ); /* 6.1 Type Operator ~TUPLE2~ 6.1.1 Type mapping function of operator ~TUPLE2~ Extract tuple type from a stream or relation type given as the second argument. ---- ((stream x) (stream y) ...) -> y ((mrel x) (mrel y) ...) -> y ---- */ ListExpr CcTUPLE2TypeMap(ListExpr args) { ListExpr second; if(nl->ListLength(args) >= 2) { second = nl->Second(args); if(nl->ListLength(second) == 2 ) { if ((CcTypeOfRelAlgSymbol(nl->First(second)) == mstream) || (CcTypeOfRelAlgSymbol(nl->First(second)) == mrel)) return nl->Second(second); } } return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.3 Specification of operator ~TUPLE2~ */ const string CcTUPLE2Spec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Remarks\" ) " "( ((stream x) (stream y) ...) -> y, " "((mrel x) (mrel y) ...) -> y" "type operator" "Extract tuple type from a stream or " "relation" " type given as the second argument." "not for use with sos-syntax" ") )"; /* 4.1.3 Definition of operator ~TUPLE2~ */ Operator ccrelTUPLE2 ( "TUPLE2", // name CcTUPLE2Spec, // specification 0, // no value mapping CcTypeOperatorSelect, // trivial selection function CcTUPLE2TypeMap // type mapping ); /* 6.1 Type Operator ~Group~ Type operators are used only for inferring argument types of parameter functions. They have a type mapping but no evaluation function. 6.1.1 Type mapping function of operator ~group~ ---- ((stream x)) -> (mrel x) ---- */ ListExpr CcGroupTypeMap(ListExpr args) { ListExpr first; ListExpr tupleDesc; if(!nl->IsAtom(args) && nl->ListLength(args) >= 1) { first = nl->First(args); if(!nl->IsAtom(first) && nl->ListLength(first) == 2 ) { tupleDesc = nl->Second(first); if(CcTypeOfRelAlgSymbol(nl->First(first)) == mstream && (!nl->IsAtom(tupleDesc)) && (nl->ListLength(tupleDesc) == 2) && CcTypeOfRelAlgSymbol(nl->First(tupleDesc)) == mtuple && CcIsTupleDescription(nl->Second(tupleDesc), nl)) return nl->TwoElemList( nl->SymbolAtom(CcRel::BasicType()), tupleDesc); } } return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.3 Specification of operator ~Group~ */ const string CcGroupSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Remarks\" ) " "( ((stream x)) -> (mrel x)" "type operator" "Maps stream type to a relation." "not for use with sos-syntax" ") )"; /* 4.1.3 Definition of operator ~group~ */ Operator ccrelgroup ( "GROUP", // name CcGroupSpec, // specification 0, // no value mapping CcTypeOperatorSelect, // trivial selection function CcGroupTypeMap // type mapping ); /* 4.1 Operator ~feed~ Produces a stream from a relation by scanning the relation tuple by tuple. 4.1.1 Type mapping function of operator ~feed~ A type mapping function takes a nested list as argument. Its contents are type descriptions of an operator's input parameters. A nested list describing the output type of the operator is returned. Result type of feed operation. ---- ((mrel x)) -> (stream x) ---- */ static ListExpr CcFeedTypeMap(ListExpr args) { if(nl->ListLength(args)!=1){ return listutils::typeError("One argument expected"); } ListExpr mrel = nl->First(args); if(!listutils::isRelDescription2(mrel, CcRel::BasicType())){ return listutils::typeError("mrel(x) expected"); } return nl->Cons(nl->SymbolAtom(Symbol::STREAM()), nl->Rest(mrel)); } /* 4.1.2 Value mapping function of operator ~feed~ */ static int CcFeed(Word* args, Word& result, int message, Word& local, Supplier s) { CcRel* r; CcRelIT* rit; switch (message) { case OPEN : r = ((CcRel*)args[0].addr); rit = r->MakeNewScan(); local.addr = rit; return 0; case REQUEST : rit = (CcRelIT*)local.addr; if (!(rit->EndOfScan())) { result = SetWord(rit->GetTuple()); rit->Next(); return YIELD; } else { return CANCEL; } case CLOSE : rit = (CcRelIT*)local.addr; if(rit) { delete rit; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of operator ~feed~ */ const string CcFeedSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( (mrel x) -> (stream x)" "_ feed" "Produces a stream from a relation by " "scanning the relation tuple by tuple." "query cities feed consume" ") )"; /* 4.1.3 Definition of operator ~feed~ Non-overloaded operators are defined by constructing a new instance of class ~Operator~, passing all operator functions as constructor arguments. */ Operator ccrelfeed ( "feed", // name CcFeedSpec, // specification CcFeed, // value mapping Operator::SimpleSelect, // trivial selection function CcFeedTypeMap // type mapping ); /* 4.1 Operator ~sample~ Produces a stream representing a sample of a relation. 4.1.1 Function ~MakeRandomSubset~ Generates a random subset of the numbers 1 ... ~setSize~, the size of which is ~subsetSize~. This function is needed for operator ~sample~. The strategy for generating a random subset works as follows: The algorithm maintains a set of already drawn numbers. The algorithm draws a new random number (using the libc random number generator) and adds it to the set of already drawn numbers, if it has not been already drawn. This is repeated until the size of the drawn set equals ~subsetSize~. If ~subsetSize~ it not considerably smaller than ~setSize~, e.g. ~subsetSize~ = ~setSize~ - 1, this approach becomes very inefficient or may even not terminate, because towards the end of the algorithm it may take a very long (or infinitely long) time until the random number generator hits one of the few numbers, which have not been already drawn. Therefore, if ~subsetSize~ is more than half of ~subSet~, we simple draw a subset of size ~setSize~ - ~subsetSize~ and take the complement of that set as result set. */ void CcMakeRandomSubset(vector& result, int subsetSize, int setSize) { assert(subsetSize >= 1); assert(setSize >= 2); assert(setSize <= RAND_MAX); assert(setSize > subsetSize); set drawnNumbers; set::iterator iter; int drawSize; int nDrawn = 0; int i; int r; bool doInvert; result.resize(0); srand(time(0)); if(((double)setSize) / ((double)subsetSize) <= 2) { doInvert = true; drawSize = setSize - subsetSize; } else { doInvert = false; drawSize = subsetSize; } while(nDrawn < drawSize) { r = rand(); r = r % (setSize + 1); if(r == 0) { continue; } if(drawnNumbers.find(r) == drawnNumbers.end()) { drawnNumbers.insert(r); ++nDrawn; } } if(doInvert) { for(i = 1; i <= setSize; ++i) { if(drawnNumbers.find(i) == drawnNumbers.end()) { result.push_back(i); } } } else { for(iter = drawnNumbers.begin(); iter != drawnNumbers.end(); ++iter) { result.push_back(*iter); } } } /* 4.1.1 Type mapping function of operator ~sample~ A type mapping function takes a nested list as argument. Its contents are type descriptions of an operator's input parameters. A nested list describing the output type of the operator is returned. Result type of feed operation. ---- ((mrel x) int real) -> (stream x) ---- */ static ListExpr CcSampleTypeMap(ListExpr args) { if(nl->ListLength(args)!=3){ return listutils::typeError("three arguments expected"); } ListExpr stream = nl->First(args) ; ListExpr minSampleSizeLE = nl->Second(args); ListExpr minSampleRateLE = nl->Third(args); if(!listutils::isRelDescription2(stream,CcRel::BasicType()) || !listutils::isSymbol(minSampleSizeLE,CcInt::BasicType()) || !listutils::isSymbol(minSampleRateLE,CcReal::BasicType())){ return listutils::typeError("mrel x int x real expected"); } return nl->Cons(nl->SymbolAtom(Symbol::STREAM()), nl->Rest(stream)); } /* 4.1.2 Value mapping function of operator ~sample~ */ struct CcSampleLocalInfo { vector sampleIndices; vector::iterator iter; int lastIndex; CcRelIT* relIT; }; static int CcSample(Word* args, Word& result, int message, Word& local, Supplier s) { CcSampleLocalInfo* localInfo; CcRel* rel; CcTuple* tuple; int sampleSize; int relSize; float sampleRate; int i; int currentIndex; switch(message) { case OPEN : localInfo = new CcSampleLocalInfo(); local = SetWord(localInfo); rel = (CcRel*)args[0].addr; relSize = rel->GetNoTuples(); localInfo->relIT = rel->MakeNewScan(); sampleSize = ((CcInt*)args[1].addr)->GetIntval(); sampleRate = ((CcReal*)args[2].addr)->GetRealval(); if(sampleSize < 1) { sampleSize = 1; } if(sampleRate <= 0.0) { sampleRate = 0.0; } else if(sampleRate > 1.0) { sampleRate = 1.0; } if((int)(sampleRate * (float)relSize) > sampleSize) { sampleSize = (int)(sampleRate * (float)relSize); } if(relSize <= sampleSize) { for(i = 1; i <= relSize; ++i) { localInfo->sampleIndices.push_back(i); } } else { CcMakeRandomSubset(localInfo->sampleIndices, sampleSize, relSize); } localInfo->iter = localInfo->sampleIndices.begin(); localInfo->lastIndex = 0; return 0; case REQUEST: localInfo = (CcSampleLocalInfo*)local.addr; if(localInfo->iter == localInfo->sampleIndices.end()) { return CANCEL; } else { currentIndex = *(localInfo->iter); if(!localInfo->relIT->EndOfScan()) { tuple = localInfo->relIT->GetTuple(); localInfo->relIT->Next(); } else { return CANCEL; } /* Advance iterator to the the next tuple belonging to the sample */ for(i = 1; i < currentIndex - localInfo->lastIndex; ++i) { tuple->DeleteIfAllowed(); if(!localInfo->relIT->EndOfScan()) { tuple = localInfo->relIT->GetTuple(); localInfo->relIT->Next(); } else { return CANCEL; } } result = SetWord(tuple); localInfo->lastIndex = *(localInfo->iter); localInfo->iter++; return YIELD; } case CLOSE : localInfo = (CcSampleLocalInfo*)local.addr; if(localInfo) { delete localInfo->relIT; delete localInfo; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of operator ~sample~ */ const string CcSampleSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( (mrel x) int real -> (stream x)" "" "_ sample [ _ , _ ]" "Produces a random sample of a relation." " The sample size is min(relSize, " "max(s, t * relSize)), where relSize is the size" " of the argument relation, s is the second " "argument, and t the third." "query cities sample[0, 0.45] count" ") )"; /* 4.1.3 Definition of operator ~sample~ Non-overloaded operators are defined by constructing a new instance of class ~Operator~, passing all operator functions as constructor arguments. */ Operator ccrelsample ( "sample", // name CcSampleSpec, // specification CcSample, // value mapping Operator::SimpleSelect, // trivial selection function CcSampleTypeMap // type mapping ); /* 4.1 Operator ~consume~ Collects objects from a stream into a relation. 4.1.1 Type mapping function of operator ~consume~ Operator ~consume~ accepts a stream of tuples and returns a relation. ---- (stream x) -> (mrel x) ---- */ ListExpr CcConsumeTypeMap(ListExpr args) { ListExpr first ; if(nl->ListLength(args) == 1) { first = nl->First(args); if ((nl->ListLength(first) == 2) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (nl->ListLength(nl->Second(first)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple)) return nl->Cons(nl->SymbolAtom(CcRel::BasicType()), nl->Rest(first)); } ErrorReporter::ReportError("Incorrect input for operator consume."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~consume~ */ static int CcConsume(Word* args, Word& result, int message, Word& local, Supplier s) { Word actual; CcRel* rel; rel = (CcRel*)((qp->ResultStorage(s)).addr); if( rel->GetNoTuples() > 0 ) rel->Empty(); qp->Open(args[0].addr); qp->Request(args[0].addr, actual); while (qp->Received(args[0].addr)) { CcTuple* tuple = (CcTuple*)actual.addr; tuple = tuple->CloneIfNecessary(); tuple->SetFree(false); rel->AppendTuple(tuple); qp->Request(args[0].addr, actual); } result = SetWord((void*) rel); qp->Close(args[0].addr); return 0; } /* 4.1.3 Specification of operator ~consume~ */ const string CcConsumeSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( (stream x) -> (mrel x)" "_ consume" "Collects objects from a stream." "" "query cities feed consume" ") )"; /* 4.1.3 Definition of operator ~consume~ */ Operator ccrelconsume ( "consume", // name CcConsumeSpec, // specification CcConsume, // value mapping Operator::SimpleSelect, // trivial selection function CcConsumeTypeMap // type mapping ); /* 7.1 Operator ~attr~ 7.1.1 Type mapping function of operator ~attr~ Result type attr operation. ---- ((mtuple ((x1 t1)...(xn tn))) xi) -> ti APPEND (i) ti) ---- This type mapping uses a special feature of the query processor, in that if requests to append a further argument to the given list of arguments, namely, the index of the attribute within the tuple. This indes is computed within the type mapping function. The request is given through the result expression of the type mapping which has the form, for example, ---- (APPEND (1) string) ---- The keyword ~APPEND~ occuring as a first element of a returned type expression tells the query processor to add the elements of the following list - the second element of the type expression - as further arguments to the operator (as if they had been written in the query). The third element of the query is then used as the real result type. In this case 1 is the index of the attribute determined in this procedure. The query processor, more precisely the procedure ~anotate~ there, will produce the annotation for the constant 1, append it to the list of annotated arguments, and then use CcString::BasicType() as the result type of the ~attr~ operation. */ ListExpr CcAttrTypeMap(ListExpr args) { ListExpr first, second, attrtype; string attrname; int j; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mtuple) && (nl->IsAtom(second)) && (nl->AtomType(second) == SymbolType)) { attrname = nl->SymbolValue(second); j = CcFindAttribute(nl->Second(first), attrname, attrtype, nl); if (j) return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()), nl->OneElemList(nl->IntAtom(j)), attrtype); } } ErrorReporter::ReportError("Incorrect input for operator attr."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~attr~ The argument vector ~arg~ contains in the first slot ~args[0]~ the tuple and in ~args[2]~ the position of the attribute as a number. Returns as ~result~ the value of an attribute at the given position ~args[2]~ in a tuple object. The attribute name is argument 2 in the query and is used in the function ~AttributeTypeMap~ to determine the attribute number ~args[2]~ . */ static int CcAttr(Word* args, Word& result, int message, Word& local, Supplier s) { CcTuple* tupleptr; int index; tupleptr = (CcTuple*)args[0].addr; index = ((CcInt*)args[2].addr)->GetIntval(); assert( 1 <= index && index <= tupleptr->GetNoAttrs() ); result = SetWord(tupleptr->Get(index - 1)); return 0; } /* 4.1.3 Specification of operator ~attr~ */ const string CcAttrSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Remarks\" ) " "( ((tuple ((x1 t1)...(xn tn))) xi) -> " "ti)" "attr ( _ , _ )" "Returns the value of an attribute at a " "given position." "not for use with sos-syntax" ") )"; /* 4.1.3 Definition of operator ~attr~ */ Operator ccrelattr ( "attr", // name CcAttrSpec, // specification CcAttr, // value mapping Operator::SimpleSelect, // trivial selection function CcAttrTypeMap // type mapping ); /* 7.3 Operator ~filter~ Only tuples, fulfilling a certain condition are passed on to the output stream. 7.3.1 Type mapping function of operator ~filter~ Result type of filter operation. ---- ((stream (mtuple x)) (map (mtuple x) bool)) -> (stream (mtuple x)) ---- */ template ListExpr CcFilterTypeMap(ListExpr args) { ListExpr first, second; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if ( (nl->ListLength(first) == 2) && (nl->ListLength(second) == 3) && (nl->ListLength(nl->Second(first)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(second)) == mmap) && (CcTypeOfRelAlgSymbol(nl->Third(second)) == mbool) && (nl->Equal(nl->Second(first),nl->Second(second)))) return first; } ErrorReporter::ReportError( isFilter ? "Incorrect input for operator filter." : "Incorrect input for operator cancel."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~filter~ */ static int CcFilter(Word* args, Word& result, int message, Word& local, Supplier s) { Word elem = SetWord(Address(0)); Word funresult = SetWord(Address(0)); bool found = false; CcTuple* tuple = 0; ArgVectorPointer funargs; switch ( message ) { case OPEN: //cout << "tfilter OPEN " << endl; qp->Open (args[0].addr); return 0; case REQUEST: //cout << "tfilter REQUEST " << endl; funargs = qp->Argument(args[1].addr); qp->Request(args[0].addr, elem); found = false; while (qp->Received(args[0].addr) && !found) { tuple = (CcTuple*)elem.addr; (*funargs)[0] = elem; qp->Request(args[1].addr, funresult); if (((Attribute*)funresult.addr)->IsDefined()) { found = ((CcBool*)funresult.addr)->GetBoolval(); } if (!found) { tuple->DeleteIfAllowed(); qp->Request(args[0].addr, elem); } } if (found) { result = SetWord(tuple); return YIELD; } else return CANCEL; case CLOSE: //cout << "tfilter CLOSE " << endl; qp->Close(args[0].addr); return 0; } return 0; } /* 4.1.3 Specification of operator ~filter~ */ const string CcFilterSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream x) (map x bool)) -> " "(stream x)" "_ filter [ fun ]" "Only tuples, fulfilling a certain " "condition are passed on to the output " "stream." "query cities feed filter " "[.population > 500000] consume" ") )"; /* 4.1.3 Definition of operator ~filter~ */ Operator ccrelfilter ( "filter", // name CcFilterSpec, // specification CcFilter, // value mapping Operator::SimpleSelect, // trivial selection function CcFilterTypeMap // type mapping ); /* 7.3 Operator ~project~ 7.3.1 Type mapping function of operator ~filter~ Result type of project operation. ---- ((stream (mtuple ((x1 T1) ... (xn Tn)))) (ai1 ... aik)) -> (APPEND (k (i1 ... ik)) (stream (mtuple ((ai1 Ti1) ... (aik Tik)))) ) ---- The type mapping computes the number of attributes and the list of attribute numbers for the given projection attributes and asks the query processor to append it to the given arguments. */ ListExpr CcProjectTypeMap(ListExpr args) { bool firstcall = false; int noAttrs = 0, j = 0; ListExpr first, second, first2; ListExpr attrtype, newAttrList, lastNewAttrList; ListExpr lastNumberList, numberList, outlist; first = second = first2 = nl->TheEmptyList(); attrtype = newAttrList = lastNewAttrList = nl->TheEmptyList(); lastNumberList = numberList = outlist = nl->TheEmptyList(); string attrname = ""; firstcall = true; if (nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if ((nl->ListLength(first) == 2) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (nl->ListLength(nl->Second(first)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && (!nl->IsAtom(second)) && (nl->ListLength(second) > 0)) { noAttrs = nl->ListLength(second); while (!(nl->IsEmpty(second))) { first2 = nl->First(second); second = nl->Rest(second); if (nl->AtomType(first2) == SymbolType) { attrname = nl->SymbolValue(first2); } else { ErrorReporter::ReportError("Incorrect input for operator project."); return nl->SymbolAtom(Symbol::TYPEERROR()); } j = CcFindAttribute(nl->Second(nl->Second(first)), attrname, attrtype, nl); if (j) { if (firstcall) { firstcall = false; newAttrList = nl->OneElemList(nl->TwoElemList(first2, attrtype)); lastNewAttrList = newAttrList; numberList = nl->OneElemList(nl->IntAtom(j)); lastNumberList = numberList; } else { lastNewAttrList = nl->Append(lastNewAttrList, nl->TwoElemList(first2, attrtype)); lastNumberList = nl->Append(lastNumberList, nl->IntAtom(j)); } } else { ErrorReporter::ReportError("Incorrect input for operator project."); return nl->SymbolAtom(Symbol::TYPEERROR()); } } // Check whether all new attribute names are distinct // - not yet implemented outlist = nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()), nl->TwoElemList(nl->IntAtom(noAttrs), numberList), nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList( nl->SymbolAtom(CcTuple::BasicType()), newAttrList))); // cout << nl->WriteToFile("/dev/tty",outlist) << endl; return outlist; } } ErrorReporter::ReportError("Incorrect input for operator project."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~project~ */ static int CcProject(Word* args, Word& result, int message, Word& local, Supplier s) { Word elem1, elem2; int noOfAttrs, index; Supplier son; Attribute* attr; CcTuple* t; switch (message) { case OPEN : //cout << "project OPEN" << endl; qp->Open(args[0].addr); return 0; case REQUEST : //cout << "project REQUEST" << endl; qp->Request(args[0].addr, elem1); if (qp->Received(args[0].addr)) { t = new CcTuple(); t->SetFree(true); noOfAttrs = ((CcInt*)args[2].addr)->GetIntval(); t->SetNoAttrs(noOfAttrs); for (int i=1; i <= noOfAttrs; i++) { son = qp->GetSupplier(args[3].addr, i-1); qp->Request(son, elem2); index = ((CcInt*)elem2.addr)->GetIntval(); attr = ((CcTuple*)elem1.addr)->Get(index-1); t->Put(i-1, ((Attribute*)attr->Clone())); } ((CcTuple*)elem1.addr)->DeleteIfAllowed(); result = SetWord(t); return YIELD; } else return CANCEL; case CLOSE : //cout << "project CLOSE" << endl; qp->Close(args[0].addr); return 0; } return 0; } /* 4.1.3 Specification of operator ~project~ */ const string CcProjectSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple ((x1 T1) ... " "(xn Tn)))) (ai1 ... aik)) -> (stream (mtuple" " ((ai1 Ti1) ... (aik Tik))))" "_ project [ list ]" "Produces a projection tuple for each " "tuple of its input stream." "query cities feed project[cityname, " "population] consume" ") )"; /* 4.1.3 Definition of operator ~project~ */ Operator ccrelproject ( "project", // name CcProjectSpec, // specification CcProject, // value mapping Operator::SimpleSelect, // trivial selection function CcProjectTypeMap // type mapping ); /* 7.3 Operator ~remove~ 7.3.1 Type mapping function of operator ~remove~ Result type of ~remove~ operation. ---- ((stream (mtuple ((x1 T1) ... (xn Tn)))) (ai1 ... aik)) -> (APPEND (n-k (j1 ... jn-k)) (stream (mtuple ((aj1 Tj1) ... (ajn-k Tjn-k)))) ) ---- The type mapping computes the number of attributes and the list of attribute numbers for the given left attributes (after removal) and asks the query processor to append it to the given arguments. */ ListExpr CcRemoveTypeMap(ListExpr args) { bool firstcall = false; int noAttrs = 0, j = 0; ListExpr first, second, first2; ListExpr attrtype, newAttrList, lastNewAttrList; ListExpr lastNumberList, numberList, outlist; first = second = first2 = nl->TheEmptyList(); attrtype = newAttrList = lastNewAttrList = nl->TheEmptyList(); lastNumberList = numberList = outlist = nl->TheEmptyList(); string attrname = ""; set removeSet; removeSet.clear(); firstcall = true; if (nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if ((nl->ListLength(first) == 2) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (nl->ListLength(nl->Second(first)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && (!nl->IsAtom(second)) && (nl->ListLength(second) > 0)) { while (!(nl->IsEmpty(second))) { first2 = nl->First(second); second = nl->Rest(second); if (nl->AtomType(first2) == SymbolType) { attrname = nl->SymbolValue(first2); } else { ErrorReporter::ReportError("Incorrect input for operator ~remove~."); return nl->SymbolAtom(Symbol::TYPEERROR()); } j = CcFindAttribute(nl->Second(nl->Second(first)), attrname, attrtype, nl); if (j) removeSet.insert(j); else { ErrorReporter::ReportError("Incorrect input for operator ~remove~."); return nl->SymbolAtom(Symbol::TYPEERROR()); } } /* here we need to generate new attr list according to removeSet */ ListExpr oldAttrList; int i; i=0; // i is the index of the old attriblist first = nl->First(args); second = nl->Second(args); oldAttrList=nl->Second(nl->Second(first)); //noAttrs = nl->ListLength(oldAttrList) - nl->ListLength(second); // n-k noAttrs =0; while (!(nl->IsEmpty(oldAttrList))) { i++; first2 = nl->First(oldAttrList); oldAttrList = nl->Rest(oldAttrList); if (removeSet.find(i)==removeSet.end()) //the attribute is not in the removal list { noAttrs++; if (firstcall) { firstcall = false; newAttrList = nl->OneElemList(first2); lastNewAttrList = newAttrList; numberList = nl->OneElemList(nl->IntAtom(i)); lastNumberList = numberList; } else { lastNewAttrList = nl->Append(lastNewAttrList, first2); lastNumberList = nl->Append(lastNumberList, nl->IntAtom(i)); } } } // Check whether all new attribute names are distinct // - not yet implemented //check whether the returning list is null if (noAttrs>0) {outlist = nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()), nl->TwoElemList(nl->IntAtom(noAttrs), numberList), nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList(nl->SymbolAtom( CcTuple::BasicType()), newAttrList))); return outlist; } else { ErrorReporter::ReportError( "Incorrect input for operator ~remove~ - " "trying to remove all attributes."); return nl->SymbolAtom(Symbol::TYPEERROR()); } } } ErrorReporter::ReportError("Incorrect input for operator ~remove~."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~remove~ */ static int CcRemove(Word* args, Word& result, int message, Word& local, Supplier s) { Word elem1, elem2; int noOfAttrs, index; Supplier son; Attribute* attr; CcTuple* t; switch (message) { case OPEN : qp->Open(args[0].addr); return 0; case REQUEST : qp->Request(args[0].addr, elem1); if (qp->Received(args[0].addr)) { t = new CcTuple(); t->SetFree(true); noOfAttrs = ((CcInt*)args[2].addr)->GetIntval(); t->SetNoAttrs(noOfAttrs); for (int i=1; i <= noOfAttrs; i++) { son = qp->GetSupplier(args[3].addr, i-1); qp->Request(son, elem2); index = ((CcInt*)elem2.addr)->GetIntval(); attr = ((CcTuple*)elem1.addr)->Get(index-1); t->Put(i-1, ((Attribute*)attr->Clone())); } ((CcTuple*)elem1.addr)->DeleteIfAllowed(); result = SetWord(t); return YIELD; } else return CANCEL; case CLOSE : qp->Close(args[0].addr); return 0; } return 0; } /* 4.1.3 Specification of operator ~remove~ */ const string CcRemoveSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple ((x1 T1) ... " "(xn Tn)))) (ai1 ... aik)) -> (stream " "(mtuple ((aj1 Tj1) ... (ajn-k Tjn-k))))" "" "_ remove [list]" "Produces a removal tuple for each " "tuple of its input stream." "query cities feed remove[zipcode] " "consume" ") )"; /* 4.1.3 Definition of operator ~remove~ */ Operator ccrelremove ( "remove", // name CcRemoveSpec, // specification CcRemove, // value mapping Operator::SimpleSelect, // trivial selection function CcRemoveTypeMap // type mapping ); /* 7.3 Operator ~product~ 5.6.1 Help Function ~Concat~ Copies the attribute values of two tuples (words) ~r~ and ~s~ into tuple (word) ~t~. */ void CcConcat (Word r, Word s, Word& t) { int rnoattrs, snoattrs, tnoattrs; Attribute* attr; rnoattrs = ((CcTuple*)r.addr)->GetNoAttrs(); snoattrs = ((CcTuple*)s.addr)->GetNoAttrs(); if ((rnoattrs + snoattrs) > MaxSizeOfAttr) { tnoattrs = MaxSizeOfAttr; } else { tnoattrs = rnoattrs + snoattrs; } ((CcTuple*)t.addr)->SetNoAttrs(tnoattrs); for (int i = 1; i <= rnoattrs; i++) { attr = ((CcTuple*)r.addr)->Get(i - 1); ((CcTuple*)t.addr)->Put((i - 1), ((Attribute*)attr)->Clone()); } for (int j = (rnoattrs + 1); j <= tnoattrs; j++) { attr = ((CcTuple*)s.addr)->Get(j - rnoattrs - 1); ((CcTuple*)t.addr)->Put((j - 1), ((Attribute*)attr)->Clone()); } } /* 7.3.1 Type mapping function of operator ~product~ Result type of product operation. ---- ((stream (mtuple (x1 ... xn))) (stream (mtuple (y1 ... ym)))) -> (stream (mtuple (x1 ... xn y1 ... ym))) ---- */ bool CcCompareNames(ListExpr list) { vector attrnamestrlist; vector::iterator it; ListExpr attrnamelist; int unique; string attrname; attrnamelist = list; attrnamestrlist.resize(nl->ListLength(list)); it = attrnamestrlist.begin(); while (!nl->IsEmpty(attrnamelist)) { attrname = nl->SymbolValue(nl->First(nl->First(attrnamelist))); attrnamelist = nl->Rest(attrnamelist); unique = std::count(attrnamestrlist.begin(), attrnamestrlist.end(), attrname); *it = attrname; if (unique) return false; it++; } return true; } ListExpr CcProductTypeMap(ListExpr args) { ListExpr first, second, list, list1, list2, outlist; if (nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); // Check first argument and extract list1 if (nl->ListLength(first) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) { if (nl->ListLength(nl->Second(first)) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) { list1 = nl->Second(nl->Second(first)); } else goto typeerror; } else goto typeerror; } else goto typeerror; } else goto typeerror; // Check second argument and extract list2 if (nl->ListLength(second) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(second)) == mstream) { if (nl->ListLength(nl->Second(second)) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(nl->Second(second))) == mtuple) { list2 = nl->Second(nl->Second(second)); } else goto typeerror; } else goto typeerror; } else goto typeerror; } else goto typeerror; list = CcConcatLists(list1, list2); // Check whether all new attribute names are distinct // - not yet implemented if ( CcCompareNames(list) ) { outlist = nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList(nl->SymbolAtom(CcTuple::BasicType()), list)); return outlist; } else goto typeerror; } else goto typeerror; typeerror: ErrorReporter::ReportError("Incorrect input for operator product."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~product~ */ CPUTimeMeasurer ccProductMeasurer; struct CcProductLocalInfo { CcTuple* currentTuple; CcRep rightRel; CcRep::iterator iter; }; static int CcProduct(Word* args, Word& result, int message, Word& local, Supplier s) { Word r, u, t; CcTuple* tuple; CcProductLocalInfo* pli; switch (message) { case OPEN : qp->Open(args[0].addr); qp->Request(args[0].addr, r); pli = new CcProductLocalInfo; pli->currentTuple = qp->Received(args[0].addr) ? (CcTuple*)r.addr : 0; /* materialize right stream */ qp->Open(args[1].addr); qp->Request(args[1].addr, u); while(qp->Received(args[1].addr)) { pli->rightRel.push_back((CcTuple*)u.addr); qp->Request(args[1].addr, u); } pli->iter = pli->rightRel.begin(); local = SetWord(pli); return 0; case REQUEST : pli = (CcProductLocalInfo*)local.addr; ccProductMeasurer.Enter(); if (pli->currentTuple == 0) { ccProductMeasurer.Exit(); return CANCEL; } else { if(pli->iter != pli->rightRel.end()) { tuple = new CcTuple(); tuple->SetFree(true); t = SetWord(tuple); CcConcat(SetWord(pli->currentTuple), SetWord(*(pli->iter)), t); result = t; ++(pli->iter); ccProductMeasurer.Exit(); return YIELD; } else { /* restart iterator for right relation and fetch a new tuple from left stream */ pli->currentTuple->DeleteIfAllowed(); pli->currentTuple = 0; qp->Request(args[0].addr, r); if (qp->Received(args[0].addr)) { pli->currentTuple = (CcTuple*)r.addr; pli->iter = pli->rightRel.begin(); if(pli->iter == pli->rightRel.end()) // second stream is empty { ccProductMeasurer.Exit(); return CANCEL; } else { tuple = new CcTuple(); tuple->SetFree(true); t = SetWord(tuple); CcConcat(SetWord(pli->currentTuple), SetWord(*(pli->iter)), t); result = t; ++(pli->iter); ccProductMeasurer.Exit(); return YIELD; } } else { ccProductMeasurer.Exit(); return CANCEL; // left stream exhausted } } } case CLOSE : pli = (CcProductLocalInfo*)local.addr; if(pli) { if(pli->currentTuple != 0) { pli->currentTuple->DeleteIfAllowed(); } for(pli->iter = pli->rightRel.begin(); pli->iter != pli->rightRel.end(); ++(pli->iter)) { (*(pli->iter))->DeleteIfAllowed(); } delete pli; local = SetWord(Address(0)); } qp->Close(args[0].addr); qp->Close(args[1].addr); ccProductMeasurer.PrintCPUTimeAndReset("Product CPU Time : "); return 0; } return 0; } /* 4.1.3 Specification of operator ~product~ */ const string CcProductSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple (x1 ... xn))) (stream " "(mtuple (y1 ... ym)))) -> (stream (mtuple (x1 " "... xn y1 ... ym)))" "_ _ product" "Computes a Cartesian product stream from " "its two argument streams." "query ten feed twenty feed product count" "" " ) )"; /* 4.1.3 Definition of operator ~product~ */ Operator ccrelproduct ( "product", // name CcProductSpec, // specification CcProduct, // value mapping Operator::SimpleSelect, // trivial selection function CcProductTypeMap // type mapping ); /* 7.3 Operator ~cancel~ Transmits tuple from its input stream to its output stream until a tuple arrives fulfilling some condition. 7.3.1 Type mapping function of operator ~cancel~ Type mapping for ~cancel~ is the same, as type mapping for operator ~filter~. Result type of cancel operation. ---- ((stream x) (map x bool)) -> (stream x) ---- 4.1.2 Value mapping function of operator ~cancel~ */ static int CcCancel(Word* args, Word& result, int message, Word& local, Supplier s) { Word t, value; CcTuple* tuple; bool found; ArgVectorPointer vector; switch (message) { case OPEN : qp->Open(args[0].addr); return 0; case REQUEST : qp->Request(args[0].addr, t); found= false; if (qp->Received(args[0].addr)) { tuple = (CcTuple*)t.addr; vector = qp->Argument(args[1].addr); (*vector)[0] = t; qp->Request(args[1].addr, value); found = ((CcBool*)value.addr)->IsDefined() && ((CcBool*)value.addr)->GetBoolval(); if (found) { qp->Close(args[0].addr); return CANCEL; } else { result = SetWord(tuple); return YIELD; } } else return CANCEL; case CLOSE : qp->Close(args[0].addr); return 0; } return 0; } /* 4.1.3 Specification of operator ~cancel~ */ const string CcCancelSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream x) (map x bool)) -> " "(stream x)" "_ cancel [ fun ]" "Transmits tuple from its input stream " "to its output stream until a tuple arrives " "fulfilling some condition." "query cities feed cancel [.cityname = " "\"Dortmund\"] consume" ") )"; /* 4.1.3 Definition of operator ~cancel~ */ Operator ccrelcancel ( "cancel", // name CcCancelSpec, // specification CcCancel, // value mapping Operator::SimpleSelect, // trivial selection function CcFilterTypeMap // type mapping ); /* 7.3 Operator ~count~ Count the number of tuples within a stream of tuples. 7.3.1 Type mapping function of operator ~count~ Operator ~count~ accepts a stream of tuples and returns an integer. ---- (stream (mtuple x)) -> int ---- */ ListExpr CcCountTypeMap(ListExpr args) { ListExpr first; if( nl->ListLength(args) == 1 ) { first = nl->First(args); if ( (nl->ListLength(first) == 2) && nl->ListLength(nl->Second(first)) == 2 ) { if ( ( CcTypeOfRelAlgSymbol(nl->First(first)) == mstream || CcTypeOfRelAlgSymbol(nl->First(first)) == mrel ) && CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple ) return nl->SymbolAtom(CcInt::BasicType()); } } ErrorReporter::ReportError("Incorrect input for operator count."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping functions of operator ~count~ */ static int CcCountStream(Word* args, Word& result, int message, Word& local, Supplier s) { Word elem; int count = 0; //cout << "tcount" << endl; qp->Open(args[0].addr); qp->Request(args[0].addr, elem); while ( qp->Received(args[0].addr) ) { ((CcTuple*)elem.addr)->DeleteIfAllowed(); count++; qp->Request(args[0].addr, elem); } result = qp->ResultStorage(s); ((CcInt*) result.addr)->Set(true, count); qp->Close(args[0].addr); return 0; } static int CcCountRel(Word* args, Word& result, int message, Word& local, Supplier s) { CcRel* rel = (CcRel*)args[0].addr; result = qp->ResultStorage(s); ((CcInt*) result.addr)->Set(true, rel->GetNoTuples()); return 0; } /* 4.1.3 Specification of operator ~count~ */ const string CcCountSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream/mrel (mtuple x))) -> int" "" "_ count" "Count number of tuples within a stream " "or a relation of tuples." "query cities count or query cities " "feed count" ") )"; /* 4.3.1 Selection function of operator ~tcount~ */ static int CcCountSelect( ListExpr args ) { if(nl->ListLength(args) == 1) { ListExpr first = nl->First(args); if(nl->ListLength(first) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) { return 0; } else { if(CcTypeOfRelAlgSymbol(nl->First(first)) == mrel) { return 1; } } } } return -1; } /* 4.1.3 Definition of operator ~count~ */ ValueMapping ccrelcountmap[] = {CcCountStream, CcCountRel }; Operator ccrelcount ( "count", // name CcCountSpec, // specification 2, // number of value mapping functions ccrelcountmap, // value mapping functions CcCountSelect, // trivial selection function CcCountTypeMap // type mapping ); /* 7.3 Operator ~rename~ Renames all attribute names by adding them with the postfix passed as parameter. 7.3.1 Type mapping function of operator ~rename~ Type mapping for ~rename~ is ---- ((stream (mtuple([a1:d1, ... ,an:dn)))ar) -> (stream (mtuple([a1ar:d1, ... ,anar:dn))) ---- */ static ListExpr CcRenameTypeMap( ListExpr args ) { ListExpr first, first2, second; ListExpr rest, listn, lastlistn; first = first2 = second = nl->TheEmptyList(); rest = listn = lastlistn = nl->TheEmptyList(); string attrname = ""; string attrnamen = ""; bool firstcall = true; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && nl->IsAtom(second) && (nl->AtomType(second) == SymbolType)) { rest = nl->Second(nl->Second(first)); while (!(nl->IsEmpty(rest))) { first2 = nl->First(rest); rest = nl->Rest(rest); nl->SymbolValue(nl->First(first)); attrname = nl->SymbolValue(nl->First(first2)); attrnamen = nl->SymbolValue(second); attrname.append("_"); attrname.append(attrnamen); if (!firstcall) { lastlistn = nl->Append(lastlistn, nl->TwoElemList(nl->SymbolAtom(attrname), nl->Second(first2))); } else { firstcall = false; listn = nl->OneElemList(nl->TwoElemList(nl->SymbolAtom(attrname), nl->Second(first2))); lastlistn = listn; } } return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList(nl->SymbolAtom(CcTuple::BasicType()), listn)); } ErrorReporter::ReportError("Incorrect input for operator rename."); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError("Incorrect input for operator rename."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~rename~ */ static int CcRename(Word* args, Word& result, int message, Word& local, Supplier s) { Word t; CcTuple* tuple; switch (message) { case OPEN : qp->Open(args[0].addr); return 0; case REQUEST : qp->Request(args[0].addr,t); if (qp->Received(args[0].addr)) { tuple = (CcTuple*)t.addr; result = SetWord(tuple); return YIELD; } else return CANCEL; case CLOSE : qp->Close(args[0].addr); return 0; } return 0; } /* 4.1.3 Specification of operator ~rename~ */ const string CcRenameSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ," "an:dn)))ar) -> (stream (mtuple([a1ar:d1, " "... ,anar:dn)))" "_ rename [ _ ] or just _ { _ }" "" "Renames all attribute names by adding" " them with the postfix passed as parameter. " "NOTE: parameter must be of symbol type." "" "query ten feed rename [ r1 ] consume " "or query ten feed {r1} consume, the result " "has format e.g. n_r1" ") )"; /* 4.1.3 Definition of operator ~rename~ */ Operator ccrelrename ( "rename", // name CcRenameSpec, // specification CcRename, // value mapping Operator::SimpleSelect, // trivial selection function CcRenameTypeMap // type mapping ); /* 7.3 Operator ~extract~ This operator has a stream of tuples and the name of an attribut as input and returns the value of this attribute from the first tuple of the input stream. If the input stream is empty a run time error occurs. In this case value -1 will be returned. 7.3.1 Type mapping function of operator ~extract~ Type mapping for ~extract~ is ---- ((stream (mtuple ((x1 t1)...(xn tn))) xi) -> ti APPEND (i) ti) ---- */ static ListExpr CcExtractTypeMap( ListExpr args ) { ListExpr first, second, attrtype; string attrname; int j; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && (nl->IsAtom(second)) && (nl->AtomType(second) == SymbolType)) { attrname = nl->SymbolValue(second); j = CcFindAttribute(nl->Second(nl->Second(first)), attrname, attrtype, nl); if (j) return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()), nl->OneElemList(nl->IntAtom(j)), attrtype); } ErrorReporter::ReportError("Incorrect input for operator extract."); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError("Incorrect input for operator extract."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~extract~ The argument vector ~args~ contains in the first slot ~args[0]~ the tuple and in ~args[2]~ the position of the attribute as a number. Returns as ~result~ the value of an attribute at the given position ~args[2]~ in a tuple object. */ static int CcExtract(Word* args, Word& result, int message, Word& local, Supplier s) { Word t; CcTuple* tupleptr; int index; Attribute* res = (Attribute*)((qp->ResultStorage(s)).addr); result = SetWord(res); qp->Open(args[0].addr); qp->Request(args[0].addr,t); if (qp->Received(args[0].addr)) { tupleptr = (CcTuple*)t.addr; index = ((CcInt*)args[2].addr)->GetIntval(); assert((1 <= index) && (index <= tupleptr->GetNoAttrs())); res->CopyFrom((const Attribute*)tupleptr->Get(index - 1)); tupleptr->DeleteIfAllowed(); } else { res->SetDefined(false); } qp->Close(args[0].addr); return 0; } /* 4.1.3 Specification of operator ~extract~ */ const string CcExtractSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn]" "))) x ai) -> di" "_ extract [ _ ]" "Returns the value of attribute ai of " "the first tuple in the input stream." "" "query cities feed extract [population]" "" ") )"; /* 4.1.3 Definition of operator ~extract~ */ Operator ccrelextract ( "extract", // name CcExtractSpec, // specification CcExtract, // value mapping Operator::SimpleSelect, // trivial selection function CcExtractTypeMap // type mapping ); /* 7.3 Operator ~head~ This operator fetches the first n tuples from a stream. 7.3.1 Type mapping function of operator ~head~ Type mapping for ~head~ is ---- ((stream (mtuple ((x1 t1)...(xn tn))) int) -> ((stream (mtuple ((x1 t1)...(xn tn)))) ---- */ static ListExpr CcHeadTypeMap( ListExpr args ) { ListExpr first, second; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && CcIsTupleDescription(nl->Second(nl->Second(first)), nl) && (nl->IsAtom(second)) && (nl->AtomType(second) == SymbolType) && nl->SymbolValue(second) == CcInt::BasicType()) { return first; } ErrorReporter::ReportError("Incorrect input for operator head."); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError("Incorrect input for operator head."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~head~ */ static int CcHead(Word* args, Word& result, int message, Word& local, Supplier s) { Word tupleWord; CcTuple* tuple; switch(message) { case OPEN: //cout << "head OPEN" << endl; qp->Open(args[0].addr); local.addr = new int(((CcInt*)args[1].addr)->GetIntval()); return 0; case REQUEST:{ //cout << "head REQUEST" << endl; int* c = static_cast(local.addr); if( (*c) <= 0) { return CANCEL; } qp->Request(args[0].addr, tupleWord); if(qp->Received(args[0].addr)) { tuple = (CcTuple*)tupleWord.addr; result = SetWord(tuple); (*(c))--; return YIELD; } else { return CANCEL; } } case CLOSE: //cout << "head CLOSE" << endl; qp->Close(args[0].addr); if(local.addr){ int* li = static_cast(local.addr); delete li; local.addr=0; } return 0; } return 0; } /* 4.1.3 Specification of operator ~head~ */ const string CcHeadSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn]" "))) x int) -> (stream (mtuple([a1:d1, ... ," "an:dn])))" "_ head [ _ ]" "Returns the first n tuples in the input " "stream." "query cities feed head[10] consume" "" ") )"; /* 4.1.3 Definition of operator ~head~ */ Operator ccrelhead ( "head", // name CcHeadSpec, // specification CcHead, // value mapping Operator::SimpleSelect, // trivial selection function CcHeadTypeMap // type mapping ); /* 7.3 Operators ~max~ and ~min~ 7.3.1 Type mapping function of Operators ~max~ and ~min~ Type mapping for ~max~ and ~min~ is ---- ((stream (mtuple ((x1 t1)...(xn tn))) xi) -> ti APPEND (i ti) ---- */ template ListExpr CcMaxMinTypeMap( ListExpr args ) { ListExpr first, second, attrtype; string attrname; int j; const char* errorMessage = isMax ? "Incorrect input for operator max." : "Incorrect input for operator min."; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && CcIsTupleDescription(nl->Second(nl->Second(first)), nl) && (nl->IsAtom(second)) && (nl->AtomType(second) == SymbolType)) { attrname = nl->SymbolValue(second); j = CcFindAttribute(nl->Second(nl->Second(first)), attrname, attrtype, nl); if (j > 0 && (nl->SymbolValue(attrtype) == CcReal::BasicType() || nl->SymbolValue(attrtype) == CcString::BasicType() || nl->SymbolValue(attrtype) == CcBool::BasicType() || nl->SymbolValue(attrtype) == CcInt::BasicType())) { return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()), nl->OneElemList(nl->IntAtom(j)), attrtype); } } ErrorReporter::ReportError(errorMessage); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError(errorMessage); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operators ~max~ and ~min~ */ template int CcMaxMinValueMapping(Word* args, Word& result, int message, Word& local, Supplier s) { bool definedValueFound = false; Word currentTupleWord; Attribute* extremum = (Attribute*)(qp->ResultStorage(s)).addr; extremum->SetDefined(false); result = SetWord(extremum); assert(args[2].addr != 0); int attributeIndex = ((CcInt*)args[2].addr)->GetIntval() - 1; qp->Open(args[0].addr); qp->Request(args[0].addr, currentTupleWord); while(qp->Received(args[0].addr)) { CcTuple* currentTuple = (CcTuple*)currentTupleWord.addr; const Attribute* currentAttr = (const Attribute*)currentTuple->Get(attributeIndex); if(currentAttr->IsDefined()) { if(definedValueFound) { if(isMax) { if(currentAttr->Compare(extremum) > 0) { extremum->CopyFrom(currentAttr); } } else { if(currentAttr->Compare(extremum) < 0) { extremum->CopyFrom(currentAttr); } } } else { definedValueFound = true; extremum->CopyFrom(currentAttr); } } currentTuple->DeleteIfAllowed(); qp->Request(args[0].addr, currentTupleWord); } qp->Close(args[0].addr); return 0; } /* 4.1.3 Specification of operator ~max~ */ const string CcMaxOpSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn]" "))) x ai) -> di" "_ _ mergesec" "Returns the maximum value of attribute " "ai over the input stream." "query cities feed max [ cityname ]" "" ") )"; /* 4.1.3 Definition of operator ~max~ */ Operator ccrelmax ( "max", // name CcMaxOpSpec, // specification CcMaxMinValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcMaxMinTypeMap // type mapping ); /* 4.1.3 Specification of operator ~min~ */ const string CcMinOpSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn])))" " x ai) -> di" "_ min [ _ ]" "Returns the minimum value of attribute ai " "over the input stream." "query cities feed min [ cityname ]" "" ") )"; /* 4.1.3 Definition of operator ~min~ */ Operator ccrelmin ( "min", // name CcMinOpSpec, // specification CcMaxMinValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcMaxMinTypeMap // type mapping ); /* 7.3 Operators ~avg~ and ~sum~ 7.3.1 Type mapping function of Operators ~avg~ and ~sum~ Type mapping for ~avg~ is ---- ((stream (mtuple ((x1 t1)...(xn tn))) xi) -> real APPEND (i ti) ---- Type mapping for ~sum~ is ---- ((stream (mtuple ((x1 t1)...(xn tn))) xi) -> ti APPEND (i ti) ---- */ template ListExpr CcAvgSumTypeMap( ListExpr args ) { ListExpr first, second, attrtype; string attrname; int j; const char* errorMessage = isAvg ? "Incorrect input for operator avg." : "Incorrect input for operator sum."; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && CcIsTupleDescription(nl->Second(nl->Second(first)), nl) && (nl->IsAtom(second)) && (nl->AtomType(second) == SymbolType)) { attrname = nl->SymbolValue(second); j = CcFindAttribute(nl->Second(nl->Second(first)), attrname, attrtype, nl); if (j > 0 && (nl->SymbolValue(attrtype) == CcReal::BasicType() || nl->SymbolValue(attrtype) == CcInt::BasicType())) { return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()), nl->TwoElemList(nl->IntAtom(j), nl->StringAtom(nl->SymbolValue(attrtype))), isAvg ? nl->SymbolAtom(CcReal::BasicType()) : attrtype); } } ErrorReporter::ReportError(errorMessage); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError(errorMessage); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operators ~avg~ and ~sum~ */ template int CcAvgSumValueMapping(Word* args, Word& result, int message, Word& local, Supplier s) { bool definedValueFound = false; Word currentTupleWord; Attribute* accumulated = 0; int nProcessedItems = 0; assert(args[2].addr != 0); assert(args[3].addr != 0); int attributeIndex = ((CcInt*)args[2].addr)->GetIntval() - 1; const STRING_T *attributeType = ((CcString*)args[3].addr)->GetStringval(); qp->Open(args[0].addr); qp->Request(args[0].addr, currentTupleWord); while(qp->Received(args[0].addr)) { CcTuple* currentTuple = (CcTuple*)currentTupleWord.addr; Attribute* currentAttr = (Attribute*)currentTuple->Get(attributeIndex); if(currentAttr->IsDefined()) { nProcessedItems++; if(definedValueFound) { if(strcmp(*attributeType, CcReal::BasicType().c_str()) == 0) { CcReal* accumulatedReal = (CcReal*)accumulated; CcReal* currentReal = (CcReal*)currentAttr; accumulatedReal->Set(currentReal->GetRealval() + accumulatedReal->GetRealval()); } else { CcInt* accumulatedInt = (CcInt*)accumulated; CcInt* currentInt = (CcInt*)currentAttr; accumulatedInt->Set(currentInt->GetIntval() + accumulatedInt->GetIntval()); } } else { definedValueFound = true; accumulated = currentAttr->Clone(); } } currentTuple->DeleteIfAllowed(); qp->Request(args[0].addr, currentTupleWord); } qp->Close(args[0].addr); if(definedValueFound) { if(isAvg) { CcReal* resultAttr = (CcReal*)(qp->ResultStorage(s).addr); SEC_STD_REAL nItems = (SEC_STD_REAL)nProcessedItems; if(strcmp(*attributeType, CcReal::BasicType().c_str()) == 0) { CcReal* accumulatedReal = (CcReal*)accumulated; resultAttr->Set(accumulatedReal->GetRealval() / nItems); } else { CcInt* accumulatedInt = (CcInt*)accumulated; resultAttr->Set(((SEC_STD_REAL)accumulatedInt->GetIntval()) / nItems); } delete accumulated; result = SetWord(resultAttr); } else { if(strcmp(*attributeType, CcReal::BasicType().c_str()) == 0) { CcReal* resultAttr = (CcReal*)(qp->ResultStorage(s).addr); CcReal* accumulatedReal = (CcReal*)accumulated; resultAttr->Set(accumulatedReal->GetRealval()); result = SetWord(resultAttr); } else { CcInt* resultAttr = (CcInt*)(qp->ResultStorage(s).addr); CcInt* accumulatedInt = (CcInt*)accumulated; resultAttr->Set(accumulatedInt->GetIntval()); result = SetWord(resultAttr); } delete accumulated; } return 0; } else { ((Attribute*)qp->ResultStorage(s).addr)->SetDefined(false); result = qp->ResultStorage(s); return 0; } } /* 4.1.3 Specification of operator ~avg~ */ const string CcAvgOpSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn]" "))) x ai) -> real" "_ avg [ _ ]" "Returns the average value of attribute " "ai over the input stream." "query cities feed avg [population]" "" ") )"; /* 4.1.3 Definition of operator ~avg~ */ Operator ccrelavg ( "avg", // name CcAvgOpSpec, // specification CcAvgSumValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcAvgSumTypeMap // type mapping ); /* 4.1.3 Specification of operator ~sum~ */ const string CcSumOpSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn]" "))) x ai) -> di" "_ sum [ _ ]" "Returns the sum of the values of attribute" " ai over the input stream." "query cities feed sum[population]" "" ") )"; /* 4.1.3 Definition of operator ~sum~ */ Operator ccrelsum ( "sum", // name CcSumOpSpec, // specification CcAvgSumValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcAvgSumTypeMap // type mapping ); /* 7.3 Operator ~sortBy~ This operator sorts a stream of tuples by a given list of attributes. For each attribute it must be specified wether the list should be sorted in ascending (asc) or descending (desc) order with regard to that attribute. 7.3.1 Type mapping function of operator ~sortBy~ Type mapping for ~sortBy~ is ---- ((stream (mtuple ((x1 t1)...(xn tn))) ((xi1 asc/desc) ... (xij asc/desc))) -> (stream (mtuple ((x1 t1)...(xn tn))) APPEND (j i1 asc/desc i2 asc/desc ... ij asc/desc) ---- */ const char* sortAscending = "asc"; const char* sortDescending = "desc"; static ListExpr CcSortByTypeMap( ListExpr args ) { ListExpr attrtype; string attrname; if(nl->ListLength(args) == 2) { ListExpr streamDesc = nl->First(args); ListExpr sortSpec = nl->Second(args); if((nl->ListLength(streamDesc) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(streamDesc)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(streamDesc))) == mtuple)) { int numberOfSortAttrs = nl->ListLength(sortSpec); if(numberOfSortAttrs > 0) { ListExpr sortOrderDesc = nl->OneElemList(nl->IntAtom(numberOfSortAttrs)); ListExpr sortOrderDescLast = sortOrderDesc; ListExpr rest = sortSpec; while(!nl->IsEmpty(rest)) { ListExpr attrSpec = nl->First(rest); rest = nl->Rest(rest); if((nl->ListLength(attrSpec) == 2) && (nl->IsAtom(nl->First(attrSpec))) && (nl->AtomType(nl->First(attrSpec)) == SymbolType) && (nl->IsAtom(nl->Second(attrSpec))) && (nl->AtomType(nl->Second(attrSpec)) == SymbolType)) { attrname = nl->SymbolValue(nl->First(attrSpec)); int j = CcFindAttribute(nl->Second(nl->Second(streamDesc)), attrname, attrtype, nl); if ((j > 0) && ((nl->SymbolValue(nl->Second(attrSpec)) == sortAscending) || (nl->SymbolValue(nl->Second(attrSpec)) == sortDescending))) { sortOrderDescLast = nl->Append(sortOrderDescLast, nl->IntAtom(j)); bool isAscending = nl->SymbolValue(nl->Second(attrSpec)) == sortAscending; sortOrderDescLast = nl->Append(sortOrderDescLast, nl->BoolAtom(isAscending)); } else { ErrorReporter::ReportError( "Incorrect input for operator sortby."); return nl->SymbolAtom(Symbol::TYPEERROR()); } } else { ErrorReporter::ReportError("Incorrect input for operator sortby."); return nl->SymbolAtom(Symbol::TYPEERROR()); } } return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()), sortOrderDesc, streamDesc); }; ErrorReporter::ReportError("Incorrect input for operator sortby."); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError("Incorrect input for operator sortby."); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError("Incorrect input for operator sortby."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~sortBy~ The argument vector ~args~ contains in the first slot ~args[0]~ the stream and in ~args[2]~ the number of sort attributes. ~args[3]~ contains the index of the first sort attribute, ~args[4]~ a boolean indicating wether the stream is sorted in ascending order with regard to the sort first attribute. ~args[5]~ and ~args[6]~ contain these values for the second sort attribute and so on. */ typedef vector< pair > CcSortOrderSpecification; class CcTupleCmp { public: CcSortOrderSpecification spec; bool operator()(const CcTuple* aConst, const CcTuple* bConst) const { CcTuple* a = (CcTuple*)aConst; CcTuple* b = (CcTuple*)bConst; CcSortOrderSpecification::const_iterator iter = spec.begin(); while(iter != spec.end()) { if(((Attribute*)a->Get(iter->first - 1))-> Compare(((Attribute*)b->Get(iter->first - 1))) < 0) { return iter->second; } else { if(((Attribute*)a->Get(iter->first - 1))-> Compare(((Attribute*)b->Get(iter->first - 1))) > 0) { return !(iter->second); } } iter++; } return false; } }; struct CcSortByLocalInfo { vector* tuples; size_t currentIndex; }; CPUTimeMeasurer ccSortMeasurer; template int CcSortBy(Word* args, Word& result, int message, Word& local, Supplier s) { Word tuple; vector* tuples; CcSortByLocalInfo* localInfo; CcSortOrderSpecification spec; int i; size_t j; int sortAttrIndex; int nSortAttrs; bool sortOrderIsAscending; CcTupleCmp ccCmp; LexicographicalCcTupleCmp lCcCmp; CcTuple* t; switch(message) { case OPEN: tuples = new vector; qp->Open(args[0].addr); qp->Request(args[0].addr,tuple); while(qp->Received(args[0].addr)) { t =(CcTuple*)tuple.addr; tuples->push_back(t); qp->Request(args[0].addr,tuple); } qp->Close(args[0].addr); if(lexicographically) { ccSortMeasurer.Enter(); sort(tuples->begin(), tuples->end(), lCcCmp); ccSortMeasurer.Exit(); } else { nSortAttrs = ((CcInt*)args[2].addr)->GetIntval(); for(i = 1; i <= nSortAttrs; i++) { sortAttrIndex = ((CcInt*)args[2 * i + 1].addr)->GetIntval(); sortOrderIsAscending = ((CcBool*)args[2 * i + 2].addr)->GetBoolval(); spec.push_back(pair(sortAttrIndex, sortOrderIsAscending)); }; ccCmp.spec = spec; ccSortMeasurer.Enter(); sort(tuples->begin(), tuples->end(), ccCmp); ccSortMeasurer.Exit(); } ccSortMeasurer.PrintCPUTimeAndReset("CPU Time for Sorting Tuples : "); localInfo = new CcSortByLocalInfo; localInfo->tuples = tuples; localInfo->currentIndex = 0; local = SetWord(localInfo); return 0; case REQUEST: localInfo = (CcSortByLocalInfo*)local.addr; tuples = localInfo->tuples; if(localInfo->currentIndex + 1 <= tuples->size()) { result = SetWord((*tuples)[localInfo->currentIndex]); localInfo->currentIndex++; return YIELD; } else { return CANCEL; } case CLOSE: localInfo = (CcSortByLocalInfo*)local.addr; if(localInfo) { for(j = localInfo->currentIndex; j + 1 <= localInfo->tuples->size(); j++) { (*(localInfo->tuples))[j]->DeleteIfAllowed(); } delete localInfo->tuples; delete localInfo; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of operator ~sortBy~ */ const string CcSortBySpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn])))" " ((xi1 asc/desc) ... (xij asc/desc))) -> " "(stream (mtuple([a1:d1, ... ,an:dn])))" "_ sortby [list]" "Sorts input stream according to a list " "of attributes ai1 ... aij." "query employee feed sortby[DeptNo asc] " "consume" ") )"; /* 4.1.3 Definition of operator ~sortBy~ */ Operator ccrelsortby ( "sortby", // name CcSortBySpec, // specification CcSortBy, // value mapping Operator::SimpleSelect, // trivial selection function CcSortByTypeMap // type mapping ); /* 7.3 Operator ~sort~ This operator sorts a stream of tuples lexicographically. 7.3.1 Type mapping function of operator ~sort~ Type mapping for ~sort~ is ---- ((stream (mtuple ((x1 t1)...(xn tn)))) -> (stream (mtuple ((x1 t1)...(xn tn))) ---- */ template ListExpr CcIdenticalTypeMap( ListExpr args ) { ListExpr first; const char* errorMessage = isSort ? "Incorrect input for operator sort." : "Incorrect input for operator rdup."; if(nl->ListLength(args) == 1) { first = nl->First(args); if((nl->ListLength(first) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && CcIsTupleDescription(nl->Second(nl->Second(first)), nl)) { return first; } ErrorReporter::ReportError(errorMessage); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError(errorMessage); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.3 Specification of operator ~sort~ */ const string CcSortSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn]" ")))) -> (stream (mtuple([a1:d1, ... ,an:dn])))" "" "_ sort" "Sorts input stream lexicographically." "" "query cities feed sort consume" ") )"; /* 4.1.3 Definition of operator ~sort~ */ Operator ccrelsort ( "sort", // name CcSortSpec, // specification CcSortBy, // value mapping Operator::SimpleSelect, // trivial selection function CcIdenticalTypeMap // type mapping ); /* 7.3 Operator ~rdup~ This operator removes duplicates from a sorted stream. 4.1.2 Value mapping function of operator ~rdup~ */ static int CcRdupValueMapping(Word* args, Word& result, int message, Word& local, Supplier s) { Word tuple; LexicographicalCcTupleCmp cmp; CcTuple* currentTuple; CcTuple* lastOutputTuple; switch(message) { case OPEN: qp->Open(args[0].addr); local = SetWord(Address(0)); return 0; case REQUEST: while(true) { qp->Request(args[0].addr, tuple); if(qp->Received(args[0].addr)) { if(local.addr != 0) { currentTuple = (CcTuple*)tuple.addr; lastOutputTuple = (CcTuple*)local.addr; if(cmp(currentTuple, lastOutputTuple) || cmp(lastOutputTuple, currentTuple)) { lastOutputTuple->DeleteIfAllowed(); local = SetWord(currentTuple->Clone()); result = SetWord(currentTuple); return YIELD; } else { currentTuple->DeleteIfAllowed(); } } else { currentTuple = (CcTuple*)tuple.addr; local = SetWord(currentTuple->Clone()); result = SetWord(currentTuple); return YIELD; } } else { lastOutputTuple = (CcTuple*)local.addr; if(lastOutputTuple != 0) { lastOutputTuple->DeleteIfAllowed(); } return CANCEL; } } case CLOSE: qp->Close(args[0].addr); return 0; } return 0; } /* 4.1.3 Specification of operator ~rdup~ */ const string CcRdupSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple([a1:d1, ... ,an:dn]))))" " -> (stream (mtuple([a1:d1, ... ,an:dn])))" "" "_ rdup" "Removes duplicates from a sorted " "stream." "query twenty feed ten feed concat sort " "rdup consume" ") )"; /* 4.1.3 Definition of operator ~rdup~ */ Operator ccrelrdup ( "rdup", // name CcRdupSpec, // specification CcRdupValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcIdenticalTypeMap // type mapping ); /* 7.3 Set Operators These operators compute set operations on two sorted stream. 7.3.1 Generic Type Mapping for Set Operations */ const char* ccSetOpErrorMessages[] = { "Incorrect input for operator mergesec.", "Incorrect input for operator mergediff.", "Incorrect input for operator mergeunion." }; template ListExpr CcSetOpTypeMap( ListExpr args ) { ListExpr first, second; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2 ) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && CcIsTupleDescription(nl->Second(nl->Second(first)), nl) && (nl->Equal(first, second))) { return first; } ErrorReporter::ReportError(ccSetOpErrorMessages[errorMessageIdx]); return nl->SymbolAtom(Symbol::TYPEERROR()); } ErrorReporter::ReportError(ccSetOpErrorMessages[errorMessageIdx]); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 7.3.2 Auxiliary Class for Set Operations */ class CcSetOperation { public: bool outputAWithoutB; bool outputBWithoutA; bool outputMatches; private: LexicographicalCcTupleCmp smallerThan; Word streamA; Word streamB; CcTuple* currentATuple; CcTuple* currentBTuple; CcTuple* NextATuple(bool deleteOldTuple) { Word tuple; if(deleteOldTuple && currentATuple != 0) { currentATuple->DeleteIfAllowed(); } qp->Request(streamA.addr, tuple); if(qp->Received(streamA.addr)) { currentATuple = (CcTuple*)tuple.addr; return currentATuple; } else { currentATuple = 0; return 0; } } CcTuple* NextBTuple(bool deleteOldTuple) { Word tuple; if(deleteOldTuple && currentBTuple != 0) { currentBTuple->DeleteIfAllowed(); } qp->Request(streamB.addr, tuple); if(qp->Received(streamB.addr)) { currentBTuple = (CcTuple*)tuple.addr; return currentBTuple; } else { currentBTuple = 0; return 0; } } bool TuplesEqual(CcTuple* a, CcTuple* b) { return !(smallerThan(a, b) || smallerThan(b, a)); } public: CcSetOperation(Word streamA, Word streamB) { this->streamA = streamA; this->streamB = streamB; currentATuple = 0; currentBTuple = 0; qp->Open(streamA.addr); qp->Open(streamB.addr); NextATuple(false); NextBTuple(false); } virtual ~CcSetOperation() { qp->Close(streamA.addr); qp->Close(streamB.addr); } CcTuple* NextResultTuple() { CcTuple* result = 0; while(result == 0) { if(currentATuple == 0) { if(currentBTuple == 0) { return 0; } else { if(outputBWithoutA) { result = currentBTuple; NextBTuple(false); while(currentBTuple != 0 && TuplesEqual(result, currentBTuple)) { NextBTuple(true); } } else { currentBTuple->DeleteIfAllowed(); return 0; } } } else { if(currentBTuple == 0) { if(outputAWithoutB) { result = currentATuple; NextATuple(false); while(currentATuple != 0 && TuplesEqual(result, currentATuple)) { NextATuple(true); } } else { currentATuple->DeleteIfAllowed(); return 0; } } else { /* both current tuples != 0 */ if(smallerThan(currentATuple, currentBTuple)) { if(outputAWithoutB) { result = currentATuple; } CcTuple* tmp = currentATuple; NextATuple(false); while(currentATuple != 0 && TuplesEqual(tmp, currentATuple)) { NextATuple(true); } if(!outputAWithoutB) { tmp->DeleteIfAllowed(); } } else if(smallerThan(currentBTuple, currentATuple)) { if(outputBWithoutA) { result = currentBTuple; } CcTuple* tmp = currentBTuple; NextBTuple(false); while(currentBTuple != 0 && TuplesEqual(tmp, currentBTuple)) { NextBTuple(true); } if(!outputBWithoutA) { tmp->DeleteIfAllowed(); } } else { /* found match */ assert(TuplesEqual(currentATuple, currentBTuple)); CcTuple* match = currentATuple; if(outputMatches) { result = match; } NextATuple(false); while(currentATuple != 0 && TuplesEqual(match, currentATuple)) { NextATuple(true); } while(currentBTuple != 0 && TuplesEqual(match, currentBTuple)) { NextBTuple(true); } if(!outputMatches) { match->DeleteIfAllowed(); } } } } } return result; } }; /* 7.3.2 Generic Value Mapping Function for Set Operations */ template int CcSetOpValueMapping(Word* args, Word& result, int message, Word& local, Supplier s) { CcSetOperation* localInfo; switch(message) { case OPEN: localInfo = new CcSetOperation(args[0], args[1]); localInfo->outputBWithoutA = outputBWithoutA; localInfo->outputAWithoutB = outputAWithoutB; localInfo->outputMatches = outputMatches; local = SetWord(localInfo); return 0; case REQUEST: localInfo = (CcSetOperation*)local.addr; result = SetWord(localInfo->NextResultTuple()); return result.addr != 0 ? YIELD : CANCEL; case CLOSE: localInfo = (CcSetOperation*)local.addr; if(localInfo) { delete localInfo; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of Operator ~mergesec~ */ const string CcMergeSecSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple ((x1 t1) ... " "(xn tn)))) stream (mtuple ((x1 t1) ... (xn tn)" ")))) -> (stream (mtuple ((x1 t1) ... (xn tn))))" "" "_ _ mergesec" "Computes the intersection of two sorted " "streams." "query twenty feed oddtwenty feed mergesec" " consume" ") )"; /* 4.1.3 Definition of Operator ~mergesec~ */ Operator ccrelmergesec( "mergesec", // name CcMergeSecSpec, // specification CcSetOpValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcSetOpTypeMap<0> // type mapping ); /* 4.1.3 Specification of Operator ~mergediff~ */ const string CcMergeDiffSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple ((x1 t1) ... (xn tn)" "))) stream (mtuple ((x1 t1) ... (xn tn))))) ->" " (stream (mtuple ((x1 t1) ... (xn tn))))" "" "_ _ mergediff" "Computes the difference of two sorted " "streams." "query twenty feed oddtwenty feed" " mergediff consume" ") )"; /* 4.1.3 Definition of Operator ~mergediff~ */ Operator ccrelmergediff( "mergediff", // name CcMergeDiffSpec, // specification CcSetOpValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcSetOpTypeMap<1> // type mapping ); /* 4.1.3 Specification of Operator ~mergeunion~ */ const string CcMergeUnionSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple ((x1 t1) ... (xn tn))))" "stream (mtuple ((x1 t1) ... (xn tn))))) -> (stream" " (mtuple ((x1 t1) ... (xn tn))))" "_ _ mergeunion" "Computes the union of two sorted streams." "" "query twenty feed oddtwenty feed " "mergeunion consume" ") )"; /* 4.1.3 Definition of Operator ~mergeunion~ */ Operator ccrelmergeunion( "mergeunion", // name CcMergeUnionSpec, // specification CcSetOpValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcSetOpTypeMap<2> // type mapping ); /* 7.3 Operator ~mergejoin~ This operator computes the equijoin two streams. 7.3.1 Type mapping function of operators ~mergejoin~ and ~hashjoin~ Type mapping for ~mergejoin~ is ---- ((stream (mtuple ((x1 t1) ... (xn tn)))) (stream (mtuple ((y1 d1) ... (ym dm)))) xi yj) -> (stream (mtuple ((x1 t1) ... (xn tn) (y1 d1) ... (ym dm)))) APPEND (i j) ---- Type mapping for ~hashjoin~ is ---- ((stream (mtuple ((x1 t1) ... (xn tn)))) (stream (mtuple ((y1 d1) ... (ym dm)))) xi yj int) -> (stream (mtuple ((x1 t1) ... (xn tn) (y1 d1) ... (ym dm)))) APPEND (i j) ---- */ const char* ccJoinErrorMessages[] = { "Incorrect input for operator mergejoin.", "Incorrect input for operator sortmergejoin.", "Incorrect input for operator hashjoin." }; template ListExpr CcJoinTypeMap(ListExpr args) { ListExpr attrTypeA, attrTypeB; ListExpr streamA, streamB, list, list1, list2, outlist; if (nl->ListLength(args) == (expectIntArgument ? 5 : 4)) { streamA = nl->First(args); streamB = nl->Second(args); if (nl->ListLength(streamA) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(streamA)) == mstream) { if (nl->ListLength(nl->Second(streamA)) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(nl->Second(streamA))) == mtuple) { list1 = nl->Second(nl->Second(streamA)); } else goto typeerror; } else goto typeerror; } else goto typeerror; } else goto typeerror; if (nl->ListLength(streamB) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(streamB)) == mstream) { if (nl->ListLength(nl->Second(streamB)) == 2) { if (CcTypeOfRelAlgSymbol(nl->First(nl->Second(streamB))) == mtuple) { list2 = nl->Second(nl->Second(streamB)); } else goto typeerror; } else goto typeerror; } else goto typeerror; } else goto typeerror; if(!CcAttributesAreDisjoint(list1, list2)) { goto typeerror; } list = CcConcatLists(list1, list2); outlist = nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList(nl->SymbolAtom(CcTuple::BasicType()), list)); ListExpr joinAttrDescription; if(!(nl->IsAtom(nl->Third(args)) && nl->IsAtom(nl->Fourth(args)) && nl->AtomType(nl->Third(args)) == SymbolType && nl->AtomType(nl->Fourth(args)) == SymbolType)) { goto typeerror; } string attrAName = nl->SymbolValue(nl->Third(args)); string attrBName = nl->SymbolValue(nl->Fourth(args)); int attrAIndex = CcFindAttribute(nl->Second(nl->Second(streamA)), attrAName, attrTypeA, nl); int attrBIndex = CcFindAttribute(nl->Second(nl->Second(streamB)), attrBName, attrTypeB, nl); if(attrAIndex <= 0 || attrBIndex <= 0 || !nl->Equal(attrTypeA, attrTypeB)) { goto typeerror; } if(expectIntArgument && nl->SymbolValue(nl->Fifth(args)) != CcInt::BasicType()) { goto typeerror; } joinAttrDescription = nl->TwoElemList(nl->IntAtom(attrAIndex), nl->IntAtom(attrBIndex)); return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()), joinAttrDescription, outlist); } else goto typeerror; typeerror: ErrorReporter::ReportError(ccJoinErrorMessages[errorMessageIdx]); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Auxiliary definitions for value mapping function of operator ~mergejoin~ */ static CcInt oneCcInt(true, 1); static CcBool trueCcBool(true, true); CPUTimeMeasurer ccMergeMeasurer; class CcMergeJoinLocalInfo { private: vector bucketA; vector bucketB; deque resultBucket; Word aResult; Word bResult; Word streamALocalInfo; Word streamBLocalInfo; Word streamA; Word streamB; ArgVector aArgs; ArgVector bArgs; int attrIndexA; int attrIndexB; bool expectSorted; int CompareCcTuples(CcTuple* a, CcTuple* b) { /* tuples with NULL-Values in the join attributes are never matched with other tuples. */ if(!((Attribute*)a->Get(attrIndexA))->IsDefined()) { return -1; } if(!((Attribute*)b->Get(attrIndexB))->IsDefined()) { return 1; } return ((Attribute*)a->Get(attrIndexA))-> Compare((Attribute*)b->Get(attrIndexB)); } void SetArgs(ArgVector& args, Word stream, Word attrIndex) { args[0] = SetWord(stream.addr); args[2] = SetWord(&oneCcInt); args[3] = SetWord(attrIndex.addr); args[4] = SetWord(&trueCcBool); } CcTuple* nextATuple() { bool yield; if(expectSorted) { qp->Request(streamA.addr, aResult); yield = qp->Received(streamA.addr); } else { int errorCode = CcSortBy(aArgs, aResult, REQUEST, streamALocalInfo, 0); yield = (errorCode == YIELD); } if(yield) { return (CcTuple*)aResult.addr; } else { aResult = SetWord((void*)0); return 0; } } CcTuple* nextBTuple() { bool yield; if(expectSorted) { qp->Request(streamB.addr, bResult); yield = qp->Received(streamB.addr); } else { int errorCode = CcSortBy(bArgs, bResult, REQUEST, streamBLocalInfo, 0); yield = (errorCode == YIELD); } if(yield) { return (CcTuple*)bResult.addr; } else { bResult = SetWord((void*)0); return 0; } } bool FetchNextMatch() { CcTuple* aCcTuple = (CcTuple*)aResult.addr; CcTuple* bCcTuple = (CcTuple*)bResult.addr; if(aCcTuple == 0 || bCcTuple == 0) { if(aCcTuple != 0) aCcTuple->DeleteIfAllowed(); if(bCcTuple != 0) bCcTuple->DeleteIfAllowed(); return false; } else { int cmpResult = CompareCcTuples((CcTuple*)aResult.addr, (CcTuple*)bResult.addr); while(cmpResult != 0) { if(cmpResult < 0) { ((CcTuple*)aResult.addr)->DeleteIfAllowed(); if(nextATuple() == 0) { ((CcTuple*)bResult.addr)->DeleteIfAllowed(); return false; } } else { ((CcTuple*)bResult.addr)->DeleteIfAllowed(); if(nextBTuple() == 0) { ((CcTuple*)aResult.addr)->DeleteIfAllowed(); return false; } } cmpResult = CompareCcTuples((CcTuple*)aResult.addr, (CcTuple*)bResult.addr); } return true; } } void ComputeProductOfBuckets() { assert(!bucketA.empty()); assert(!bucketB.empty()); vector::iterator iterA = bucketA.begin(); vector::iterator iterB = bucketB.begin(); for(; iterA != bucketA.end(); iterA++) { for(iterB = bucketB.begin(); iterB != bucketB.end(); iterB++) { CcTuple* resultTuple = new CcTuple; resultTuple->SetFree(true); Word resultWord = SetWord(resultTuple); Word aWord = SetWord(*iterA); Word bWord = SetWord(*iterB); CcConcat(aWord, bWord, resultWord); resultBucket.push_back(resultTuple); } } } void ClearBuckets() { vector::iterator iterA = bucketA.begin(); vector::iterator iterB = bucketB.begin(); for(; iterA != bucketA.end(); iterA++) { (*iterA)->DeleteIfAllowed(); } for(; iterB != bucketB.end(); iterB++) { (*iterB)->DeleteIfAllowed(); } bucketA.clear(); bucketB.clear(); } void FillResultBucket() { assert((CcTuple*)aResult.addr != 0); assert((CcTuple*)bResult.addr != 0); CcTuple* aMatch = (CcTuple*)aResult.addr; CcTuple* bMatch = (CcTuple*)bResult.addr; assert(CompareCcTuples(aMatch, bMatch) == 0); CcTuple* currentA = aMatch; CcTuple* currentB = bMatch; while(currentA != 0 && CompareCcTuples(currentA, bMatch) == 0) { bucketA.push_back(currentA); currentA = nextATuple(); } while(currentB != 0 && CompareCcTuples(aMatch, currentB) == 0) { bucketB.push_back(currentB); currentB = nextBTuple(); } ComputeProductOfBuckets(); ClearBuckets(); } public: CcMergeJoinLocalInfo(Word streamA, Word attrIndexA, Word streamB, Word attrIndexB, bool expectSorted) { assert(streamA.addr != 0); assert(streamB.addr != 0); assert(attrIndexA.addr != 0); assert(attrIndexB.addr != 0); assert(((CcInt*)attrIndexA.addr)->GetIntval() > 0); assert(((CcInt*)attrIndexB.addr)->GetIntval() > 0); aResult = SetWord(Address(0)); bResult = SetWord(Address(0)); this->expectSorted = expectSorted; this->streamA = streamA; this->streamB = streamB; this->attrIndexA = ((CcInt*)attrIndexA.addr)->GetIntval() - 1; this->attrIndexB = ((CcInt*)attrIndexB.addr)->GetIntval() - 1; if(expectSorted) { qp->Open(streamA.addr); qp->Open(streamB.addr); } else { SetArgs(aArgs, streamA, attrIndexA); SetArgs(bArgs, streamB, attrIndexB); CcSortBy(aArgs, aResult, OPEN, streamALocalInfo, 0); CcSortBy(bArgs, bResult, OPEN, streamBLocalInfo, 0); } nextATuple(); nextBTuple(); } ~CcMergeJoinLocalInfo() { if(expectSorted) { qp->Close(streamA.addr); qp->Close(streamB.addr); } else { CcSortBy(aArgs, aResult, CLOSE, streamALocalInfo, 0); CcSortBy(bArgs, bResult, CLOSE, streamBLocalInfo, 0); }; } CcTuple* NextResultTuple() { if(resultBucket.empty()) { if(FetchNextMatch()) { FillResultBucket(); } else { return 0; } } CcTuple* next = resultBucket.front(); resultBucket.pop_front(); return next; } }; /* 4.1.2 Value mapping function of operator ~mergejoin~ */ template int CcMergeJoin(Word* args, Word& result, int message, Word& local, Supplier s) { CcMergeJoinLocalInfo* localInfo; switch(message) { case OPEN: localInfo = new CcMergeJoinLocalInfo (args[0], args[4], args[1], args[5], expectSorted); local = SetWord(localInfo); return 0; case REQUEST: ccMergeMeasurer.Enter(); localInfo = (CcMergeJoinLocalInfo*)local.addr; result = SetWord(localInfo->NextResultTuple()); ccMergeMeasurer.Exit(); return result.addr != 0 ? YIELD : CANCEL; case CLOSE: ccMergeMeasurer.PrintCPUTimeAndReset("CPU Time for Merging Tuples : "); localInfo = (CcMergeJoinLocalInfo*)local.addr; if(localInfo) { delete localInfo; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of operator ~mergejoin~ */ const string CcMergeJoinSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple ((x1 t1) ... " "(xn tn)))) (stream (mtuple ((y1 d1) ... " "(ym dm)))) xi yj) -> (stream (mtuple ((x1 t1)" " ... (xn tn) (y1 d1) ... (ym dm))))" "" "_ _ mergejoin [_, _]" "Computes the equijoin two streams. " "Expects that input streams are sorted." "" "query duplicates feed ten feed " "rename[A] mergejoin[no, no_A] sort rdup " "consume" ") )"; /* 4.1.3 Definition of operator ~mergejoin~ */ Operator ccrelmergejoin ( "mergejoin", // name CcMergeJoinSpec, // specification CcMergeJoin, // value mapping Operator::SimpleSelect, // trivial selection function CcJoinTypeMap // type mapping ); /* 7.3 Operator ~sortmergejoin~ This operator sorts two input streams and computes their equijoin. 4.1.3 Specification of operator ~sortmergejoin~ */ const string CcSortMergeJoinSpec = "( ( \"Signature\" \"Syntax\" " "\"Meaning\" \"Example\" ) " "( ((stream (mtuple ((x1 t1) ... " "(xn tn)))) (stream (mtuple ((y1 d1) ..." " (ym dm)))) xi yj) -> (stream (mtuple " "((x1 t1) ... (xn tn) (y1 d1) ... (ym dm)" ")))" "_ _ sortmergejoin [ _ , _ ]" "" "Computes the equijoin two streams." "" "query duplicates feed ten feed " "mergejoin[no, nr] consume" ") )"; /* 4.1.3 Definition of operator ~sortmergejoin~ */ Operator ccrelsortmergejoin ( "sortmergejoin", // name CcSortMergeJoinSpec, // specification CcMergeJoin, // value mapping Operator::SimpleSelect, // trivial selection function CcJoinTypeMap // type mapping ); /* 7.3 Operator ~hashjoin~ This operator computes the equijoin two streams via a hash join. The user can specify the number of hash buckets. 7.3.1 Auxiliary Class for Operator ~hashjoin~ */ CPUTimeMeasurer ccHashMeasurer; // measures cost of distributing into buckets and // of computing products of buckets CPUTimeMeasurer ccBucketMeasurer; // measures the cost of producing the tuples in // the result set class CcHashJoinLocalInfo { private: static const size_t MAX_BUCKETS = 6151; static const size_t MIN_BUCKETS = 1; static const size_t DEFAULT_BUCKETS = 97; size_t nBuckets; size_t currentBucket; int attrIndexA; int attrIndexB; Word streamA; Word streamB; vector > bucketsA; vector > bucketsB; vector resultBucket; int CompareCcTuples(CcTuple* a, CcTuple* b) { /* tuples with NULL-Values in the join attributes are never matched with other tuples. */ if(!((Attribute*)a->Get(attrIndexA))->IsDefined()) { return -1; } if(!((Attribute*)b->Get(attrIndexB))->IsDefined()) { return 1; } return ((Attribute*)a->Get(attrIndexA))-> Compare((Attribute*)b->Get(attrIndexB)); } size_t HashTuple(CcTuple* tuple, int attrIndex) { return (((Attribute*)tuple->Get(attrIndex))-> HashValue() % nBuckets); } void FillHashBuckets(Word stream, int attrIndex, vector >& buckets) { Word tupleWord; qp->Open(stream.addr); qp->Request(stream.addr, tupleWord); while(qp->Received(stream.addr)) { ccHashMeasurer.Enter(); CcTuple* tuple = (CcTuple*)tupleWord.addr; buckets[HashTuple(tuple, attrIndex)].push_back(tuple); ccHashMeasurer.Exit(); qp->Request(stream.addr, tupleWord); } qp->Close(stream.addr); } void ClearBucket(vector& bucket) { vector::iterator iter = bucket.begin(); while(iter != bucket.end()) { (*iter)->DeleteIfAllowed(); iter++; } } bool FillResultBucket() { ccHashMeasurer.Enter(); while(resultBucket.empty() && currentBucket < nBuckets) { vector& a = bucketsA[currentBucket]; vector& b = bucketsB[currentBucket]; vector::iterator iterA = a.begin(); vector::iterator iterB; for(; iterA != a.end(); iterA++) { for(iterB = b.begin(); iterB != b.end(); iterB++) { if(CompareCcTuples(*iterA, *iterB) == 0) { ccHashMeasurer.Exit(); ccBucketMeasurer.Enter(); CcTuple* resultTuple = new CcTuple; resultTuple->SetFree(true); Word resultWord = SetWord(resultTuple); Word aWord = SetWord(*iterA); Word bWord = SetWord(*iterB); CcConcat(aWord, bWord, resultWord); resultBucket.push_back(resultTuple); ccBucketMeasurer.Exit(); ccHashMeasurer.Enter(); }; } } ClearBucket(a); ClearBucket(b); currentBucket++; } ccHashMeasurer.Exit(); return !resultBucket.empty(); }; public: CcHashJoinLocalInfo(Word streamA, Word attrIndexAWord, Word streamB, Word attrIndexBWord, Word nBucketsWord) { this->streamA = streamA; this->streamB = streamB; currentBucket = 0; attrIndexA = ((CcInt*)attrIndexAWord.addr)->GetIntval() - 1; attrIndexB = ((CcInt*)attrIndexBWord.addr)->GetIntval() - 1; nBuckets = ((CcInt*)nBucketsWord.addr)->GetIntval(); if(nBuckets < MIN_BUCKETS) { nBuckets = MIN_BUCKETS; } else if(nBuckets > MAX_BUCKETS) { nBuckets = MAX_BUCKETS; } ccHashMeasurer.Enter(); bucketsA.resize(nBuckets); bucketsB.resize(nBuckets); ccHashMeasurer.Exit(); FillHashBuckets(streamA, attrIndexA, bucketsA); FillHashBuckets(streamB, attrIndexB, bucketsB); } ~CcHashJoinLocalInfo() { } CcTuple* NextResultTuple() { if(resultBucket.empty()) { if(!FillResultBucket()) { return 0; } } CcTuple* result = resultBucket.back(); resultBucket.pop_back(); return result; } }; /* 7.3.2 Value Mapping Function of Operator ~hashjoin~ */ static int CcHashJoin(Word* args, Word& result, int message, Word& local, Supplier s) { CcHashJoinLocalInfo* localInfo; switch(message) { case OPEN: localInfo = new CcHashJoinLocalInfo(args[0], args[5], args[1], args[6], args[4]); local = SetWord(localInfo); return 0; case REQUEST: localInfo = (CcHashJoinLocalInfo*)local.addr; result = SetWord(localInfo->NextResultTuple()); return result.addr != 0 ? YIELD : CANCEL; case CLOSE: ccHashMeasurer.PrintCPUTimeAndReset("CPU Time for Hashing Tuples : "); ccBucketMeasurer.PrintCPUTimeAndReset( "CPU Time for Computing Products of Buckets : "); localInfo = (CcHashJoinLocalInfo*)local.addr; if(localInfo) { delete localInfo; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of Operator ~hashjoin~ */ const string CcHashJoinSpec = "( ( \"Signature\" \"Syntax\" " "\"Meaning\" \"Example\" ) " "( ((stream (mtuple ((x1 t1) ... " "(xn tn)))) (stream (mtuple ((y1 d1) ... " "(ym dm)))) xi yj nbuckets) -> (stream " "(mtuple ((x1 t1) ... (xn tn) (y1 d1) ..." " (ym dm))))" " _ _ hashjoin [ _ , _ , _ ]" "" "Computes the equijoin two streams " "via a hash join. The number of hash buckets" " is given by the parameter nBuckets." "" "query Employee feed Dept feed " "rename[A] hashjoin[DeptNr, DeptNr_A, 17] " "sort consume" ") )"; /* 4.1.3 Definition of Operator ~hashjoin~ */ Operator ccrelhashjoin ( "hashjoin", // name CcHashJoinSpec, // specification CcHashJoin, // value mapping Operator::SimpleSelect, // trivial selection function CcJoinTypeMap // type mapping ); /* 7.3 Operator ~extend~ Extends each input tuple by new attributes as specified in the parameter list. 7.3.1 Type mapping function of operator ~extend~ Type mapping for ~extend~ is ---- ((stream x) ((b1 (map x y1)) ... (bm (map x ym)))) -> (stream (mtuple ((a1 x1) ... (an xn) (b1 y1 ... bm ym)))) where x = (mtuple ((a1 x1) ... (an xn))) ---- */ static ListExpr CcExtendTypeMap( ListExpr args ) { ListExpr first, second, rest, listn, errorInfo, lastlistn, first2, second2, firstr, outlist; bool loopok; AlgebraManager* algMgr; algMgr = SecondoSystem::GetAlgebraManager(); errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR")); if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (!nl->IsAtom(second)) && (nl->ListLength(second) > 0)) { rest = nl->Second(nl->Second(first)); listn = nl->OneElemList(nl->First(rest)); lastlistn = listn; rest = nl->Rest(rest); while (!(nl->IsEmpty(rest))) { lastlistn = nl->Append(lastlistn,nl->First(rest)); rest = nl->Rest(rest); } loopok = true; rest = second; while (!(nl->IsEmpty(rest))) { firstr = nl->First(rest); rest = nl->Rest(rest); first2 = nl->First(firstr); second2 = nl->Second(firstr); if ((nl->IsAtom(first2)) && (nl->ListLength(second2) == 3) && (nl->AtomType(first2) == SymbolType) && (CcTypeOfRelAlgSymbol(nl->First(second2)) == mmap) && (algMgr->CheckKind(Kind::DATA(), nl->Third(second2), errorInfo)) && (nl->Equal(nl->Second(first),nl->Second(second2)))) { lastlistn = nl->Append(lastlistn, (nl->TwoElemList(first2,nl->Third(second2)))); } else { loopok = false; } } if ((loopok) && (CcCompareNames(listn))) { outlist = nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList(nl->SymbolAtom(CcTuple::BasicType()),listn)); return outlist; } else { ErrorReporter::ReportError("Incorrect input for operator extend."); return nl->SymbolAtom(Symbol::TYPEERROR()); } } else { ErrorReporter::ReportError("Incorrect input for operator extend."); return nl->SymbolAtom(Symbol::TYPEERROR()); } } ErrorReporter::ReportError("Incorrect input for operator extend."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~extend~ */ static int CcExtend(Word* args, Word& result, int message, Word& local, Supplier s) { Word t, value; CcTuple* tup; Supplier supplier, supplier2, supplier3; int noofoldattrs, nooffun, noofsons; ArgVectorPointer funargs; switch (message) { case OPEN : qp->Open(args[0].addr); return 0; case REQUEST : qp->Request(args[0].addr,t); if (qp->Received(args[0].addr)) { tup = (CcTuple*)t.addr; tup = tup->CloneIfNecessary(); noofoldattrs = tup->GetNoAttrs(); supplier = args[1].addr; nooffun = qp->GetNoSons(supplier); for (int i=0; i < nooffun;i++) { supplier2 = qp->GetSupplier(supplier, i); noofsons = qp->GetNoSons(supplier2); supplier3 = qp->GetSupplier(supplier2, 1); funargs = qp->Argument(supplier3); (*funargs)[0] = SetWord(tup); qp->Request(supplier3,value); tup->Put(noofoldattrs+i,((Attribute*)value.addr)->Clone()); } tup->SetNoAttrs(noofoldattrs + nooffun); result = SetWord(tup); return YIELD; } else return CANCEL; case CLOSE : qp->Close(args[0].addr); return 0; } return 0; } /* 4.1.3 Specification of operator ~extend~ */ const string CcExtendSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( (stream(mtuple(x)) x [(a1, (mtuple(x)" " -> d1)) ... (an, (mtuple(x) -> dn))] -> " "stream(mtuple(x@[a1:d1, ... , an:dn])))" "" "_ extend [funlist]" "Extends each input tuple by new " "attributes as specified in the parameter" " list." "query ten feed extend [mult5 : " ".nr * 5, mod2 : .nr mod 2] consume" "" ") )"; /* 4.1.3 Definition of operator ~extend~ */ Operator ccrelextend ( "extend", // name CcExtendSpec, // specification CcExtend, // value mapping Operator::SimpleSelect, // trivial selection function CcExtendTypeMap // type mapping ); /* 7.3 Operator ~loopjoin~ This operator will fulfill a join of two relations. Tuples in the cartesian product which satisfy certain conditions are passed on to the output stream. For instance, ---- query Staedte feed loopjoin [plz feed filter [.Ort=.SName] ] consume; (query (consume (loopjoin (feed tryrel) (fun (t1 TUPLE) (filter (feed null) (fun t2 TUPLE) (= (attr t1 name) (attr t2 pname))))))) ---- 7.3.1 Type mapping function of operator ~loopjoin~ The type mapping function of the loopjoin operation is as follows: ---- ((stream (mtuple x)) (map (mtuple x) (stream (mtuple y)))) -> (stream (mtuple x * y)) where x = ((x1 t1) ... (xn tn)) and y = ((y1 d1) ... (ym dm)) ---- */ ListExpr CcLoopjoinTypeMap(ListExpr args) { ListExpr first, second; ListExpr list1, list2, list, outlist; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if( (nl->ListLength(first) == 2) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (nl->ListLength(nl->Second(first)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && (nl->ListLength(second) == 3) && (CcTypeOfRelAlgSymbol(nl->First(second)) == mmap) && (nl->Equal(nl->Second(first), nl->Second(second))) && (nl->ListLength(nl->Third(second)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Third(second))) == mstream) && (nl->ListLength(nl->Second(nl->Third(second))) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(nl->Third(second)))) == mtuple) ) { list1 = nl->Second(nl->Second(first)); list2 = nl->Second(nl->Second(nl->Third(second))); if(!CcAttributesAreDisjoint(list1, list2)) { goto typeerror; } list = CcConcatLists(list1, list2); outlist = nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList(nl->SymbolAtom(CcTuple::BasicType()), list)); return outlist; } else goto typeerror; } else goto typeerror; typeerror: ErrorReporter::ReportError("Incorrect input for operator loopjoin."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~loopjoin~ */ struct CcLoopjoinLocalInfo { Word tuplex; Word streamy; }; static int CcLoopjoin(Word* args, Word& result, int message, Word& local, Supplier s) { ArgVectorPointer funargs; Word tuplex, tupley, tuplexy, streamy; CcTuple* ctuplex; CcTuple* ctupley; CcTuple* ctuplexy; CcLoopjoinLocalInfo *localinfo; switch ( message ) { case OPEN: //1.open the stream and initiate the variables qp->Open (args[0].addr); qp->Request(args[0].addr, tuplex); if (qp->Received(args[0].addr)) { //2>>> here: compute the rely which corresponding to tuplex funargs = qp->Argument(args[1].addr); (*funargs)[0] = tuplex; streamy=args[1]; qp->Open (streamy.addr); //3>>> here: put the information of tuplex and rely into local localinfo=new CcLoopjoinLocalInfo; localinfo->tuplex=tuplex; localinfo->streamy=streamy; local = SetWord(localinfo); } else { local = SetWord(Address(0)); } return 0; case REQUEST: if (local.addr ==0) return CANCEL; //1>>>>>>restore localinformation from the local variable. localinfo=(CcLoopjoinLocalInfo *) local.addr; tuplex=localinfo->tuplex; ctuplex=(CcTuple*)tuplex.addr; streamy=localinfo->streamy; //2>>>>>> prepare tuplex and tupley for processing. //if rely is exausted: fetch next tuplex. tupley=SetWord(Address(0)); while (tupley.addr==0) { qp->Request(streamy.addr, tupley); if (!(qp->Received(streamy.addr))) { qp->Close(streamy.addr); ((CcTuple*)tuplex.addr)->DeleteIfAllowed(); qp->Request(args[0].addr, tuplex); if (qp->Received(args[0].addr)) { funargs = qp->Argument(args[1].addr); ctuplex=(CcTuple*)tuplex.addr; (*funargs)[0] = tuplex; streamy=args[1]; qp->Open (streamy.addr); tupley=SetWord(Address(0)); localinfo->tuplex=tuplex; localinfo->streamy=streamy; local = SetWord(localinfo); } else return CANCEL; } else { ctupley=(CcTuple*)tupley.addr; } } //3>>>>>> compute tuplexy. ctuplexy = new CcTuple(); ctuplexy->SetFree(true); tuplexy = SetWord(ctuplexy); CcConcat(tuplex, tupley, tuplexy); ((CcTuple*)tupley.addr)->DeleteIfAllowed(); result = tuplexy; return YIELD; case CLOSE: qp->Close(args[0].addr); localinfo=(CcLoopjoinLocalInfo *) local.addr; if(localinfo) { delete localinfo; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of operator ~loopjoin~ */ const string CcLoopjoinSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream mtuple1) (map mtuple1 " "mrel(mtuple2))) -> (stream mtuple1*mtuple2)" "" "_ loopjoin [ fun ]" "Only tuples in the cartesian product " "which satisfy certain conditions are passed on" " to the output stream." "query cities feed loopjoin [five feed " "filter [.no > 2]] consume" ") )"; /* 4.1.3 Definition of operator ~loopjoin~ */ Operator ccrelloopjoin ( "loopjoin", // name CcLoopjoinSpec, // specification CcLoopjoin, // value mapping Operator::SimpleSelect, // trivial selection function CcLoopjoinTypeMap // type mapping ); /* 7.4 Operator ~loopselect~ This operator is similar to the ~loopjoin~ operator except that it only returns the inner tuple (instead of the concatination of two tuples). Tuples in the cartesian product which satisfy certain conditions are passed on to the output stream. For instance, ---- query Staedte feed loopselect [plz feed filter [.Ort=.SName] ] consume; (query (consume (loopselect (feed tryrel) (fun (t1 TUPLE) (filter (feed null) (fun t2 TUPLE) (= (attr t1 name) (attr t2 pname))))))) ---- 7.4.1 Type mapping function of operator ~loopselect~ The type mapping function of the loopjoin operation is as follows: ---- ((stream (mtuple x)) (map (mtuple x) (stream (mtuple y)))) -> (stream (mtuple y)) where x = ((x1 t1) ... (xn tn)) and y = ((y1 d1) ... (ym dm)) ---- */ ListExpr CcLoopselectTypeMap(ListExpr args) { ListExpr first, second; ListExpr list1, list2, outlist; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if( (nl->ListLength(first) == 2) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (nl->ListLength(nl->Second(first)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && (nl->ListLength(second) == 3) && (CcTypeOfRelAlgSymbol(nl->First(second)) == mmap) && (nl->Equal(nl->Second(first), nl->Second(second))) && (nl->ListLength(nl->Third(second)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Third(second))) == mstream) && (nl->ListLength(nl->Second(nl->Third(second))) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(nl->Third(second)))) == mtuple) ) { list1 = nl->Second(nl->Second(first)); list2 = nl->Second(nl->Second(nl->Third(second))); //if(!CcAttributesAreDisjoint(list1, list2)) //{ // goto typeerror; //} //list = CcConcatLists(list1, list2); outlist = nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList(nl->SymbolAtom(CcTuple::BasicType()), list2)); return outlist; } else goto typeerror; } else goto typeerror; typeerror: ErrorReporter::ReportError("Incorrect input for operator loopselect."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~loopjoin~ */ struct CcLoopselectLocalInfo { Word tuplex; Word streamy; }; static int CcLoopselect(Word* args, Word& result, int message, Word& local, Supplier s) { ArgVectorPointer funargs; Word tuplex, tupley, streamy; //tuplexy CcTuple* ctuplex; CcTuple* ctupley; //CcTuple* ctuplexy; CcLoopselectLocalInfo *localinfo; switch ( message ) { case OPEN: //1.open the stream and initiate the variables qp->Open (args[0].addr); qp->Request(args[0].addr, tuplex); if (qp->Received(args[0].addr)) { //2>>> here: compute the rely which corresponding to tuplex funargs = qp->Argument(args[1].addr); (*funargs)[0] = tuplex; streamy=args[1]; qp->Open (streamy.addr); //3>>> here: put the information of tuplex and rely into local localinfo=new CcLoopselectLocalInfo; localinfo->tuplex=tuplex; localinfo->streamy=streamy; local = SetWord(localinfo); } else { local = SetWord(Address(0)); } return 0; case REQUEST: if (local.addr ==0) return CANCEL; //1>>>>>>restore localinformation from the local variable. localinfo=(CcLoopselectLocalInfo *) local.addr; tuplex=localinfo->tuplex; ctuplex=(CcTuple*)tuplex.addr; streamy=localinfo->streamy; //2>>>>>> prepare tuplex and tupley for processing. //if rely is exausted: fetch next tuplex. tupley=SetWord(Address(0)); while (tupley.addr==0) { qp->Request(streamy.addr, tupley); if (!(qp->Received(streamy.addr))) { qp->Close(streamy.addr); ((CcTuple*)tuplex.addr)->DeleteIfAllowed(); qp->Request(args[0].addr, tuplex); if (qp->Received(args[0].addr)) { funargs = qp->Argument(args[1].addr); ctuplex=(CcTuple*)tuplex.addr; (*funargs)[0] = tuplex; streamy=args[1]; qp->Open (streamy.addr); tupley=SetWord(Address(0)); localinfo->tuplex=tuplex; localinfo->streamy=streamy; local = SetWord(localinfo); } else return CANCEL; } else { ctupley=(CcTuple*)tupley.addr; } } //3>>>>>> compute tuplexy. //ctuplexy = new CcTuple(); //ctuplexy->SetFree(true); //tuplexy = SetWord(ctuplexy); //CcConcat(tuplex, tupley, tuplexy); //((CcTuple*)tupley.addr)->DeleteIfAllowed(); result = tupley; return YIELD; case CLOSE: qp->Close(args[0].addr); localinfo=(CcLoopselectLocalInfo *) local.addr; if(localinfo) { delete localinfo; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of operator ~loopjoin~ */ const string CcLoopselectSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream mtuple1) (map mtuple1 " "mrel(mtuple2))) -> (stream mtuple2)" "" "_ loopselect [ fun ]" "Only tuples in the cartesian product " "which satisfy certain conditions are passed on" " to the output stream." "query cities feed loopselect [five feed " "filter [.no > 2]] consume" ") )"; /* 4.1.3 Definition of operator ~loopjoin~ */ Operator ccrelloopselect ( "loopsel", // name CcLoopselectSpec, // specification CcLoopselect, // value mapping Operator::SimpleSelect, // trivial selection function CcLoopselectTypeMap // type mapping ); /* 7.3 Operator ~loopjoinrel~ This operator will fulfill a join of two relations. Tuples in the cartesian product which satisfy certain conditions are passed on to the output stream. For instance, ---- query Staedte feed loopjoinrel [plz feed filter [.Ort=.SName] consume] consume; (query (consume (loopjoinrel (feed tryrel) (fun (t1 TUPLE) (consume filter (feed null) (fun t2 TUPLE) (= (attr t1 name) (attr t2 pname))))))) ---- 7.3.1 Type mapping function of operator ~loopjoinrel~ The type mapping function of the loopjoinrel operation is as follows: ---- ((stream (mtuple x)) (map (mtuple x) (mrel (mtuple y)))) -> (stream (mtuple x * y)) where x = ((x1 t1) ... (xn tn)) and y = ((y1 d1) ... (ym dm)) ---- */ ListExpr CcLoopjoinrelTypeMap(ListExpr args) { ListExpr first, second; ListExpr list1, list2, list, outlist; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if( (nl->ListLength(first) == 2) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (nl->ListLength(nl->Second(first)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && (nl->ListLength(second) == 3) && (CcTypeOfRelAlgSymbol(nl->First(second)) == mmap) && (nl->Equal(nl->Second(first), nl->Second(second))) && (nl->ListLength(nl->Third(second)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Third(second))) == mrel) && (nl->ListLength(nl->Second(nl->Third(second))) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(nl->Third(second)))) == mtuple) ) { list1 = nl->Second(nl->Second(first)); list2 = nl->Second(nl->Second(nl->Third(second))); if(!CcAttributesAreDisjoint(list1, list2)) { goto typeerror; } list = CcConcatLists(list1, list2); outlist = nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->TwoElemList(nl->SymbolAtom(CcTuple::BasicType()), list)); return outlist; } else goto typeerror; } else goto typeerror; typeerror: ErrorReporter::ReportError("Incorrect input for operator loopjoinrel."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~loopjoinrel~ */ struct CcLoopjoinrelLocalInfo { Word tuplex; Word rely; Word relyit; }; static int CcLoopjoinrel(Word* args, Word& result, int message, Word& local, Supplier s) { ArgVectorPointer funargs; Word tuplex, tupley, tuplexy, funresult, rely; CcTuple* ctuplex; CcTuple* ctupley; CcTuple* ctuplexy; CcRel* crely; CcRelIT* crelyit; CcLoopjoinrelLocalInfo *localinfo; switch ( message ) { case OPEN: //1.open the stream and initiate the variables qp->Open (args[0].addr); qp->Request(args[0].addr, tuplex); if (qp->Received(args[0].addr)) { //2>>> here: compute the rely which corresponding to tuplex funargs = qp->Argument(args[1].addr); ctuplex=(CcTuple*)tuplex.addr; (*funargs)[0] = tuplex; qp->Request(args[1].addr, funresult); rely=funresult; crely = (CcRel*)(funresult.addr); crelyit=crely->MakeNewScan(); //3>>> here: put the information of tuplex and rely into local localinfo=new CcLoopjoinrelLocalInfo; localinfo->tuplex=tuplex; localinfo->rely=rely; localinfo->relyit=SetWord(crelyit); local = SetWord(localinfo); } else { local = SetWord(Address(0)); } return 0; case REQUEST: if (local.addr ==0) return CANCEL; //1>>>>>>restore localinformation from the local variable. localinfo=(CcLoopjoinrelLocalInfo *) local.addr; tuplex=localinfo->tuplex; ctuplex=(CcTuple*)tuplex.addr; rely=localinfo->rely; crely=((CcRel*)rely.addr); crelyit=(CcRelIT*)((localinfo->relyit).addr); //2>>>>>> prepare tuplex and tupley for processing. //if rely is exausted: fetch next tuplex. tupley=SetWord(Address(0)); while (tupley.addr==0) { crely=((CcRel*)rely.addr); if (crelyit->EndOfScan()) { ((CcTuple*)tuplex.addr)->DeleteIfAllowed(); qp->Request(args[0].addr, tuplex); if (qp->Received(args[0].addr)) { funargs = qp->Argument(args[1].addr); ctuplex=(CcTuple*)tuplex.addr; (*funargs)[0] = tuplex; qp->Request(args[1].addr, funresult); rely=SetWord(funresult.addr); crely = (CcRel*)(funresult.addr); crelyit=crely->MakeNewScan(); tupley=SetWord(Address(0)); //cout<<"number of tuples in rel y:" // <<((CcRel*)rely.addr)->GetNoTuples()<tuplex=tuplex; localinfo->rely=rely; localinfo->relyit=SetWord(crelyit); local = SetWord(localinfo); } else return CANCEL; } else { tupley=SetWord(crelyit->GetTuple()); ctupley=(CcTuple*)tupley.addr; crelyit->Next(); localinfo->relyit=SetWord(crelyit); local = SetWord(localinfo); } } //3>>>>>> compute tuplexy. ctuplexy = new CcTuple(); ctuplexy->SetFree(true); tuplexy = SetWord(ctuplexy); CcConcat(tuplex, tupley, tuplexy); ((CcTuple*)tupley.addr)->DeleteIfAllowed(); result = tuplexy; return YIELD; case CLOSE: qp->Close(args[0].addr); localinfo=(CcLoopjoinrelLocalInfo *) local.addr; if(localinfo) { delete localinfo; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of operator ~loopjoinrel~ */ const string CcLoopjoinrelSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream mtuple1) (map mtuple1 mrel(mtuple2))) " "-> (stream mtuple1*mtuple2)" "_ loopjoin [ fun ]" "Only tuples in the cartesian product which " "satisfy certain conditions are passed on to the " "output stream." "query cities feed loopjoin [ five ] consume" "" " ) )"; /* 4.1.3 Definition of operator ~loopjoinrel~ */ Operator ccrelloopjoinrel ( "loopjoinrel", // name CcLoopjoinrelSpec, // specification CcLoopjoinrel, // value mapping Operator::SimpleSelect, // trivial selection function CcLoopjoinrelTypeMap // type mapping ); /* 7.3 Operator ~concat~ 7.3.1 Type mapping function of operator ~concat~ Type mapping for ~concat~ is ---- ((stream (mtuple (a1:d1 ... an:dn))) (stream (mtuple (b1:d1 ... bn:dn)))) -> (stream (mtuple (a1:d1 ... an:dn))) ---- */ ListExpr CcGetAttrTypeList (ListExpr l) { ListExpr olist = nl->TheEmptyList(); ListExpr lastolist = nl->TheEmptyList(); ListExpr attrlist = l; while (!nl->IsEmpty(attrlist)) { ListExpr first = nl->First(attrlist); attrlist = nl->Rest(attrlist); if (olist == nl->TheEmptyList()) { olist = nl->Cons(nl->Second(first), nl->TheEmptyList()); lastolist = olist; } else { lastolist = nl->Append(lastolist, nl->Second(first)); } } return olist; } static ListExpr CcConcatTypeMap( ListExpr args ) { ListExpr first, second; if(nl->ListLength(args) == 2) { first = nl->First(args); second = nl->Second(args); if((nl->ListLength(first) == 2) && (CcTypeOfRelAlgSymbol(nl->First(first)) == mstream) && (nl->ListLength(nl->Second(first)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(first))) == mtuple) && (nl->ListLength(second) == 2) && (CcTypeOfRelAlgSymbol(nl->First(second)) == mstream) && (nl->ListLength(nl->Second(second)) == 2) && (CcTypeOfRelAlgSymbol(nl->First(nl->Second(second))) == mtuple) && (nl->Equal(CcGetAttrTypeList(nl->Second(nl->Second(first))), CcGetAttrTypeList(nl->Second(nl->Second(second)))))) return first; else { ErrorReporter::ReportError("Incorrect input for operator concat."); return nl->SymbolAtom(Symbol::TYPEERROR()); } } ErrorReporter::ReportError("Incorrect input for operator concat."); return nl->SymbolAtom(Symbol::TYPEERROR()); } /* 4.1.2 Value mapping function of operator ~concat~ */ static int CcConcat(Word* args, Word& result, int message, Word& local, Supplier s) { Word t; CcTuple* tuple; switch (message) { case OPEN : qp->Open(args[0].addr); qp->Open(args[1].addr); local = SetWord(new CcInt(true, 0)); return 0; case REQUEST : if ( (((CcInt*)local.addr)->GetIntval()) == 0) { qp->Request(args[0].addr, t); if (qp->Received(args[0].addr)) { tuple = (CcTuple*)t.addr; result = SetWord(tuple); return YIELD; } else { ((CcInt*)local.addr)->Set(1); } } qp->Request(args[1].addr, t); if (qp->Received(args[1].addr)) { tuple = (CcTuple*)t.addr; result = SetWord(tuple); return YIELD; } else return CANCEL; case CLOSE : qp->Close(args[0].addr); qp->Close(args[1].addr); if(local.addr) { delete (CcInt*)local.addr; local = SetWord(Address(0)); } return 0; } return 0; } /* 4.1.3 Specification of operator ~concat~ */ const string CcConcatSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple (a1:d1 ... an:dn))) " "(stream (mtuple (b1:d1 ... bn:dn)))) -> (stream" " (mtuple (a1:d1 ... an:dn)))" "_ _ concat" "Union." "query ten feed five feed concat consume" "" ") )"; /* 4.1.3 Definition of operator ~concat~ */ Operator ccrelconcat ( "concat", // name CcConcatSpec, // specification CcConcat, // value mapping Operator::SimpleSelect, // trivial selection function CcConcatTypeMap // type mapping ); /* 7.20 Operator ~groupby~ 7.20.1 Type mapping function of operator ~groupby~ Result type of ~groupby~ operation. */ ListExpr CcGroupByTypeMap(ListExpr args) { // To avoid redundant code the type mapping is // implemented in the file ExtRelationAlgebra.cpp return GroupByTypeMap2(args, true); } /* 7.20.2 Value mapping function of operator ~groupby~ Remark this implementation causes a SIGSEGV - to be fixed! */ int CcGroupByValueMapping(Word* args, Word& result, int message, Word& local, Supplier supplier) { CcTuple *t = 0; CcTuple *s = 0; Word sWord = SetWord(Address(0)); Word relWord = SetWord(Address(0)); CcRel* tp = 0; CcRelIT* relIter = 0; int i=0, j=0, k=0; int numberatt = 0; bool ifequal = false; Word value = SetWord(Address(0)); Supplier value2; Supplier supplier1; Supplier supplier2; int ind = 0; int noOffun = 0; ArgVectorPointer vector; const int indexOfCountArgument = 3; const int startIndexOfExtraArguments = indexOfCountArgument +1; int attribIdx = 0; Word nAttributesWord = SetWord(Address(0)); Word attribIdxWord = SetWord(Address(0)); switch(message) { case OPEN: qp->Open (args[0].addr); qp->Request(args[0].addr, sWord); if (qp->Received(args[0].addr)) { local = SetWord((CcTuple*)sWord.addr); } else { local = SetWord(Address(0)); } return 0; case REQUEST: tp = new CcRel; SHOW(tp) if(local.addr == 0) { delete tp; return CANCEL; } else { t = (CcTuple*)local.addr; t = t->Clone(); t->SetFree(false); SHOW(t) SHOW(*t) tp->AppendTuple(t); } numberatt = ((CcInt*)args[indexOfCountArgument].addr)->GetIntval(); ifequal = true; qp->Request(args[0].addr, sWord); while ((qp->Received(args[0].addr)) && ifequal) { s = (CcTuple*)sWord.addr; for (k = 0; k < numberatt; k++) { attribIdx = ((CcInt*)args[startIndexOfExtraArguments+k].addr)->GetIntval(); j = attribIdx - 1; if (((Attribute*)t->Get(j))->Compare((Attribute *)s->Get(j))) ifequal = false; } if (ifequal) { s = s->Clone(); s->SetFree(false); SHOW(s) SHOW(*s) tp->AppendTuple(s); qp->Request(args[0].addr, sWord); } else local = SetWord((CcTuple*)sWord.addr); } if(ifequal) { local = SetWord(Address(0)); } t = new CcTuple; t->SetFree(true); SHOW(tp) relIter = tp->MakeNewScan(); TRACE("GetNextTuple") s = relIter->GetNextTuple(); SHOW(s) SHOW(*s) for(i = 0; i < numberatt; i++) { attribIdx = ((CcInt*)args[startIndexOfExtraArguments+i].addr)->GetIntval(); t->Put(i, ((Attribute*)s->Get(attribIdx - 1))->Clone()); } value2 = (Supplier)args[2].addr; noOffun = qp->GetNoSons(value2); t->SetNoAttrs(numberatt + noOffun); delete relIter; SHOW(noOffun) for(ind = 0; ind < noOffun; ind++) { supplier1 = qp->GetSupplier(value2, ind); supplier2 = qp->GetSupplier(supplier1, 1); vector = qp->Argument(supplier2); (*vector)[0] = SetWord(tp); qp->Request(supplier2, value); t->Put(numberatt + ind, ((Attribute*)value.addr)->Clone()) ; } SHOW(*t) result = SetWord(t); relWord = SetWord(tp); //DeleteCcRel(relWord); return YIELD; case CLOSE: qp->Close(args[0].addr); return 0; } return 0; } /* 4.1.3 Specification of operator ~groupby~ */ const string CcGroupBySpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( ((stream (mtuple (a1:d1 ... an:dn))) " "(ai1 ... aik) ((bj1 (fun (mrel (mtuple (a1:d1" " ... an:dn))) (_))) ... (bjl (fun (mrel (mtuple" " (a1:d1 ... an:dn))) (_))))) -> (stream (mtuple" " (ai1:di1 ... aik:dik bj1 ... bjl)))" "_ groupby [list; funlist]" "Groups a relation according to attributes " "ai1, ..., aik and feeds the groups to other " "functions. The results of those functions are " "appended to the grouping attributes." "query Employee feed sortby[DeptNr asc] " "groupby[DeptNr; anz : group feed count] consume" "" ") )"; /* 4.1.3 Definition of operator ~groupby~ */ Operator ccrelgroupby ( "groupby", // name CcGroupBySpec, // specification CcGroupByValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function CcGroupByTypeMap // type mapping ); /* 5 Defnition of type constructor ~mtuple~ Eventually a type constructor is created by defining an instance of class ~TypeConstructor~. Constructor's arguments are the type constructor's name and the eleven functions previously defined. */ void DummyDelete(const ListExpr, Word& w) {}; void DummyClose(const ListExpr, Word& w) {}; Word DummyClone(const ListExpr, const Word& w) { return SetWord( Address(0) ); }; int DummySizeOf() { return 0; } TypeConstructor ccreltuple( CcTuple::BasicType(), CcTupleProp, OutCcTuple, InCcTuple, SaveToListCcTuple, RestoreFromListCcTuple, CreateCcTuple, DummyDelete, 0, 0, DummyClose, DummyClone, CastCcTuple, DummySizeOf, CheckCcTuple ); /* 5 Definition of type constructor ~mrel~ Eventually a type constructor is created by defining an instance of class ~TypeConstructor~. Constructor's arguments are the type constructor's name and the eleven functions previously defined. */ TypeConstructor ccrelrel( CcRel::BasicType(), CcRelProp, OutCcRel, InCcRel, 0, 0, CreateCcRel, DummyDelete, OpenCcRel, SaveCcRel, DummyClose, DummyClone, CastCcRel, DummySizeOf, CheckCcRel ); /* 5.13 Operator ~mconsume~ Collects objects from a stream of tuples into a main memory relation using the ~mrel~ type constructor of the old relational algebra. This operator is used to convert from a relation in the persistent relational algebra to the old one. 5.6.1 Type mapping function of operator ~mconsume~ Operator ~mconsume~ accepts a stream of tuples and returns a main memory relation. ---- (stream tuple(x)) -> ( mrel mtuple(x) ) ---- */ ListExpr MConsumeTypeMap(ListExpr args) { if(nl->ListLength(args)!=1){ return listutils::typeError("one argument expected"); } ListExpr first = nl->First(args); if(!listutils::isTupleStream(nl->First(args))){ return listutils::typeError(" stream(tuple(x)) expected"); } return nl->TwoElemList( nl->SymbolAtom(CcRel::BasicType()), nl->TwoElemList( nl->SymbolAtom(CcTuple::BasicType()), nl->Second(nl->Second(first)))); } /* 5.6.2 Value mapping function of operator ~mconsume~ */ CcTuple * CloneToMemoryTuple(Tuple* t) { Counter::getRef("RA:ClonedTuples")++; CcTuple *result = new CcTuple(); result->SetNoAttrs( t->GetNoAttributes() ); for( int i = 0; i < t->GetNoAttributes(); i++ ) { Attribute *attr = t->GetAttribute( i )->Clone(); result->Put( i, attr ); } return result; } int MConsume(Word* args, Word& result, int message, Word& local, Supplier s) { Word actual; CcRel* rel; rel = (CcRel*)((qp->ResultStorage(s)).addr); if(rel->GetNoTuples() > 0) { rel->Empty(); } qp->Open(args[0].addr); qp->Request(args[0].addr, actual); while (qp->Received(args[0].addr)) { CcTuple* tuple = CloneToMemoryTuple(((Tuple*)actual.addr)); rel->AppendTuple(tuple); ((Tuple*)actual.addr)->DeleteIfAllowed(); qp->Request(args[0].addr, actual); } result = SetWord((void*) rel); qp->Close(args[0].addr); return 0; } /* 5.6.3 Specification of operator ~mconsume~ */ const string MConsumeSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" " "\"Example\" ) " "( (stream tuple(x)) -> (mrel mtuple(x))" "_ mconsume" "Collects objects from a stream into an mrel." "" "query cities feed mconsume" ") )"; /* 5.6.4 Definition of operator ~mconsume~ */ Operator relalgmconsume ( "mconsume", // name MConsumeSpec, // specification MConsume, // value mapping Operator::SimpleSelect, // trivial selection function MConsumeTypeMap // type mapping ); /* 6 Class ~OldRelationAlgebra~ A new subclass ~OldRelationAlgebra~ of class ~Algebra~ is declared. The only specialization with respect to class ~Algebra~ takes place within the constructor: all type constructors and operators are registered at the actual algebra. After declaring the new class, its only instance ~RelationAlgebra~ is defined. */ class OldRelationAlgebra : public Algebra { public: OldRelationAlgebra() : Algebra() { AddTypeConstructor( &ccreltuple ); AddTypeConstructor( &ccrelrel ); AddOperator(&ccrelfeed); AddOperator(&ccrelsample); AddOperator(&ccrelconsume); AddOperator(&ccrelTUPLE); AddOperator(&ccrelgroup); AddOperator(&ccrelTUPLE2); AddOperator(&ccrelattr); AddOperator(&ccrelfilter); AddOperator(&ccrelproject); AddOperator(&ccrelremove); AddOperator(&ccrelproduct); AddOperator(&ccrelcancel); AddOperator(&ccrelcount); AddOperator(&ccrelrename); AddOperator(&ccrelextract); AddOperator(&ccrelextend); AddOperator(&ccrelconcat); AddOperator(&ccrelmax); AddOperator(&ccrelmin); AddOperator(&ccrelavg); AddOperator(&ccrelsum); AddOperator(&ccrelhead); AddOperator(&ccrelsortby); AddOperator(&ccrelsort); AddOperator(&ccrelrdup); AddOperator(&ccrelmergesec); AddOperator(&ccrelmergediff); AddOperator(&ccrelmergeunion); AddOperator(&ccrelmergejoin); AddOperator(&ccrelloopjoin); AddOperator(&ccrelloopselect); AddOperator(&ccrelloopjoinrel); AddOperator(&ccrelsortmergejoin); AddOperator(&ccrelhashjoin); AddOperator(&ccrelgroupby); AddOperator(&relalgmconsume); ccreltuple.AssociateKind( "MTUPLE" ); ccrelrel.AssociateKind( "MREL" ); } ~OldRelationAlgebra() {}; }; } // end of namespace oldrelation /* 7 Initialization Each algebra module needs an initialization function. The algebra manager has a reference to this function if this algebra is included in the list of required algebras, thus forcing the linker to include this module. The algebra manager invokes this function to get a reference to the instance of the algebra class and to provide references to the global nested list container (used to store constructor, type, operator and object information) and to the query processor. The function has a C interface to make it possible to load the algebra dynamically at runtime. */ extern "C" Algebra* InitializeOldRelationAlgebra( NestedList* nlRef, QueryProcessor* qpRef ) { nl = nlRef; qp = qpRef; return (new oldrelation::OldRelationAlgebra()); }