16001 lines
431 KiB
C++
16001 lines
431 KiB
C++
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2004-2008, University in Hagen, Faculty of Mathematics and
|
|
Computer Science, Database Systems for New Applications.
|
|
|
|
SECONDO is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SECONDO is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
//paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}]
|
|
//paragraph [10] Footnote: [{\footnote{] [}}]
|
|
//[TOC] [\tableofcontents]
|
|
//[_] [\_]
|
|
//[x] [\ensuremath{\times}]
|
|
//[->] [\ensuremath{\rightarrow}]
|
|
//[>] [\ensuremath{>}]
|
|
//[<] [\ensuremath{<}]
|
|
|
|
|
|
|
|
|
|
[1] Implementation of Module Extended Relation Algebra
|
|
|
|
[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.
|
|
|
|
April 2002 Victor Almeida Separated the relational algebra into two algebras
|
|
called Relational Algebra and Extended Relational Algebra.
|
|
|
|
December 2005, Victor Almeida deleted the deprecated algebra levels
|
|
(~executable~, ~descriptive~, and ~hibrid~). Only the executable
|
|
level remains. Models are also removed from type constructors.
|
|
|
|
January 2006 Victor Almeida replaced the ~free~ tuples concept to
|
|
reference counters. There are reference counters on tuples and also
|
|
on attributes. Some assertions were removed, since the code is
|
|
stable.
|
|
|
|
June 2006, Corrected a bug caused by improper reference counting of tuples
|
|
observed in operator ~mergesec~.
|
|
|
|
June 2006, Christian D[ue]ntgen added operators ~symmproduct~ and
|
|
~symmproductextend~.
|
|
|
|
August 2006, Christian D[ue]ntgen added signature ((stream T) int) -> (stream T)
|
|
to operator ~head~.
|
|
|
|
January 2007, M. Spiekermann. Reference counting in groupby corrected, since it
|
|
causes a segmentation fault, when the Tuplebuffer needs to be flushed on disk.
|
|
|
|
July 2007, C. Duentgen. Changed groupbyTypeMap. It will now accept an empty
|
|
list of grouping attributes (argument 1)). The result will then be constructed
|
|
from a single group containing ALL tuples from the input stream (argument 0).
|
|
Logically, the result tuples contain no original attributes from the input
|
|
stream, but only those created by aggregation functions from argument 2.
|
|
|
|
October 2007, M. Spiekermann, T. Behr. Reimplementation of the operators ~avg~
|
|
and ~sum~ in order to provide better example code since these should be used as
|
|
examples for the practical course.
|
|
|
|
January 2008, C. D[ue]ntgen adds aggregation operator ~var~, computing the
|
|
variance on a stream.
|
|
|
|
November 2011, F. Valdes. Operator ~toFields~ added, decomposing a stream of
|
|
tuples from a relation schema into its items.
|
|
|
|
December 2011, F. Valdes. Operator ~fromFields~ added, composing a stream of
|
|
tuples into a relation schema.
|
|
|
|
[TOC]
|
|
|
|
1 Includes and defines
|
|
|
|
*/
|
|
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <sstream>
|
|
#include <stack>
|
|
#include <limits.h>
|
|
#include <set>
|
|
|
|
//#define TRACE_ON
|
|
#undef TRACE_ON
|
|
#include "LogMsg.h"
|
|
#define TRACE_OFF
|
|
|
|
#include "Algebras/Relation-C++/RelationAlgebra.h"
|
|
#include "QueryProcessor.h"
|
|
#include "AlgebraManager.h"
|
|
#include "CPUTimeMeasurer.h"
|
|
#include "StandardTypes.h"
|
|
#include "Counter.h"
|
|
#include "Algebras/TupleIdentifier/TupleIdentifier.h"
|
|
#include "Progress.h"
|
|
#include "RTuple.h"
|
|
#include "Symbols.h"
|
|
#include "ListUtils.h"
|
|
#include "Outerjoin.h"
|
|
#include "DateTime.h"
|
|
#include "Stream.h"
|
|
#include "Algebras/FText/FTextAlgebra.h"
|
|
#include "SecondoCatalog.h"
|
|
#include "Algebras/Standard-C++/LongInt.h"
|
|
|
|
#ifdef USE_PROGRESS
|
|
#include "../CostEstimation/ExtRelationAlgebraCostEstimation.h"
|
|
#endif
|
|
|
|
extern NestedList* nl;
|
|
extern QueryProcessor* qp;
|
|
extern AlgebraManager* am;
|
|
extern Operator extrelsmouterjoin;
|
|
extern Operator extrelsymmouterjoin;
|
|
|
|
using namespace listutils;
|
|
using namespace std;
|
|
|
|
|
|
namespace extrelationalg{
|
|
|
|
/*
|
|
2 Operators
|
|
|
|
2.2 Selection function for type operators
|
|
|
|
The selection function of a type operator always returns -1.
|
|
|
|
*/
|
|
int exttypeOperatorSelect(ListExpr args)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
2.3 Type Operator ~Group~
|
|
|
|
Type operators are used only for inferring argument types of parameter
|
|
functions. They have a type mapping but no evaluation function.
|
|
|
|
2.3.1 Type mapping function of operator ~group~
|
|
|
|
---- ((stream x)) -> (rel x)
|
|
----
|
|
|
|
*/
|
|
ListExpr GroupTypeMap(ListExpr args)
|
|
{
|
|
if(nl->ListLength(args)<1){
|
|
ErrorReporter::ReportError("one argument expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr first = nl->First(args);
|
|
|
|
if(!Stream<Tuple>::checkType(first)){
|
|
return listutils::typeError("tuple stream expected");
|
|
}
|
|
|
|
return nl->TwoElemList(
|
|
nl->SymbolAtom(Relation::BasicType()),
|
|
nl->Second(first));
|
|
}
|
|
/*
|
|
2.3.2 Specification of operator ~Group~
|
|
|
|
*/
|
|
const string GroupSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Remarks\" ) "
|
|
"( <text>((stream x)) -> (rel x)</text--->"
|
|
"<text>type operator</text--->"
|
|
"<text>Maps stream type to a rel.</text--->"
|
|
"<text>not for use with sos-syntax</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.3.3 Definition of operator ~group~
|
|
|
|
*/
|
|
Operator extrelgroup (
|
|
"GROUP", // name
|
|
GroupSpec, // specification
|
|
0, // no value mapping
|
|
exttypeOperatorSelect, // trivial selection function
|
|
GroupTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.4 Operator ~sample~
|
|
|
|
Produces a stream representing a sample of a relation.
|
|
|
|
2.4.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.
|
|
|
|
If the optional parameter ~subsetSize~ is set to ~true~, the optional parameter
|
|
~randSeed~ (defaults to 0) is used as the starting sequence offset for the
|
|
random
|
|
number generator. Otherwise, the offset is calculated from the current time and
|
|
will differ for each call.
|
|
|
|
*/
|
|
void
|
|
MakeRandomSubset(vector<int>& result, int subsetSize, int setSize,
|
|
bool useSeed = false, unsigned int randSeed = 1)
|
|
{
|
|
set<int> drawnNumbers;
|
|
set<int>::iterator iter;
|
|
int drawSize = 0;
|
|
int nDrawn = 0;
|
|
int i = 0;
|
|
int r = 0;
|
|
bool doInvert = false;
|
|
|
|
result.resize(0);
|
|
|
|
// The variable below defines an offset into the
|
|
// random number sequence. It will be incremented by
|
|
// the number of rand() calls. Hence subsequent calls
|
|
// will avoid to return the same sequence of numbers,
|
|
// unless useSeed is true and both calls provide the
|
|
// same randSeed.
|
|
static unsigned int randCalls = (time(0) % 1000) * 1000;
|
|
// For some experiments, one would like to get "known" samples.
|
|
// To that end, useSeed can be set to true and an explicit random seed can be
|
|
// passed.
|
|
// To allow for "real" random sequences after such a seeding, we need to
|
|
// remember the fact whether the current randCalls is due to an explicit
|
|
// seed or not (otherwise the sample-result would be random)
|
|
static bool lastWasSeeded = useSeed;
|
|
if(useSeed){
|
|
randCalls = randSeed;
|
|
lastWasSeeded = true; // time-based seed overwritten!
|
|
} else {
|
|
if(lastWasSeeded){
|
|
randCalls = (time(0) % 1000) * 1000; // re-init seed
|
|
}
|
|
lastWasSeeded = false;
|
|
}
|
|
srand(randCalls);
|
|
if(((double)setSize) / ((double)subsetSize) <= 2)
|
|
{
|
|
doInvert = true;
|
|
drawSize = setSize - subsetSize;
|
|
}
|
|
else
|
|
{
|
|
doInvert = false;
|
|
drawSize = subsetSize;
|
|
}
|
|
|
|
// Using Windows RAND_MAX is very small (about 2^15) therefore we
|
|
// need to limit the drawSize to 3/4 (to avoid long runtimes) of
|
|
// this size.
|
|
int drawMax = 3*(RAND_MAX/4);
|
|
const int randMax = RAND_MAX;
|
|
const int intMax = INT_MAX;
|
|
const int newMax = max(randMax, intMax);
|
|
static const int f = (intMax / randMax) - 1;
|
|
static long& ctr = Counter::getRef("EXT::sample:randPos");
|
|
static long& ctrMax = Counter::getRef("EXT::sample:maxDrawnRand");
|
|
|
|
|
|
if ( drawSize > drawMax )
|
|
{
|
|
drawSize = drawMax;
|
|
cerr << "Warning: Sample size reduced to 3/4*RAND_MAX." << endl;
|
|
}
|
|
|
|
TRACE("*** sample parameters ***")
|
|
SHOW(f)
|
|
SHOW(setSize)
|
|
SHOW(subsetSize)
|
|
SHOW(drawSize)
|
|
|
|
while(nDrawn < drawSize)
|
|
{
|
|
// 28.04.04 M. Spiekermann.
|
|
// The calculation of random numbers below is recommended in
|
|
// the man page documentation of the rand() function.
|
|
// Moreover, the factor f is used to retrieve values in the
|
|
// range of LONG_MAX
|
|
long nextRand = rand();
|
|
randCalls++;
|
|
if ( f > 1 )
|
|
{
|
|
nextRand += (f * rand());
|
|
randCalls++;
|
|
}
|
|
if (nextRand > ctrMax)
|
|
ctrMax = nextRand;
|
|
|
|
r = (int) ((double)(setSize + 1) * nextRand/(newMax+1.0));
|
|
|
|
if ( r != 0) {
|
|
if(drawnNumbers.find(r) == drawnNumbers.end())
|
|
{
|
|
drawnNumbers.insert(r);
|
|
++nDrawn;
|
|
}
|
|
}
|
|
}
|
|
ctr = randCalls;
|
|
SHOW(drawnNumbers.size())
|
|
|
|
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);
|
|
}
|
|
SHOW(result.size())
|
|
}
|
|
|
|
/*
|
|
2.4.2 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.
|
|
|
|
---- ((rel x) int real [int] ) -> (stream x)
|
|
----
|
|
|
|
*/
|
|
ListExpr SampleTypeMap(ListExpr args)
|
|
{
|
|
|
|
int len = nl->ListLength(args);
|
|
if(len!=3 && len!=4){
|
|
ErrorReporter::ReportError("three or four arguments expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr rel = nl->First(args);
|
|
ListExpr minSampleSize = nl->Second(args);
|
|
ListExpr minSampleRate = nl->Third(args);
|
|
|
|
if( !Relation::checkType(rel) ||
|
|
!CcInt::checkType(minSampleSize) ||
|
|
!CcReal::checkType(minSampleRate)){
|
|
ErrorReporter::ReportError("rel x int x real [ x int] expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr streamDescription =
|
|
nl->Cons(nl->SymbolAtom(Symbol::STREAM()), nl->Rest(rel));
|
|
|
|
if(len==4){
|
|
ListExpr randSeed = nl->Fourth(args);
|
|
if(!CcInt::checkType(randSeed)){
|
|
ErrorReporter::ReportError("rel x int x real [ x int] expected");
|
|
return nl->TypeError();
|
|
}
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(true)),
|
|
streamDescription);
|
|
}
|
|
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->TwoElemList(nl->IntAtom(0),
|
|
nl->BoolAtom(false)),
|
|
streamDescription);
|
|
}
|
|
/*
|
|
2.4.3 Value mapping function of operator ~sample~
|
|
|
|
*/
|
|
struct SampleLocalInfo
|
|
{
|
|
vector<int> sampleIndices;
|
|
vector<int>::iterator iter;
|
|
int lastIndex;
|
|
RandomRelationIterator* relIter;
|
|
bool useSeed;
|
|
unsigned int randSeed;
|
|
};
|
|
|
|
int Sample(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
SampleLocalInfo* localInfo = static_cast<SampleLocalInfo*>( local.addr );
|
|
|
|
Relation* rel = 0;
|
|
Tuple* tuple = 0;
|
|
|
|
int sampleSize = 0;
|
|
int relSize = 0;
|
|
float sampleRate = 0;
|
|
int i = 0;
|
|
int currentIndex = 0;
|
|
|
|
switch(message)
|
|
{
|
|
case OPEN :
|
|
localInfo = new SampleLocalInfo();
|
|
local.addr = localInfo;
|
|
|
|
localInfo->randSeed = (unsigned int)((CcInt*)(args[3].addr))->GetIntval();
|
|
localInfo->useSeed = ((CcBool*)(args[4].addr))->GetBoolval();
|
|
|
|
rel = (Relation*)args[0].addr;
|
|
relSize = rel->GetNoTuples();
|
|
localInfo->relIter = rel->MakeRandomScan();
|
|
sampleSize = StdTypes::GetInt(args[1]);
|
|
sampleRate = StdTypes::GetReal(args[2]);
|
|
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
|
|
{
|
|
MakeRandomSubset(localInfo->sampleIndices, sampleSize, relSize,
|
|
localInfo->useSeed, localInfo->randSeed);
|
|
}
|
|
|
|
localInfo->iter = localInfo->sampleIndices.begin();
|
|
localInfo->lastIndex = 0;
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
if(localInfo->iter == localInfo->sampleIndices.end())
|
|
{
|
|
return CANCEL;
|
|
}
|
|
else
|
|
{
|
|
currentIndex = *(localInfo->iter);
|
|
int step = currentIndex - localInfo->lastIndex;
|
|
if(!(tuple = localInfo->relIter->GetNextTuple(step)))
|
|
{
|
|
return CANCEL;
|
|
}
|
|
|
|
result.setAddr(tuple);
|
|
localInfo->lastIndex = *(localInfo->iter);
|
|
localInfo->iter++;
|
|
return YIELD;
|
|
}
|
|
|
|
case CLOSE :
|
|
if(local.addr){
|
|
localInfo = (SampleLocalInfo*)local.addr;
|
|
delete localInfo->relIter;
|
|
delete localInfo;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
/*
|
|
2.4.4 Specification of operator ~sample~
|
|
|
|
*/
|
|
const string SampleSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>rel(X) int x real [x int ] "
|
|
"-> (stream x)"
|
|
"</text--->"
|
|
"<text>_ sample [ Size , Fraction , Seed ]</text--->"
|
|
"<text>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 Size "
|
|
"argument, and t is Fraction. The optional fourth "
|
|
"parameter selects a seed for the used random "
|
|
"number generator."
|
|
"The sample has the same ordering as the original "
|
|
"relation. </text--->"
|
|
"<text>query cities sample[0, 0.45] count</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.4.5 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 extrelsample (
|
|
"sample", // name
|
|
SampleSpec, // specification
|
|
Sample, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SampleTypeMap // type mapping
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
2.6 Operator ~cancel~
|
|
|
|
Transmits tuple from its input stream to its output stream until a tuple
|
|
arrives fulfilling some condition.
|
|
|
|
2.6.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)
|
|
----
|
|
|
|
*/
|
|
ListExpr CancelTypeMap(ListExpr args)
|
|
{
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr fun = nl->Second(args);
|
|
|
|
string err = " stream(tuple(x)) x (tuple(x) -> bool) expected";
|
|
if(!listutils::isTupleStream(stream) ||
|
|
!listutils::isMap<1>(fun)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(!listutils::isSymbol(nl->Third(fun),CcBool::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!nl->Equal(nl->Second(stream),nl->Second(fun))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
/*
|
|
2.6.2 Value mapping function of operator ~cancel~
|
|
|
|
*/
|
|
int Cancel(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Word t, value;
|
|
Tuple* 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 = (Tuple*)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)
|
|
{
|
|
tuple->DeleteIfAllowed();
|
|
return CANCEL;
|
|
}
|
|
else
|
|
{
|
|
result.setAddr(tuple);
|
|
return YIELD;
|
|
}
|
|
}
|
|
else return CANCEL;
|
|
|
|
case CLOSE :
|
|
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
/*
|
|
2.6.3 Specification of operator ~cancel~
|
|
|
|
*/
|
|
const string CancelSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream x) (map x bool)) -> "
|
|
"(stream x)</text--->"
|
|
"<text>_ cancel [ fun ]</text--->"
|
|
"<text>Transmits tuple from its input stream "
|
|
"to its output stream until a tuple arrives "
|
|
"fulfilling some condition.</text--->"
|
|
"<text>query cities feed cancel [.cityname = "
|
|
"\"Dortmund\"] consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.6.4 Definition of operator ~cancel~
|
|
|
|
*/
|
|
Operator extrelcancel (
|
|
"cancel", // name
|
|
CancelSpec, // specification
|
|
Cancel, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
CancelTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.7 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.
|
|
|
|
2.7.1 Type mapping function of operator ~extract~
|
|
|
|
Type mapping for ~extract~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) xi) -> ti
|
|
APPEND (i) ti)
|
|
----
|
|
|
|
*/
|
|
ListExpr ExtractTypeMap( ListExpr args )
|
|
{
|
|
|
|
|
|
string err = "(stream( tuple[ a1 : t1, .., an : tn ])) x a_i"
|
|
" or stream(DATA) expected";
|
|
if(!nl->HasLength(args,2) && !nl->HasLength(args,1)){
|
|
return listutils::typeError("one or two arguments expected");
|
|
}
|
|
|
|
if(nl->HasLength(args,1)){ // DATA stream version
|
|
if(!Stream<Attribute>::checkType(nl->First(args))){
|
|
return listutils::typeError("if called with one arg, "
|
|
"this arg must be stream(DATA)");
|
|
}
|
|
return nl->Second(nl->First(args));
|
|
}
|
|
|
|
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr attrname = nl->Second(args);
|
|
|
|
if(!listutils::isTupleStream(stream) ||
|
|
nl->AtomType(attrname)!=SymbolType){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
string attr = nl->SymbolValue(attrname);
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
ListExpr attrType;
|
|
int j = listutils::findAttribute(attrList, attr, attrType);
|
|
if (j) {
|
|
if (nl->ListLength(attrType) == 2 && nl->SymbolValue
|
|
(nl->First(attrType)) == "arel"){
|
|
ErrorReporter::ReportError("Standard extract is not defined for arel");
|
|
return nl->TypeError();
|
|
}
|
|
else{
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(nl->IntAtom(j)), attrType);
|
|
}
|
|
} else {
|
|
return listutils::typeError("Attribute name " + attr +
|
|
" not known in the tuple");
|
|
}
|
|
}
|
|
/*
|
|
2.7.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.
|
|
|
|
*/
|
|
int Extract(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
|
|
if(qp->GetNoSons(s)>1){
|
|
int index;
|
|
Attribute* res = (Attribute*)((qp->ResultStorage(s)).addr);
|
|
result.setAddr(res);
|
|
Stream<Tuple> stream(args[0]);
|
|
stream.open();
|
|
Tuple* tuple = stream.request();
|
|
|
|
if(tuple) {
|
|
index = ((CcInt*)args[2].addr)->GetIntval();
|
|
res->CopyFrom(
|
|
(const Attribute*)tuple->GetAttribute(index - 1));
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
else
|
|
{
|
|
res->SetDefined(false);
|
|
}
|
|
|
|
stream.close();
|
|
return 0;
|
|
} else { // attribute stream version
|
|
Stream<Attribute> stream(args[0]);
|
|
result = qp->ResultStorage(s);
|
|
Attribute* res = (Attribute*)(result.addr);
|
|
stream.open();
|
|
Attribute* a = stream.request();
|
|
if(a){
|
|
res->CopyFrom(a);
|
|
a->DeleteIfAllowed();
|
|
} else {
|
|
res->SetDefined(false);
|
|
}
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
}
|
|
/*
|
|
2.7.3 Specification of operator ~extract~
|
|
|
|
*/
|
|
const string ExtractSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn]"
|
|
"))) x ai) -> di || stream<X> -> X , X in DATA</text--->"
|
|
"<text>_ extract [ _ ]</text--->"
|
|
"<text>Returns the value of attribute ai of "
|
|
"the first tuple in the input stream."
|
|
"</text--->"
|
|
"<text>query cities feed extract [population]"
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.7.4 Definition of operator ~extract~
|
|
|
|
*/
|
|
Operator extrelextract (
|
|
"extract", // name
|
|
ExtractSpec, // specification
|
|
Extract, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
ExtractTypeMap // type mapping
|
|
);
|
|
|
|
|
|
/*
|
|
2.7 Operator extractDef
|
|
|
|
This operator works similar to the extract operator but uses
|
|
a default value if the stream is empty instead of setting the
|
|
result to be undefined.
|
|
|
|
*/
|
|
ListExpr extractDefTM(ListExpr args){
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError("three args expected");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError("first arg is not a tuple stream");
|
|
}
|
|
if(nl->AtomType(nl->Second(args))!=SymbolType){
|
|
return listutils::typeError("Second arg is not a valid attribute name");
|
|
}
|
|
string attrName = nl->SymbolValue(nl->Second(args));
|
|
ListExpr attrType;
|
|
int index = listutils::findAttribute(nl->Second(nl->Second(nl->First(args))),
|
|
attrName, attrType);
|
|
if(!index){
|
|
return listutils::typeError("Attribute name " + attrName
|
|
+ " not part of the tuple");
|
|
}
|
|
if(!nl->Equal(nl->Third(args), attrType)){
|
|
return listutils::typeError("type of attribute and default value differ");
|
|
}
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->IntAtom(index-1)),
|
|
attrType);
|
|
}
|
|
|
|
|
|
int extractDefVM(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Stream<Tuple> stream(args[0]);
|
|
stream.open();
|
|
Tuple* t = stream.request();
|
|
stream.close();
|
|
result = qp->ResultStorage(s);
|
|
Attribute* res = (Attribute*) result.addr;
|
|
|
|
if(t){
|
|
int index = ((CcInt*) args[3].addr)->GetValue();
|
|
res->CopyFrom(t->GetAttribute(index));
|
|
t->DeleteIfAllowed();
|
|
} else {
|
|
res->CopyFrom((Attribute*) args[2].addr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
OperatorSpec extractDefSpec(
|
|
"stream(tuple) x attrName x DATA -> DATA",
|
|
" _ extractDel[_,_ ] ",
|
|
"Extract the attribute with the specified name "
|
|
"from the first tuple of the incoming stream. "
|
|
"if the stream is empty, a default value (the third "
|
|
"argument is used as the result.",
|
|
" query ten feed extactDel[No,-1]"
|
|
);
|
|
|
|
Operator extractDefOp(
|
|
"extractDef",
|
|
extractDefSpec.getStr(),
|
|
extractDefVM,
|
|
Operator::SimpleSelect,
|
|
extractDefTM
|
|
);
|
|
|
|
/*
|
|
2.8 Operator ~head~
|
|
|
|
This operator fetches the first n elements (e.g. tuples) from a stream.
|
|
|
|
2.8.1 Type mapping function of operator ~head~
|
|
|
|
Type mapping for ~head~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) int) ->
|
|
((stream (tuple ((x1 t1)...(xn tn))))
|
|
|
|
or ((stream T) int) -> (stream T) for T in kind DATA
|
|
----
|
|
|
|
*/
|
|
ListExpr HeadTypeMap( ListExpr args )
|
|
{
|
|
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
|
|
string err = " stream(tuple(...) x int or stream(DATA) x int expected";
|
|
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr count = nl->Second(args);
|
|
|
|
if(( !Stream<Tuple>::checkType(stream) &&
|
|
!Stream<Attribute>::checkType(stream) ) ||
|
|
!CcInt::checkType(count) ){
|
|
return listutils::typeError(err);
|
|
}
|
|
return stream;
|
|
}
|
|
/*
|
|
2.8.3 Value mapping function of operator ~head~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
struct HeadLocalInfo
|
|
{
|
|
HeadLocalInfo( const int maxTuples = 0 ):
|
|
numTuples( 0 ),
|
|
maxTuples( maxTuples )
|
|
{}
|
|
|
|
int numTuples;
|
|
int maxTuples;
|
|
};
|
|
|
|
int Head(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
HeadLocalInfo *localInfo;
|
|
Word tupleWord;
|
|
|
|
switch(message)
|
|
{
|
|
case OPEN:
|
|
|
|
qp->Open(args[0].addr);
|
|
localInfo =
|
|
new HeadLocalInfo( ((CcInt*)args[1].addr)->GetIntval() );
|
|
local.setAddr( localInfo );
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
|
|
localInfo = (HeadLocalInfo*)local.addr;
|
|
if(localInfo->numTuples >= localInfo->maxTuples)
|
|
{
|
|
return CANCEL;
|
|
}
|
|
|
|
qp->Request(args[0].addr, tupleWord);
|
|
if(qp->Received(args[0].addr))
|
|
{
|
|
result = tupleWord;
|
|
localInfo->numTuples++;
|
|
return YIELD;
|
|
}
|
|
else
|
|
{
|
|
return CANCEL;
|
|
}
|
|
case CLOSE:
|
|
if(local.addr)
|
|
{
|
|
localInfo = (HeadLocalInfo*)local.addr;
|
|
delete localInfo;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
struct HeadLocalInfo
|
|
{
|
|
HeadLocalInfo( const int maxTuples = 0 ):
|
|
numTuples( 0 ),
|
|
maxTuples( maxTuples )
|
|
{}
|
|
|
|
int numTuples;
|
|
int maxTuples;
|
|
};
|
|
|
|
int Head(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
HeadLocalInfo *hli;
|
|
Word tupleWord;
|
|
|
|
hli = (HeadLocalInfo*)local.addr;
|
|
|
|
switch(message)
|
|
{
|
|
case OPEN:
|
|
|
|
if ( hli ) delete hli;
|
|
|
|
hli =
|
|
new HeadLocalInfo( ((CcInt*)args[1].addr)->GetIntval() );
|
|
local.setAddr( hli );
|
|
|
|
qp->Open(args[0].addr);
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
|
|
|
|
if(hli->numTuples >= hli->maxTuples)
|
|
{
|
|
return CANCEL;
|
|
}
|
|
|
|
qp->Request(args[0].addr, tupleWord);
|
|
if(qp->Received(args[0].addr))
|
|
{
|
|
result = tupleWord;
|
|
hli->numTuples++;
|
|
return YIELD;
|
|
}
|
|
else
|
|
{
|
|
return CANCEL;
|
|
}
|
|
|
|
case CLOSE:
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
|
|
case CLOSEPROGRESS:
|
|
if ( hli )
|
|
{
|
|
delete hli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
case REQUESTPROGRESS:
|
|
|
|
ProgressInfo p1;
|
|
ProgressInfo* pRes;
|
|
const double uHead = 0.00056;
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
|
|
if ( !hli ) {
|
|
return CANCEL;
|
|
}
|
|
|
|
if ( qp->RequestProgress(args[0].addr, &p1) )
|
|
{
|
|
pRes->Card = (p1.Card < hli->maxTuples ? p1.Card : hli->maxTuples);
|
|
pRes->CopySizes(p1);
|
|
|
|
double perTupleTime =
|
|
(p1.Time - p1.BTime) / (p1.Card + 1);
|
|
|
|
pRes->Time = p1.BTime + (double) pRes->Card * (perTupleTime + uHead);
|
|
|
|
pRes->Progress =
|
|
(p1.BProgress * p1.BTime
|
|
+ (double) hli->numTuples * (perTupleTime + uHead))
|
|
/ pRes->Time;
|
|
|
|
pRes->CopyBlocking(p1);
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
2.8.4 Specification of operator ~head~
|
|
|
|
*/
|
|
const string HeadSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( "
|
|
"<text>((stream (tuple([a1:d1, ... ,an:dn]"
|
|
"))) x int) -> (stream (tuple([a1:d1, ... ,"
|
|
"an:dn]))) or \n"
|
|
"((stream T) int) -> (stream T), "
|
|
"for T in kind DATA.</text--->"
|
|
"<text>_ head [ _ ]</text--->"
|
|
"<text>Returns the first n elements of the input "
|
|
"stream.</text--->"
|
|
"<text>query cities feed head[10] consume\n"
|
|
"query intstream(1,1000) head[10] printstream count"
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.8.5 Definition of operator ~head~
|
|
|
|
*/
|
|
Operator extrelhead (
|
|
"head", // name
|
|
HeadSpec, // specification
|
|
Head, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
HeadTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.9 Operators ~max~ and ~min~
|
|
|
|
2.9.1 Type mapping function of Operators ~max~ and ~min~
|
|
|
|
Type mapping for ~max~ and ~min~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) xi) -> ti
|
|
APPEND (i ti)
|
|
----
|
|
|
|
*/
|
|
template<bool isMax> ListExpr
|
|
MaxMinTypeMap( ListExpr args )
|
|
{
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
|
|
string err = "stream(tuple(...)) x attrname expected";
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr attr = nl->Second(args);
|
|
|
|
if(!listutils::isTupleStream(stream) ||
|
|
nl->AtomType(attr)!=SymbolType){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr attrtype;
|
|
string attrname = nl->SymbolValue(attr);
|
|
ListExpr attrlist = nl->Second(nl->Second(stream));
|
|
int j = listutils::findAttribute(attrlist, attrname, attrtype);
|
|
if ( j ) {
|
|
if(!listutils::isSymbol(attrtype,CcReal::BasicType()) &&
|
|
!listutils::isSymbol(attrtype,CcString::BasicType()) &&
|
|
!listutils::isSymbol(attrtype,CcBool::BasicType()) &&
|
|
!listutils::isSymbol(attrtype,CcInt::BasicType()) &&
|
|
!listutils::isSymbol(attrtype,Instant::BasicType()) &&
|
|
!listutils::isSymbol(attrtype,Duration::BasicType()) ){
|
|
return listutils::typeError("result type not in {real, string, "
|
|
"bool, int, instant, duration}");
|
|
}
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(nl->IntAtom(j)), attrtype);
|
|
} else {
|
|
return listutils::typeError("attribute name " + attrname +
|
|
"not known in the tuple");
|
|
}
|
|
}
|
|
/*
|
|
2.9.2 Value mapping function of operators ~max~ and ~min~
|
|
|
|
*/
|
|
|
|
template<bool isMax> int
|
|
MaxMinValueMapping(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.setAddr(extremum);
|
|
|
|
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))
|
|
{
|
|
Tuple* currentTuple = (Tuple*)currentTupleWord.addr;
|
|
const Attribute* currentAttr =
|
|
(const Attribute*)currentTuple->GetAttribute(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;
|
|
}
|
|
|
|
/*
|
|
2.9.3 Specification of operator ~max~
|
|
|
|
*/
|
|
const string MaxOpSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn]"
|
|
"))) x ai) -> di</text--->"
|
|
"<text>_ _ mergesec</text--->"
|
|
"<text>Returns the maximum value of attribute "
|
|
"ai over the input stream.</text--->"
|
|
"<text>query cities feed max [ cityname ]"
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.9.4 Definition of operator ~max~
|
|
|
|
*/
|
|
Operator extrelmax (
|
|
"max", // name
|
|
MaxOpSpec, // specification
|
|
MaxMinValueMapping<true>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
MaxMinTypeMap<true> // type mapping
|
|
);
|
|
|
|
/*
|
|
2.9.5 Specification of operator ~min~
|
|
|
|
*/
|
|
const string MinOpSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn])))"
|
|
" x ai) -> di</text--->"
|
|
"<text>_ min [ _ ]</text--->"
|
|
"<text>Returns the minimum value of attribute ai "
|
|
"over the input stream.</text--->"
|
|
"<text>query cities feed min [ cityname ]"
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.9.6 Definition of operator ~min~
|
|
|
|
*/
|
|
Operator extrelmin (
|
|
"min", // name
|
|
MinOpSpec, // specification
|
|
MaxMinValueMapping<false>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
MaxMinTypeMap<false> // type mapping
|
|
);
|
|
|
|
/*
|
|
2.10 Operators ~avg~, ~sum~, and ~var~
|
|
|
|
2.10.1 Type mapping function of Operators ~avg~, ~sum~, and ~var~
|
|
|
|
Type mapping for ~avg~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) xi) -> real)
|
|
APPEND (i ti)
|
|
----
|
|
|
|
Type mapping for ~sum~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) xi) -> ti)
|
|
APPEND (i ti)
|
|
----
|
|
|
|
Type mapping for ~var~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) xi) -> real)
|
|
APPEND (i ti)
|
|
----
|
|
|
|
*/
|
|
|
|
template<bool isAvg>
|
|
ListExpr
|
|
AvgSumTypeMap( ListExpr args )
|
|
{
|
|
|
|
NList type(args);
|
|
if ( !type.hasLength(2) )
|
|
{
|
|
return NList::typeError("Expecting two arguments.");
|
|
}
|
|
|
|
NList first = type.first();
|
|
if (
|
|
!first.hasLength(2) ||
|
|
!first.first().isSymbol(Symbol::STREAM()) ||
|
|
!first.second().hasLength(2) ||
|
|
!first.second().first().isSymbol(Tuple::BasicType()) ||
|
|
!IsTupleDescription( first.second().second().listExpr() ) )
|
|
{
|
|
return NList::typeError("Error in first argument!");
|
|
}
|
|
|
|
NList second = type.second();
|
|
if ( !second.isSymbol() ) {
|
|
return NList::typeError( "Second argument must be an attribute name. "
|
|
"Perhaps the attribute's name may be the name "
|
|
"of a Secondo object!" );
|
|
}
|
|
|
|
string attrname = type.second().str();
|
|
ListExpr attrtype = nl->Empty();
|
|
int j = FindAttribute(first.second().second().listExpr(), attrname, attrtype);
|
|
|
|
if ( j != 0 )
|
|
{
|
|
if ( nl->SymbolValue(attrtype) != CcReal::BasicType() &&
|
|
nl->SymbolValue(attrtype) != CcInt::BasicType() )
|
|
{
|
|
return NList::typeError("Attribute type is not of type real or int.");
|
|
}
|
|
NList resType = isAvg ? NList(CcReal::BasicType()) : NList(attrtype);
|
|
return NList( NList(Symbol::APPEND()),
|
|
NList(j).enclose(), resType ).listExpr();
|
|
}
|
|
else
|
|
{
|
|
return NList::typeError( "Attribute name '" + attrname + "' is not known.");
|
|
}
|
|
}
|
|
|
|
/*
|
|
Since the list structure has been already checked the selection function can
|
|
trust to have a list of the correct format. Hence we can ommit many checks and
|
|
access elements directly.
|
|
|
|
*/
|
|
|
|
int
|
|
AvgSumSelect( ListExpr args )
|
|
{
|
|
NList type(args);
|
|
NList first = type.first();
|
|
string attrname = type.second().str();
|
|
ListExpr attrtype = nl->Empty();
|
|
FindAttribute(first.second().second().listExpr(), attrname, attrtype);
|
|
|
|
if ( nl->SymbolValue(attrtype) == CcInt::BasicType() )
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
/*
|
|
|
|
2.10.2 Value mapping function of operators ~avg~, ~sum~, and ~var~
|
|
|
|
Here we use template functions which may be instantiated with the
|
|
following values:
|
|
|
|
----
|
|
T = int, real
|
|
R = CcInt, CcReal
|
|
----
|
|
|
|
*/
|
|
template<class T, class R> int
|
|
SumValueMapping(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
T sum = 0;
|
|
int number = 0;
|
|
|
|
Word currentTupleWord(Address(0));
|
|
int attributeIndex = static_cast<CcInt*>( args[2].addr)->GetIntval() - 1;
|
|
|
|
qp->Open(args[0].addr);
|
|
qp->Request(args[0].addr, currentTupleWord);
|
|
while(qp->Received(args[0].addr))
|
|
{
|
|
Tuple* currentTuple = static_cast<Tuple*>( currentTupleWord.addr );
|
|
R* currentAttr =
|
|
static_cast<R*>( currentTuple->GetAttribute(attributeIndex) );
|
|
|
|
if( currentAttr->IsDefined() ) // process only defined elements
|
|
{
|
|
number++;
|
|
sum += currentAttr->GetValue();
|
|
}
|
|
currentTuple->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, currentTupleWord);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
|
|
result = qp->ResultStorage(s);
|
|
static_cast<R*>( result.addr )->Set(true, sum);
|
|
return 0;
|
|
}
|
|
|
|
template<class T, class R> int
|
|
AvgValueMapping(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
T sum = 0;
|
|
int number = 0;
|
|
|
|
Word currentTupleWord(Address(0));
|
|
int attributeIndex = static_cast<CcInt*>( args[2].addr )->GetIntval() - 1;
|
|
|
|
qp->Open(args[0].addr);
|
|
qp->Request(args[0].addr, currentTupleWord);
|
|
while(qp->Received(args[0].addr))
|
|
{
|
|
Tuple* currentTuple = static_cast<Tuple*>( currentTupleWord.addr );
|
|
R* currentAttr =
|
|
static_cast<R*>( currentTuple->GetAttribute(attributeIndex) );
|
|
|
|
if( currentAttr->IsDefined() ) // process only defined elements
|
|
{
|
|
number++;
|
|
sum += currentAttr->GetValue();
|
|
}
|
|
currentTuple->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, currentTupleWord);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
|
|
result = qp->ResultStorage(s);
|
|
if ( number == 0 ) {
|
|
static_cast<Attribute*>( result.addr )->SetDefined(false);
|
|
}
|
|
else
|
|
{
|
|
SEC_STD_REAL sumreal = sum;
|
|
static_cast<CcReal*>( result.addr )->Set(true, sumreal / number);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
template<class T, class R> int
|
|
VarValueMapping(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
TupleBuffer *tp = 0;
|
|
GenericRelationIterator *relIter = 0;
|
|
long MaxMem = qp->GetMemorySize(s);
|
|
|
|
T sum = 0;
|
|
int number = 0;
|
|
int counter = 0;
|
|
SEC_STD_REAL mean = 0.0;
|
|
SEC_STD_REAL diffsum = 0.0;
|
|
|
|
result = qp->ResultStorage(s);
|
|
Word currentTupleWord(Address(0));
|
|
int attributeIndex = static_cast<CcInt*>( args[2].addr )->GetIntval() - 1;
|
|
|
|
tp = new TupleBuffer(MaxMem);
|
|
|
|
// In a first scan, we compute the stream's MEAN:
|
|
qp->Open(args[0].addr);
|
|
qp->Request(args[0].addr, currentTupleWord);
|
|
while(qp->Received(args[0].addr))
|
|
{
|
|
Tuple* currentTuple = static_cast<Tuple*>( currentTupleWord.addr );
|
|
R* currentAttr =
|
|
static_cast<R*>( currentTuple->GetAttribute(attributeIndex) );
|
|
|
|
if( currentAttr->IsDefined() ) // process only defined elements
|
|
{
|
|
number++;
|
|
sum += currentAttr->GetValue();
|
|
}
|
|
tp->AppendTuple(currentTuple);
|
|
currentTuple->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, currentTupleWord);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
|
|
// if there was no defined value, we are finished. Otherwise we need a secomd
|
|
// scan to compute the stream's VARIANCE
|
|
if ( number < 2 ) {
|
|
static_cast<Attribute*>( result.addr )->SetDefined(false);
|
|
}
|
|
else
|
|
{
|
|
mean = (sum * 1.0) / number;
|
|
|
|
Tuple* currentTuple = 0;
|
|
relIter = tp->MakeScan();
|
|
currentTuple = relIter->GetNextTuple();
|
|
|
|
while( currentTuple && (counter <= number) )
|
|
{
|
|
R* currentAttr =
|
|
static_cast<R*>( currentTuple->GetAttribute(attributeIndex) );
|
|
|
|
if( currentAttr->IsDefined() ) // process only defined elements
|
|
{
|
|
counter++;
|
|
SEC_STD_REAL Diff = ( (currentAttr->GetValue() * 1.0) - mean );
|
|
diffsum += Diff * Diff;
|
|
}
|
|
currentTuple->DeleteIfAllowed();
|
|
currentTuple = relIter->GetNextTuple();
|
|
}
|
|
delete relIter;
|
|
static_cast<CcReal*>( result.addr )->Set(true, diffsum / (counter - 1));
|
|
}
|
|
delete tp;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
2.10.3 Operator Descriptions for ~avg~, ~sum~, and ~var~
|
|
|
|
*/
|
|
|
|
struct avgInfo : OperatorInfo {
|
|
|
|
avgInfo() : OperatorInfo() {
|
|
|
|
name = "avg";
|
|
signature = "((stream (tuple([a1:d1, ...,"
|
|
" ai:int, ..., an:dn]))) x ai) -> real";
|
|
appendSignature( "((stream (tuple([a1:d1, ...,"
|
|
" ai:real, ..., an:dn]))) x ai) -> real");
|
|
syntax = "_ avg [ _ ]";
|
|
meaning = "Returns the average value of attribute "
|
|
"ai over the input stream. ";
|
|
}
|
|
|
|
};
|
|
|
|
struct sumInfo : OperatorInfo {
|
|
|
|
sumInfo() : OperatorInfo() {
|
|
|
|
name = "sum";
|
|
signature = "((stream (tuple([a1:d1, ..., "
|
|
"ai:int, ..., an:dn]))) x ai) -> int";
|
|
appendSignature( "((stream (tuple([a1:d1, ...,"
|
|
" ai:real, ..., an:dn]))) x ai) -> real" );
|
|
syntax = "_ sum [ _ ]";
|
|
meaning = "Returns the sum of the values of attribute "
|
|
"ai over the input stream. ";
|
|
}
|
|
|
|
};
|
|
|
|
struct varInfo : OperatorInfo {
|
|
|
|
varInfo() : OperatorInfo() {
|
|
|
|
name = "var";
|
|
signature = "((stream (tuple([a1:d1, ..., "
|
|
"ai:int, ..., an:dn]))) x ai) -> real";
|
|
appendSignature( "((stream (tuple([a1:d1, ...,"
|
|
" ai:real, ..., an:dn]))) x ai) -> real" );
|
|
syntax = "_ var [ _ ]";
|
|
meaning = "Returns the variance of the values of attribute "
|
|
"ai over the input stream. Needs to perform 2 scans!";
|
|
}
|
|
|
|
};
|
|
|
|
/*
|
|
10.5 Operator ~stats~
|
|
|
|
This operator calculates several aggregation functions and statistics on a
|
|
stream of tuples containing two mumeric attributes. And returns a single tuple
|
|
with
|
|
all the results:
|
|
|
|
* CountX - Number of defined instances for the first attribute X
|
|
|
|
* MinX - minimum instance of the first attribute
|
|
|
|
* MaxX - maximum instance of the first attribute
|
|
|
|
* SumX - sum for all defined instance of the first attribute
|
|
|
|
* AvgX - mean for all defined instance of the first attribute
|
|
|
|
* VarX - variance for all defined instance of the first attribute
|
|
|
|
* CountY - Number of defined instances for the second attribute Y
|
|
|
|
* MinY - minimum instance of the second attribute
|
|
|
|
* MaxY - maximum instance of the second attribute
|
|
|
|
* SumY - sum for all defined instance of the second attribute
|
|
|
|
* AvgY - mean for all defined instance of the second attribute
|
|
|
|
* VarY - variance for all defined instance of the second attribute
|
|
|
|
* Count - Cardinality of the stream
|
|
|
|
* CountXY - Number of tuples, where both arguments X and Y are defined
|
|
|
|
* CovXY - the covariance for X and Y
|
|
|
|
* Corr - the Pearson product-moment correlation coefficient for X and Y
|
|
|
|
*/
|
|
|
|
/*
|
|
10.5.1 Type mapping for ~stats~
|
|
|
|
Type mapping for ~stats~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) xi xj) -> stream(tuple(
|
|
(CountX int) (MinX real) (MaxX real) (SumX real) (AvgX real) (VarX real)
|
|
(CountY int) (MinY real) (MaxY real) (SumY real) (AvgY real) (VarY real)
|
|
(Count int) (CountXY int) (CovXY real) (CorrXY real)))
|
|
APPEND ((i ti) (j tj))
|
|
|
|
where ti, ty in {int, real}
|
|
----
|
|
|
|
*/
|
|
|
|
ListExpr StatsTypeMap( ListExpr args )
|
|
{
|
|
|
|
NList type(args);
|
|
if ( !type.hasLength(3) )
|
|
{
|
|
return NList::typeError("Expecting three arguments.");
|
|
}
|
|
|
|
NList first = type.first();
|
|
if (
|
|
!first.hasLength(2) ||
|
|
!first.first().isSymbol(Symbol::STREAM()) ||
|
|
!first.second().hasLength(2) ||
|
|
!first.second().first().isSymbol(Tuple::BasicType()) ||
|
|
!IsTupleDescription( first.second().second().listExpr() ) )
|
|
{
|
|
return NList::typeError("First argument must be of type stream(tuple(X))");
|
|
}
|
|
|
|
NList second = type.second();
|
|
if ( !second.isSymbol() ) {
|
|
return NList::typeError( "Second argument must be an attribute name. "
|
|
"Perhaps the attribute's name may be the name "
|
|
"of a Secondo object!" );
|
|
}
|
|
|
|
NList third = type.third();
|
|
if ( !second.isSymbol() ) {
|
|
return NList::typeError( "Third argument must be an attribute name. "
|
|
"Perhaps the attribute's name may be the name "
|
|
"of a Secondo object!" );
|
|
}
|
|
|
|
string attrnameX = type.second().str();
|
|
ListExpr attrtypeX = nl->Empty();
|
|
int jX = FindAttribute(first.second().second().listExpr(),
|
|
attrnameX, attrtypeX);
|
|
|
|
string attrnameY = type.third().str();
|
|
ListExpr attrtypeY = nl->Empty();
|
|
int jY = FindAttribute(first.second().second().listExpr(),
|
|
attrnameY, attrtypeY);
|
|
|
|
if ( (jX != 0) && (jY != 0) )
|
|
{
|
|
if ( nl->SymbolValue(attrtypeX) != CcReal::BasicType() &&
|
|
nl->SymbolValue(attrtypeX) != CcInt::BasicType() )
|
|
{
|
|
return NList::typeError(
|
|
"Attribute type of 2nd argument is not of type real or int.");
|
|
}
|
|
if ( nl->SymbolValue(attrtypeY) != CcReal::BasicType() &&
|
|
nl->SymbolValue(attrtypeY) != CcInt::BasicType() )
|
|
{
|
|
return NList::typeError(
|
|
"Attribute type of 3rd argument is not of type real or int.");
|
|
}
|
|
NList resTupleType = NList(NList("CountX"),
|
|
NList(CcInt::BasicType())).enclose();
|
|
resTupleType.append(NList(NList("MinX"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("MaxX"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("SumX"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("AvgX"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("VarX"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("CountY"),NList(CcInt::BasicType())));
|
|
resTupleType.append(NList(NList("MinY"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("MaxY"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("SumY"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("AvgY"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("VarY"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("Count"),NList(CcInt::BasicType())));
|
|
resTupleType.append(NList(NList("CountXY"),NList(CcInt::BasicType())));
|
|
resTupleType.append(NList(NList("CovXY"),NList(CcReal::BasicType())));
|
|
resTupleType.append(NList(NList("CorrXY"),NList(CcReal::BasicType())));
|
|
NList resType = NList( NList(Symbol::APPEND()), NList(NList(jX), NList(jY)),
|
|
NList(NList(Symbol::STREAM()),
|
|
NList(NList(Tuple::BasicType()),resTupleType))
|
|
);
|
|
return resType.listExpr();
|
|
}
|
|
else
|
|
{
|
|
if (jX == 0)
|
|
return NList::typeError( "Attribute '" + attrnameX + "' is not known.");
|
|
else
|
|
return NList::typeError( "Attribute '" + attrnameY + "' is not known.");
|
|
}
|
|
}
|
|
|
|
/*
|
|
Since the list structure has been already checked the selection function can
|
|
trust to have a list of the correct format. Hence we can ommit many checks and
|
|
access elements directly.
|
|
|
|
*/
|
|
|
|
int StatsSelect( ListExpr args )
|
|
{
|
|
NList type(args);
|
|
NList first = type.first();
|
|
string attrnameX = type.second().str();
|
|
string attrnameY = type.third().str();
|
|
ListExpr attrtypeX = nl->Empty();
|
|
ListExpr attrtypeY = nl->Empty();
|
|
FindAttribute(first.second().second().listExpr(), attrnameX, attrtypeX);
|
|
FindAttribute(first.second().second().listExpr(), attrnameY, attrtypeY);
|
|
|
|
if ((nl->SymbolValue(attrtypeX) == CcInt::BasicType())&&(
|
|
nl->SymbolValue(attrtypeY) == CcInt::BasicType()))
|
|
return 0;
|
|
if ((nl->SymbolValue(attrtypeX) == CcInt::BasicType())&&
|
|
(nl->SymbolValue(attrtypeY) == CcReal::BasicType()))
|
|
return 1;
|
|
if ((nl->SymbolValue(attrtypeX) == CcReal::BasicType())&&
|
|
(nl->SymbolValue(attrtypeY) == CcInt::BasicType()))
|
|
return 2;
|
|
if ((nl->SymbolValue(attrtypeX) == CcReal::BasicType())&&
|
|
(nl->SymbolValue(attrtypeY)==CcReal::BasicType()))
|
|
return 3;
|
|
assert( false );
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
10.5.2 Value mapping for ~stats~
|
|
|
|
There are 4 template class parameters.
|
|
Tx and Ty are ~int~ or ~real~,
|
|
Rx and Ry are the corresponding types ~CcInt~ resp. ~CcReal~.
|
|
|
|
*/
|
|
|
|
template<class Tx, class Rx, class Ty, class Ry> int
|
|
StatsValueMapping(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
|
|
TupleBuffer *tp = 0;
|
|
GenericRelationIterator *relIter = 0;
|
|
long MaxMem = qp->GetMemorySize(s);
|
|
bool *finished = 0;
|
|
TupleType *resultTupleType = 0;
|
|
Tuple *newTuple = 0;
|
|
CcInt *CCountX = 0;
|
|
CcReal *CMinX = 0;
|
|
CcReal *CMaxX = 0;
|
|
CcReal *CSumX = 0;
|
|
CcReal *CAvgX = 0;
|
|
CcReal *CVarX = 0;
|
|
CcInt *CCountY = 0;
|
|
CcReal *CMinY = 0;
|
|
CcReal *CMaxY = 0;
|
|
CcReal *CSumY = 0;
|
|
CcReal *CAvgY = 0;
|
|
CcReal *CVarY = 0;
|
|
CcInt *CCount = 0;
|
|
CcInt *CCountXY = 0;
|
|
CcReal *CCovXY = 0;
|
|
CcReal *CCorrXY = 0;
|
|
|
|
switch(message)
|
|
{
|
|
case OPEN:
|
|
{
|
|
finished = new bool(false);
|
|
local.addr = finished;
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:
|
|
{
|
|
if(local.addr == 0)
|
|
return CANCEL;
|
|
finished = (bool*) local.addr;
|
|
if(*finished)
|
|
return CANCEL;
|
|
|
|
int countAll = 0, countX = 0, countY = 0, countXY = 0;
|
|
SEC_STD_REAL minX = 0, maxX = 0, sumX = 0;
|
|
SEC_STD_REAL minY = 0, maxY = 0, sumY = 0;
|
|
SEC_STD_REAL meanX = 0, meanY = 0;
|
|
SEC_STD_REAL diffsumX = 0, diffsumY = 0, sumXY = 0;
|
|
SEC_STD_REAL sumX2 = 0, sumY2 = 0, sumsqX = 0, sumsqY = 0;
|
|
|
|
Tx currX = 0;
|
|
Ty currY = 0;
|
|
|
|
result = qp->ResultStorage(s);
|
|
Word currentTupleWord(Address(0));
|
|
int attributeIndexX = static_cast<CcInt*>(args[3].addr)->GetIntval() - 1;
|
|
int attributeIndexY = static_cast<CcInt*>(args[4].addr)->GetIntval() - 1;
|
|
|
|
tp = new TupleBuffer(MaxMem);
|
|
|
|
// In a first scan, we compute the stream's MEANs for X,Y
|
|
// and count defined values for X,Y:
|
|
qp->Open(args[0].addr);
|
|
qp->Request(args[0].addr, currentTupleWord);
|
|
while(qp->Received(args[0].addr))
|
|
{
|
|
countAll++;
|
|
Tuple* currentTuple = static_cast<Tuple*>( currentTupleWord.addr );
|
|
Rx* currentAttrX =
|
|
static_cast<Rx*>( currentTuple->GetAttribute(attributeIndexX) );
|
|
Ry* currentAttrY =
|
|
static_cast<Ry*>( currentTuple->GetAttribute(attributeIndexY) );
|
|
|
|
if( currentAttrX->IsDefined() )
|
|
{
|
|
countX++;
|
|
currX = currentAttrX->GetValue();
|
|
if (countX == 1)
|
|
{
|
|
minX = currX;
|
|
maxX = currX;
|
|
}
|
|
else
|
|
{
|
|
if (currX < minX)
|
|
minX = currX;
|
|
if (currX > maxX)
|
|
maxX = currX;
|
|
}
|
|
sumX += currX;
|
|
}
|
|
if( currentAttrY->IsDefined() )
|
|
{
|
|
countY++;
|
|
currY = currentAttrY->GetValue();
|
|
if (countY == 1)
|
|
{
|
|
minY = currY;
|
|
maxY = currY;
|
|
}
|
|
else
|
|
{
|
|
if (currY < minY)
|
|
minY = currY;
|
|
if (currY > maxY)
|
|
maxY = currY;
|
|
}
|
|
sumY += currY;
|
|
}
|
|
tp->AppendTuple(currentTuple);
|
|
if(currentTuple->DeleteIfAllowed()){
|
|
cout << "stats :: deleting a tuple stored in a buffer !!!" << endl;
|
|
}
|
|
qp->Request(args[0].addr, currentTupleWord);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
|
|
if ( (countX + countY) > 1 )
|
|
{
|
|
meanX = (sumX * 1.0) / countX;
|
|
meanY = (sumY * 1.0) / countY;
|
|
|
|
// A second Scan to calculate the emprical covariance and
|
|
// emprical correlation coefficient. Therefore, we recalculate the means
|
|
// using only those tuples, where both attributes are defined...
|
|
Tuple* currentTuple = 0;
|
|
relIter = tp->MakeScan();
|
|
currentTuple = relIter->GetNextTuple();
|
|
|
|
while( currentTuple )
|
|
{
|
|
Rx* currentAttrX =
|
|
static_cast<Rx*>( currentTuple->GetAttribute(attributeIndexX) );
|
|
Ry* currentAttrY =
|
|
static_cast<Ry*>( currentTuple->GetAttribute(attributeIndexY) );
|
|
if( currentAttrX->IsDefined() )
|
|
{ // for var(X):
|
|
currX = currentAttrX->GetValue();
|
|
diffsumX += (currX - meanX) * (currX - meanX);
|
|
}
|
|
if( currentAttrY->IsDefined() )
|
|
{ // for var(Y):
|
|
currY = currentAttrY->GetValue();
|
|
diffsumY += (currY - meanY) * (currY - meanY);
|
|
}
|
|
if( currentAttrX->IsDefined() && currentAttrY->IsDefined() )
|
|
{ // for cov(X,Y) and corr(X,Y):
|
|
countXY++;
|
|
sumX2 += currX;
|
|
sumY2 += currY;
|
|
sumXY += currX * currY;
|
|
sumsqX += currX * currX;
|
|
sumsqY += currY * currY;
|
|
}
|
|
currentTuple->DeleteIfAllowed();
|
|
currentTuple = relIter->GetNextTuple();
|
|
}
|
|
delete relIter;
|
|
}
|
|
delete tp;
|
|
|
|
// create the resulttuple:
|
|
resultTupleType = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
newTuple = new Tuple( resultTupleType );
|
|
|
|
CCountX = new CcInt(true, countX);
|
|
CMinX = new CcReal((countX > 0), minX);
|
|
CMaxX = new CcReal((countX > 0), maxX);
|
|
CSumX = new CcReal(true, sumX);
|
|
if (countX > 0){
|
|
CAvgX = new CcReal(true, sumX / countX);
|
|
CVarX = new CcReal(true, diffsumX / (countX - 1));
|
|
}
|
|
else{
|
|
CAvgX = new CcReal(false, 0);
|
|
CVarX = new CcReal(false, 0);
|
|
}
|
|
CCountY = new CcInt(true, countY);
|
|
CMinY = new CcReal((countY > 0), minY);
|
|
CMaxY = new CcReal((countY > 0), maxY);
|
|
CSumY = new CcReal(true, sumY);
|
|
if (countY > 0){
|
|
CAvgY = new CcReal(true, sumY / countY);
|
|
CVarY = new CcReal(true, diffsumY / (countY - 1));
|
|
}
|
|
else {
|
|
CAvgY = new CcReal(false, 0);
|
|
CVarY = new CcReal(false, 0);
|
|
}
|
|
CCount = new CcInt(true, countAll);
|
|
CCountXY = new CcInt(true, countXY);
|
|
if(countXY > 1){
|
|
SEC_STD_REAL covXY =
|
|
(sumXY - (sumX2 * sumY2 / countXY)) / (countXY - 1);
|
|
CCovXY = new CcReal(true, covXY);
|
|
SEC_STD_REAL meanX2 = sumX2/countXY;
|
|
SEC_STD_REAL meanY2 = sumY2/countXY;
|
|
SEC_STD_REAL denom = sqrt(sumsqX - (countXY * (meanX2 * meanX2)))
|
|
* sqrt(sumsqY - (countXY * (meanY2 * meanY2)));
|
|
if (denom == 0){
|
|
CCorrXY = new CcReal(false, 0);
|
|
}
|
|
else{
|
|
CCorrXY = new
|
|
CcReal(true, (sumXY - (countXY * meanX2 * meanY2) ) / denom);
|
|
}
|
|
}
|
|
else {
|
|
CCovXY = new CcReal(false, 0);
|
|
CCorrXY = new CcReal(false, 0);
|
|
}
|
|
newTuple->PutAttribute( 0,(Attribute*)CCountX);
|
|
newTuple->PutAttribute( 1,(Attribute*)CMinX);
|
|
newTuple->PutAttribute( 2,(Attribute*)CMaxX);
|
|
newTuple->PutAttribute( 3,(Attribute*)CSumX);
|
|
newTuple->PutAttribute( 4,(Attribute*)CAvgX);
|
|
newTuple->PutAttribute( 5,(Attribute*)CVarX);
|
|
newTuple->PutAttribute( 6,(Attribute*)CCountY);
|
|
newTuple->PutAttribute( 7,(Attribute*)CMinY);
|
|
newTuple->PutAttribute( 8,(Attribute*)CMaxY);
|
|
newTuple->PutAttribute( 9,(Attribute*)CSumY);
|
|
newTuple->PutAttribute( 10,(Attribute*)CAvgY);
|
|
newTuple->PutAttribute( 11,(Attribute*)CVarY);
|
|
newTuple->PutAttribute( 12,(Attribute*)CCount);
|
|
newTuple->PutAttribute( 13,(Attribute*)CCountXY);
|
|
newTuple->PutAttribute( 14,(Attribute*)CCovXY);
|
|
newTuple->PutAttribute( 15,(Attribute*)CCorrXY);
|
|
|
|
result.setAddr(newTuple);
|
|
resultTupleType->DeleteIfAllowed();
|
|
|
|
*finished = true;
|
|
return YIELD;
|
|
}
|
|
|
|
case CLOSE:
|
|
{
|
|
if(local.addr == 0)
|
|
return 0;
|
|
finished = (bool*) local.addr;
|
|
delete(finished);
|
|
local.setAddr(0);
|
|
return 0;
|
|
}
|
|
} // end switch
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
10.5.2 Operator Descriptions for ~stats~
|
|
|
|
*/
|
|
|
|
struct statsInfo : OperatorInfo {
|
|
|
|
statsInfo() : OperatorInfo() {
|
|
|
|
name = "stats";
|
|
signature = "((stream (tuple([a1:d1, ... ,an:dn]))) x ai x aj) -> "
|
|
"stream(tuple( \n"
|
|
"(CountX int) (MinX real) (MaxX real) (SumX real) (AvgX real) (VarX real)\n"
|
|
"(CountY int) (MinY real) (MaxY real) (SumY real) (AvgY real) (VarY real)\n"
|
|
"(Count int) (CountXY int) (CovXY real) (CorrXY real))\n, "
|
|
"where ti, tj in {int,real}";
|
|
syntax = "_ stats [ attrnameX , attrnameY ]";
|
|
meaning = "Returns a tuple containing several statistics for 2 numerical "
|
|
"attributes 'attrnameX', 'attrnameY' on the stream. The first 6 "
|
|
"attributes contain the number of tuples where X is defined, the "
|
|
"minimum and maximum value for X, the sum for X, the mean for X, and "
|
|
"the variance of X. \n"
|
|
"The 7th to 12th attribute hold the appropriate statistics for Y.\n"
|
|
"the last 4 attributes hold the total count of tuples in the stream, "
|
|
"the number of tuples, where both attributes X and Y contain defined "
|
|
"values, the covariance of X and X, and finally Pearson's "
|
|
"correlation coefficient";
|
|
}
|
|
|
|
};
|
|
|
|
|
|
/*
|
|
2.10 Operator ~krdup~
|
|
|
|
This operator removes duplicates from a tuple stream. In contrast
|
|
to the usual rdup operator, the test of equality of tuples is
|
|
done using only the attributes from the given attribute list.
|
|
|
|
2.10.1 Type mapping of the operator ~krdup~
|
|
|
|
*/
|
|
|
|
ListExpr krdupTM(ListExpr args){
|
|
if(nl->ListLength(args)<2){
|
|
ErrorReporter::ReportError("minimum of 2 arguments required");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr streamList = nl->First(args);
|
|
ListExpr attrnameList = nl->Second(args);
|
|
if(nl->ListLength(attrnameList)<1){
|
|
ErrorReporter::ReportError("invalid number of attribute names");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
// extract the attribute names
|
|
set<string> names;
|
|
int pos = 0;
|
|
while(!nl->IsEmpty(attrnameList)){
|
|
ListExpr attr = nl->First(attrnameList);
|
|
pos++;
|
|
attrnameList = nl->Rest(attrnameList);
|
|
if(nl->AtomType(attr)!=SymbolType){
|
|
ErrorReporter::ReportError("argument is not "
|
|
" an attribute name");
|
|
return nl->TypeError();
|
|
}
|
|
string n = nl->SymbolValue(attr);
|
|
// insert the name into the set of names
|
|
names.insert(nl->SymbolValue(attr));
|
|
}
|
|
|
|
// check the stream argument
|
|
if(nl->ListLength(streamList)!=2 ||
|
|
!nl->IsEqual(nl->First(streamList),Symbol::STREAM())){
|
|
ErrorReporter::ReportError("(stream (tuple(...)) "
|
|
" expected as first argument");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr tupleList = nl->Second(streamList);
|
|
if(nl->ListLength(tupleList)!=2 ||
|
|
!nl->IsEqual(nl->First(tupleList),Tuple::BasicType())){
|
|
ErrorReporter::ReportError("(stream (tuple(...)) "
|
|
" expected as first argument");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr attrList = nl->Second(tupleList);
|
|
|
|
if(names.empty()){ // should never occur
|
|
ErrorReporter::ReportError("internal error, no attr names found.");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
pos = 0;
|
|
|
|
ListExpr PosList = nl->TheEmptyList();
|
|
ListExpr Last = nl->TheEmptyList();
|
|
bool first = true;
|
|
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr pair = nl->First(attrList);
|
|
if((nl->ListLength(pair)!= 2) ||
|
|
(nl->AtomType(nl->First(pair))!=SymbolType)){
|
|
ErrorReporter::ReportError("Error in tuple definition ");
|
|
return nl->TypeError();
|
|
}
|
|
string name = nl->SymbolValue(nl->First(pair));
|
|
if(names.find(name)!= names.end()){
|
|
if(first){
|
|
first = false;
|
|
PosList = nl->OneElemList(nl->IntAtom(pos));
|
|
Last = PosList;
|
|
first = false;
|
|
} else {
|
|
Last = nl->Append(Last,nl->IntAtom(pos));
|
|
}
|
|
names.erase(name);
|
|
}
|
|
pos++;
|
|
attrList = nl->Rest(attrList);
|
|
}
|
|
|
|
if(!names.empty()){
|
|
ErrorReporter::ReportError("at least one given attribute name is "
|
|
"not part of the stream");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
|
|
ListExpr result = nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(PosList),
|
|
streamList);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
2.10.2 Value Mapping of the ~krdup~ operator
|
|
|
|
This value mapping is very similar the this one of the
|
|
rdup operator. The only difference is, only specified
|
|
attributes are used for the check of equality.
|
|
|
|
*/
|
|
|
|
|
|
class KrdupLocalInfo{
|
|
public:
|
|
/*
|
|
~Constructor~
|
|
|
|
Creates a localinfo for the krdup operator from the Supplier
|
|
containing the information about the indexes which should be used
|
|
for the comparison.
|
|
|
|
*/
|
|
KrdupLocalInfo(vector<int>* _indexes, Word& _stream):
|
|
stream(_stream),indexes(*_indexes),lastTuple(0){
|
|
stream.open();
|
|
}
|
|
|
|
static vector<int>* getIndexes(Supplier s){
|
|
vector<int>* indexes = new vector<int>();
|
|
int count = qp->GetNoSons(s);
|
|
Word elem;
|
|
for(int i=0;i<count; i++){
|
|
Supplier son = qp->GetSon(s,i);
|
|
qp->Request(son,elem);
|
|
int index = ((CcInt*)elem.addr)->GetIntval();
|
|
indexes->push_back(index);
|
|
}
|
|
return indexes;
|
|
}
|
|
|
|
|
|
/*
|
|
~Destructor~
|
|
|
|
Destroy this instance.
|
|
|
|
*/
|
|
|
|
~KrdupLocalInfo(){
|
|
if(lastTuple!=0){
|
|
lastTuple->DeleteIfAllowed();
|
|
}
|
|
lastTuple = 0;
|
|
stream.close();
|
|
}
|
|
|
|
|
|
Tuple* next(){
|
|
Tuple* tuple;
|
|
while((tuple=stream.request())){
|
|
if(!ReplaceIfNonEqual(tuple)){
|
|
tuple->DeleteIfAllowed();
|
|
} else {
|
|
return tuple;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
private:
|
|
/*
|
|
~equals~
|
|
|
|
Checks the equality of the last tuple with the argument where only the
|
|
attributes stored in the vector are of interest.
|
|
|
|
*/
|
|
inline bool Equals(Tuple* tuple){
|
|
int size = indexes.size();
|
|
for(int i=0;i<size;i++){
|
|
int pos = indexes[i];
|
|
int cmp = ( ((Attribute*)lastTuple->GetAttribute(pos))->CompareAlmost(
|
|
((Attribute*)tuple->GetAttribute(pos))));
|
|
if(cmp!=0){
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
~ReplaceIfNonEqual~
|
|
|
|
If the argument differs from the last stored tuple, the old tuple
|
|
is replaced by the argument and the return value is true. Otherwise,
|
|
this instance is keep unchanged and the reuslt of the call is false.
|
|
The old tuple is removed automatically.
|
|
|
|
*/
|
|
inline bool ReplaceIfNonEqual(Tuple* newTuple){
|
|
if(lastTuple==0){ // first call
|
|
newTuple->IncReference();
|
|
lastTuple = newTuple;
|
|
return true;
|
|
}
|
|
|
|
if(Equals(newTuple)){
|
|
return false;
|
|
} else{ // replace the tuple
|
|
lastTuple->DeleteIfAllowed();
|
|
newTuple->IncReference();
|
|
lastTuple = newTuple;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Stream<Tuple> stream;
|
|
vector<int> indexes;
|
|
Tuple* lastTuple;
|
|
};
|
|
|
|
|
|
int KrdupVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
|
|
|
|
switch(message)
|
|
{
|
|
case INIT : {
|
|
return 0;
|
|
}
|
|
case FINISH : {
|
|
vector<int>* indexes = (vector<int>*) qp->GetLocal2(s).addr;
|
|
if(indexes){
|
|
delete indexes;
|
|
qp->GetLocal2(s).addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN:{
|
|
vector<int>* indexes = (vector<int>*) qp->GetLocal2(s).addr;
|
|
if(!indexes){
|
|
indexes = KrdupLocalInfo::getIndexes(qp->GetSon(s,2));
|
|
qp->GetLocal2(s).addr = indexes;
|
|
}
|
|
local.setAddr(new KrdupLocalInfo(indexes,args[0]));
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
KrdupLocalInfo* localinfo = (KrdupLocalInfo*) local.addr;
|
|
result.addr = localinfo?localinfo->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE:
|
|
if(local.addr) {
|
|
KrdupLocalInfo* localinfo = (KrdupLocalInfo*) local.addr;
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
2.13.2 Specification of operator ~krdup~
|
|
|
|
*/
|
|
const string KrdupSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn]"
|
|
" x ai1 .. ain))))"
|
|
" -> (stream (tuple([a1:d1, ... ,an:dn])))"
|
|
"</text--->"
|
|
"<text>_ krdup[ _ , _ , ...]</text--->"
|
|
"<text>Removes duplicates from a sorted "
|
|
"stream with respect to the attributes "
|
|
"given as arguments.</text--->"
|
|
"<text>query plz feed sortby [Ort] "
|
|
"krdup[Ort] consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.13.3 Definition of operator ~krdup~
|
|
|
|
*/
|
|
Operator krdup (
|
|
"krdup", // name
|
|
KrdupSpec, // specification
|
|
KrdupVM, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
krdupTM // type mapping
|
|
);
|
|
|
|
|
|
/*
|
|
2.10 Operator ~krduph~
|
|
|
|
This operator removes duplicates from a stream by comparing given key
|
|
attributes.
|
|
In constrast to ~krdup~, this operator does not require a sorted input stream.
|
|
|
|
2.10.1 Type Mapping
|
|
|
|
stream(tuple) x attrname[_]1 x attrname[_]2 ... [x int] -> stream(tuple)
|
|
|
|
|
|
*/
|
|
ListExpr krduphTM(ListExpr args){
|
|
|
|
string err = "stream(tuple) x attrname_1 x attrname_2 ... [x int] expected";
|
|
if(nl->ListLength(args) < 1){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
if(!Stream<Tuple>::checkType(stream)){
|
|
return listutils::typeError(err + "(first attr is not a tuple stream)");
|
|
}
|
|
|
|
args = nl->Rest(args);
|
|
bool done = nl->IsEmpty(args);
|
|
|
|
vector<int> positions;
|
|
bool numGiven = false;
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
ListExpr attrType;
|
|
|
|
while(!done){
|
|
ListExpr first = nl->First(args);
|
|
if(CcInt::checkType(first)){
|
|
done = true;
|
|
numGiven = true;
|
|
args = nl->Rest(args);
|
|
if(!nl->IsEmpty(args)){
|
|
return listutils::typeError(err +
|
|
" (int is allowed as the last argument only");
|
|
}
|
|
} else {
|
|
if(!listutils::isSymbol(first)){
|
|
return listutils::typeError(err + " (invalid attribute name)");
|
|
}
|
|
string attrName = nl->SymbolValue(first);
|
|
int index = listutils::findAttribute(attrList, attrName, attrType);
|
|
if(index == 0){
|
|
return listutils::typeError("attribute " + attrName +
|
|
"not present");
|
|
}
|
|
positions.push_back(index -1);
|
|
args = nl->Rest(args);
|
|
done = nl->IsEmpty(args);
|
|
}
|
|
}
|
|
|
|
|
|
ListExpr appendList;
|
|
ListExpr last;
|
|
bool appendInit = false;
|
|
if(positions.empty()){ // use all attributes
|
|
for(int i=0;i<nl->ListLength(attrList);i++){
|
|
positions.push_back(i);
|
|
}
|
|
// append dummy parameters
|
|
appendList = nl->OneElemList(nl->IntAtom(0));
|
|
last = appendList;
|
|
for(unsigned int i=1;i<positions.size();i++) {
|
|
last = nl->Append(last,nl->IntAtom(0));
|
|
}
|
|
appendInit = true;
|
|
}
|
|
unsigned int start = 0;
|
|
if(!numGiven){
|
|
if(!appendInit){
|
|
appendList = nl->OneElemList(nl->IntAtom(-1));
|
|
last = appendList;
|
|
} else {
|
|
last = nl->Append(last,nl->IntAtom(-1));
|
|
}
|
|
} else {
|
|
if(!appendInit){
|
|
appendList = nl->OneElemList(nl->IntAtom(positions[0]));
|
|
last = appendList;
|
|
start = 1;
|
|
}
|
|
}
|
|
|
|
for(unsigned int i=start;i<positions.size();i++){
|
|
last = nl->Append(last, nl->IntAtom(positions[i]));
|
|
}
|
|
|
|
|
|
ListExpr resultList = nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
appendList,
|
|
stream);
|
|
|
|
cout << nl->ToString(resultList) << endl;
|
|
|
|
return resultList;
|
|
}
|
|
// TODO
|
|
|
|
/*
|
|
2.10.2 LocalInfo
|
|
|
|
*/
|
|
|
|
class KrdupHInfo{
|
|
|
|
public:
|
|
KrdupHInfo(Word& _s, const int _buckNum,
|
|
vector<int>& _positions,
|
|
const size_t _maxMem,
|
|
TupleType* _tt) :
|
|
stream(_s), buckNum(_buckNum),
|
|
positions(_positions), maxMem(_maxMem), usedMem(0),
|
|
tupleType(_tt) {
|
|
|
|
stream.open();
|
|
if(buckNum<97){
|
|
buckNum = 97;
|
|
}
|
|
if(buckNum > 9999997){
|
|
buckNum = 9999997;
|
|
}
|
|
buckets = new vector<Tuple*>*[buckNum];
|
|
memset(buckets,0, buckNum*sizeof(void*));
|
|
usedMem = buckNum * sizeof(void*);
|
|
if(usedMem>maxMem){
|
|
maxMem = usedMem +128;
|
|
}
|
|
buffer1 = 0;
|
|
buffer2 = 0;
|
|
it = 0;
|
|
tupleType->IncReference(); // reserved for local usage
|
|
stored=0;
|
|
scans = 1; // at least the stream must be scanned
|
|
|
|
|
|
}
|
|
|
|
~KrdupHInfo(){
|
|
stream.close();
|
|
tupleType->DeleteIfAllowed();
|
|
eraseBuckets();
|
|
delete[] buckets;
|
|
if(it){
|
|
delete it;
|
|
}
|
|
if(buffer1){
|
|
buffer1->DeleteAndTruncate();
|
|
}
|
|
if(buffer2){
|
|
buffer2->DeleteAndTruncate();
|
|
}
|
|
cout << "Krduph finished with " << scans << "scans" << endl;
|
|
}
|
|
|
|
|
|
|
|
Tuple* nextTuple(){
|
|
Tuple* res = 0;
|
|
while(true){
|
|
res = nextInput();
|
|
if(res==0){
|
|
return 0;
|
|
}
|
|
bool ok = insert(res);
|
|
if(ok){
|
|
return res;
|
|
} else {
|
|
res->DeleteIfAllowed();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
Stream<Tuple> stream;
|
|
int buckNum;
|
|
vector<int> positions;
|
|
size_t maxMem;
|
|
vector<Tuple*>** buckets;
|
|
Relation* buffer1; // buffer for reading tuples
|
|
Relation* buffer2; // buffer fro writing tuples
|
|
GenericRelationIterator* it;
|
|
size_t usedMem;
|
|
TupleType* tupleType;
|
|
int stored;
|
|
int scans;
|
|
|
|
void eraseBuckets(){
|
|
for(int i=0;i<buckNum;i++){
|
|
if(buckets[i]){
|
|
vector<Tuple*>* bucket = buckets[i];
|
|
for(unsigned int j=0;j<bucket->size();j++){
|
|
(*bucket)[j]->DeleteIfAllowed();
|
|
}
|
|
delete bucket;
|
|
buckets[i] = 0;
|
|
}
|
|
}
|
|
stored = 0;
|
|
usedMem = sizeof(void*) * buckNum;
|
|
}
|
|
|
|
Tuple* nextInput(){
|
|
if(!it){ // try to get data from stream
|
|
Tuple* res = stream.request();
|
|
if(res){ // tuple found
|
|
return res;
|
|
}
|
|
// no tuple found in original stream
|
|
if(!buffer2){ // no tuples written out
|
|
return 0;
|
|
}
|
|
eraseBuckets();
|
|
buffer1 = buffer2; // change read buffer
|
|
buffer2 = 0;
|
|
it = buffer1->MakeScan();
|
|
}
|
|
|
|
Tuple* res = it->GetNextTuple();
|
|
if(res!=0){
|
|
return res;
|
|
}
|
|
// iterator finished
|
|
delete it;
|
|
it = 0;
|
|
buffer1->DeleteAndTruncate();
|
|
buffer1 = 0;
|
|
if(!buffer2){
|
|
return 0;
|
|
}
|
|
buffer1 = buffer2;
|
|
buffer2 = 0;
|
|
eraseBuckets();
|
|
it = buffer1->MakeScan();
|
|
return it->GetNextTuple();
|
|
}
|
|
|
|
/*
|
|
Inserts a tuple into this structure.
|
|
|
|
*/
|
|
bool insert(Tuple* tuple){
|
|
if(usedMem>maxMem){
|
|
if(!contains(tuple)){
|
|
if(!buffer2){
|
|
buffer2 = new Relation(tupleType, true);
|
|
scans++;
|
|
}
|
|
tuple->PinAttributes();
|
|
buffer2->AppendTupleNoLOBs(tuple);
|
|
}
|
|
return false;
|
|
}
|
|
return insertMM(tuple);
|
|
}
|
|
|
|
|
|
/*
|
|
Inserts a tuple into the memory part of this structure.
|
|
|
|
*/
|
|
bool insertMM(Tuple* tuple){
|
|
size_t hash = computeHash(tuple);
|
|
vector<Tuple*>* bucket = buckets[hash];
|
|
if(!bucket){
|
|
bucket = new vector<Tuple*>();
|
|
buckets[hash] = bucket;
|
|
bucket->push_back(tuple);
|
|
stored ++;
|
|
tuple->IncReference();
|
|
usedMem += tuple->GetMemSize() + sizeof(void*) + sizeof(*bucket);
|
|
return true;
|
|
}
|
|
for(size_t i=0;i<bucket->size();i++){
|
|
if(equals((*bucket)[i],tuple)){ // tuple found
|
|
return false;
|
|
}
|
|
}
|
|
bucket->push_back(tuple);
|
|
tuple->IncReference();
|
|
usedMem += tuple->GetMemSize() + sizeof(void*);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
Checks whether the memory part of this structur contains a tuple;
|
|
|
|
*/
|
|
bool contains(Tuple* tuple) const{
|
|
size_t hash = computeHash(tuple);
|
|
vector<Tuple*>* bucket = buckets[hash];
|
|
if(!bucket){
|
|
return false;
|
|
}
|
|
for(size_t i =0;i<bucket->size();i++){
|
|
if(equals((*bucket)[i],tuple)){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
Computes the hash value of a tuple based on the key attributes.
|
|
|
|
*/
|
|
|
|
size_t computeHash(const Tuple* tuple) const{
|
|
size_t hash = 0;
|
|
for(size_t i=0;i<positions.size();i++){
|
|
hash += tuple->GetAttribute(positions[i])->HashValue();
|
|
}
|
|
return hash % buckNum;
|
|
}
|
|
|
|
/*
|
|
Returns true if the key attributes of the given tuples are euqal.
|
|
|
|
*/
|
|
bool equals(const Tuple* t1, const Tuple* t2) const{
|
|
for(size_t i=0;i<positions.size(); i++){
|
|
int k = positions[i];
|
|
if(t1->GetAttribute(k)->Compare(t2->GetAttribute(k)) != 0){
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
};
|
|
|
|
/*
|
|
2.10.3
|
|
|
|
*/
|
|
int krduphVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
KrdupHInfo* li = (KrdupHInfo*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
switch(message){
|
|
|
|
case INIT : {
|
|
assert(!tt);
|
|
qp->GetLocal2(s).addr = new TupleType(nl->Second(
|
|
GetTupleResultType(s)));
|
|
return 0;
|
|
}
|
|
|
|
case OPEN :{ if(li){
|
|
delete li;
|
|
}
|
|
int sc = qp->GetNoSons(s) / 2;
|
|
CcInt* b = (CcInt*) args[sc].addr;
|
|
int bm = 999997;
|
|
if(b->IsDefined() && b->GetValue()>0){
|
|
bm = b->GetValue();
|
|
}
|
|
|
|
vector<int> positions;
|
|
|
|
|
|
for(int i=sc+1;i<qp->GetNoSons(s);i++){
|
|
positions.push_back(((CcInt*)args[i].addr)->GetValue());
|
|
}
|
|
|
|
size_t mem = qp->GetMemorySize(s)*1024*1024; // in bytes
|
|
local.addr = new KrdupHInfo(args[0],bm, positions, mem,tt);
|
|
}
|
|
case REQUEST: { if(!li){
|
|
return CANCEL;
|
|
}
|
|
result.addr = li->nextTuple();
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE : {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
}
|
|
|
|
case FINISH : {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
};
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
2.13.2 Specification of operator ~krdup~
|
|
|
|
*/
|
|
const string krduphSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn]"
|
|
" x ai1 .. ain [x int]))))"
|
|
" -> (stream (tuple([a1:d1, ... ,an:dn])))"
|
|
"</text--->"
|
|
"<text>_ krduph[ _ , _ , ... [, buckets]]</text--->"
|
|
"<text>Removes duplicates from a "
|
|
"stream with respect to the attributes "
|
|
"given as arguments.</text--->"
|
|
"<text>query plz feed "
|
|
"krdup[Ort] consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.13.3 Definition of operator ~krdup~
|
|
|
|
*/
|
|
Operator krduph (
|
|
"krduph", // name
|
|
krduphSpec, // specification
|
|
krduphVM, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
krduphTM // type mapping
|
|
);
|
|
|
|
|
|
/*
|
|
|
|
2.10 Operator k-smallest
|
|
|
|
2.10.1 Type Mapping
|
|
|
|
stream(tuple(...)) [x] int [x] attr[_]1 ... attr[_]n [->] tuple(...)
|
|
|
|
|
|
*/
|
|
|
|
ListExpr ksmallestTM(ListExpr args){
|
|
|
|
string err = "stream(tuple(...)) x int x attr_1 x ... x attr_n expected";
|
|
if(nl->ListLength(args) < 3 ){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr Stream = nl->First(args);
|
|
args = nl->Rest(args);
|
|
ListExpr Int = nl->First(args);
|
|
ListExpr AttrList = nl->Rest(args);
|
|
|
|
if(nl->ListLength(AttrList)!=1 ){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
AttrList = nl->First(AttrList);
|
|
|
|
if(!IsStreamDescription(Stream)){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if(!nl->IsEqual(Int,CcInt::BasicType())){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr StreamList = nl->Second(nl->Second(Stream));
|
|
|
|
int attrNo = 0;
|
|
ListExpr NumberList=nl->TheEmptyList();
|
|
ListExpr Last=nl->TheEmptyList();
|
|
ListExpr attrType = nl->TheEmptyList();
|
|
|
|
if(nl->AtomType(AttrList)!=NoAtom){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
|
|
while(!nl->IsEmpty(AttrList)){
|
|
|
|
ListExpr Attr = nl->First(AttrList);
|
|
if(nl->AtomType(Attr)!=SymbolType){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
string attrName = nl->SymbolValue(Attr);
|
|
int j = FindAttribute(StreamList,attrName, attrType);
|
|
if(j==0){
|
|
ErrorReporter::ReportError("Attribute name " + attrName + "not found");
|
|
return nl->TypeError();
|
|
}
|
|
if(attrNo==0){
|
|
NumberList = nl->OneElemList(nl->IntAtom(j-1));
|
|
Last = NumberList;
|
|
} else {
|
|
Last = nl->Append(Last, nl->IntAtom(j-1));
|
|
}
|
|
attrNo++;
|
|
AttrList = nl->Rest(AttrList);
|
|
}
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->TwoElemList(nl->IntAtom(attrNo),
|
|
NumberList),
|
|
Stream);
|
|
}
|
|
|
|
|
|
/*
|
|
2.10.2 LocalInfo
|
|
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
class KSmallestLocalInfo{
|
|
public:
|
|
|
|
/*
|
|
~Constructor~
|
|
|
|
Constructs a localinfo tor given k and the attribute indexes by ~attrnumbers~.
|
|
|
|
*/
|
|
KSmallestLocalInfo(int ak, vector<int>& attrnumbers,bool Smallest):
|
|
elems(0),numbers(attrnumbers),pos(0),smallest(Smallest),
|
|
firstRequest(true){
|
|
if(ak<0){
|
|
k = 0;
|
|
} else {
|
|
k = ak;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
~Destructor~
|
|
|
|
Destroy this instance and calls DeleteIfAllowed for all
|
|
non processed tuples.
|
|
|
|
|
|
*/
|
|
~KSmallestLocalInfo(){
|
|
for(unsigned int i=0;i<elems.size();i++){
|
|
if(elems[i]){
|
|
elems[i]->DeleteIfAllowed();
|
|
elems[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
~nextTuple~
|
|
|
|
Returns the next tuple within the buffer, or 0 if no tuple is
|
|
available.
|
|
|
|
*/
|
|
Tuple* nextTuple(Word& stream){
|
|
if(firstRequest){
|
|
Word elem;
|
|
qp->Request(stream.addr,elem);
|
|
Tuple* tuple;
|
|
while(qp->Received(stream.addr)){
|
|
tuple = static_cast<Tuple*>(elem.addr);
|
|
insertTuple(tuple);
|
|
qp->Request(stream.addr,elem);
|
|
firstRequest = false;
|
|
}
|
|
}
|
|
|
|
if(pos==0){
|
|
// sort the elements
|
|
if(elems.size()< k){
|
|
initializeHeap();
|
|
}
|
|
for(unsigned int i=elems.size()-1; i>0; i--){
|
|
Tuple* top = elems[0];
|
|
Tuple* last = elems[i];
|
|
elems[0] = last;
|
|
elems[i] = top;
|
|
sink(0,i);
|
|
}
|
|
}
|
|
if(pos<elems.size()){
|
|
Tuple* res = elems[pos];
|
|
elems[pos] = 0;
|
|
pos++;
|
|
return res;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private:
|
|
vector<Tuple*> elems;
|
|
vector<int> numbers;
|
|
unsigned int k;
|
|
unsigned int pos;
|
|
bool smallest;
|
|
bool firstRequest;
|
|
|
|
|
|
/*
|
|
~insertTuple~
|
|
|
|
Inserts a tuple into the local buffer. If the buffer would be
|
|
overflow (size [>] k) , the maximum element is removed from the buffer.
|
|
|
|
*/
|
|
void insertTuple(Tuple* tuple){
|
|
if(elems.size() < k){
|
|
elems.push_back(tuple);
|
|
if(elems.size()==k){
|
|
initializeHeap();
|
|
}
|
|
} else {
|
|
Tuple* maxTuple = elems[0];
|
|
|
|
int cmp = compareTuples(tuple,maxTuple);
|
|
if(cmp>=0){ // tuple >= maxTuple
|
|
tuple->DeleteIfAllowed();
|
|
} else {
|
|
maxTuple->DeleteIfAllowed();
|
|
elems[0] = tuple;
|
|
sink(0,elems.size());
|
|
}
|
|
}
|
|
}
|
|
|
|
inline int compareTuples(Tuple* t1, Tuple* t2){
|
|
return smallest?compareTuplesSmaller(t1,t2): compareTuplesSmaller(t2,t1);
|
|
}
|
|
|
|
int compareTuplesSmaller(Tuple* t1, Tuple* t2){
|
|
for(unsigned int i=0;i<numbers.size();i++){
|
|
Attribute* a1 = t1->GetAttribute(numbers[i]);
|
|
Attribute* a2 = t2->GetAttribute(numbers[i]);
|
|
int cmp = a1->Compare(a2);
|
|
if(cmp!=0){
|
|
return cmp;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void initializeHeap(){
|
|
int s = elems.size()/2;
|
|
for(int i=s; i>=0; i--){
|
|
sink(i,elems.size());
|
|
}
|
|
}
|
|
|
|
void sink(unsigned int i, unsigned int max){
|
|
unsigned int root = i;
|
|
unsigned int son1 = ((i+1)*2) - 1;
|
|
unsigned int son2 = ((i+1)*2+1) - 1;
|
|
unsigned int swapWith;
|
|
|
|
bool done = false;
|
|
do{
|
|
swapWith = root;
|
|
son1 = ((root+1)*2) - 1;
|
|
son2 = ((root+1)*2+1) - 1;
|
|
if(son1<max){
|
|
int cmp = compareTuples(elems[root],elems[son1]);
|
|
if(cmp<0){
|
|
swapWith = son1;
|
|
}
|
|
}
|
|
if(son2 < max){
|
|
int cmp = compareTuples(elems[swapWith],elems[son2]);
|
|
if(cmp<0){
|
|
swapWith = son2;
|
|
}
|
|
}
|
|
if(swapWith!=root){
|
|
Tuple* t1 = elems[root];
|
|
Tuple* t2 = elems[swapWith];
|
|
elems[swapWith] = t1;
|
|
elems[root] = t2;
|
|
}
|
|
done = (swapWith == root);
|
|
root = swapWith;
|
|
|
|
}while(!done);
|
|
}
|
|
};
|
|
|
|
template<bool smaller>
|
|
int ksmallestVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
switch( message )
|
|
{
|
|
case OPEN:{
|
|
qp->Open(args[0].addr);
|
|
CcInt* cck = static_cast<CcInt*>(args[1].addr);
|
|
int attrNum = (static_cast<CcInt*>(args[3].addr))->GetIntval();
|
|
Supplier son;
|
|
Word elem;
|
|
vector<int> attrPos;
|
|
for(int i=0;i<attrNum;i++){
|
|
son = qp->GetSupplier(args[4].addr,i);
|
|
qp->Request(son,elem);
|
|
int anum = (static_cast<CcInt*>(elem.addr))->GetIntval();
|
|
attrPos.push_back(anum);
|
|
}
|
|
int k = cck->IsDefined()?cck->GetIntval():0;
|
|
KSmallestLocalInfo* linfo = new KSmallestLocalInfo(k,attrPos,smaller);
|
|
local.addr = linfo;
|
|
return 0;
|
|
}
|
|
case REQUEST:{
|
|
KSmallestLocalInfo* linfo;
|
|
linfo = static_cast<KSmallestLocalInfo*>(local.addr);
|
|
if(linfo){
|
|
Tuple* tuple = linfo->nextTuple(args[0]);
|
|
if(tuple){
|
|
result.setAddr(tuple);
|
|
return YIELD;
|
|
}
|
|
}
|
|
return CANCEL;
|
|
|
|
}
|
|
case CLOSE:{
|
|
qp->Close(args[0].addr);
|
|
KSmallestLocalInfo* linfo;
|
|
linfo = static_cast<KSmallestLocalInfo*>(local.addr);
|
|
if(linfo){
|
|
delete linfo;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
default:{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
class KSmallestLocalInfo: public ProgressLocalInfo{
|
|
public:
|
|
|
|
/*
|
|
~Constructor~
|
|
|
|
Constructs a localinfo tor given k and the attribute indexes by ~attrnumbers~.
|
|
|
|
*/
|
|
|
|
KSmallestLocalInfo(int ak, vector<int>& attrnumbers,bool Smallest):
|
|
elems(0),numbers(attrnumbers),pos(0),smallest(Smallest),
|
|
firstRequest(true){
|
|
if(ak<0){
|
|
k = 0;
|
|
} else {
|
|
k = ak;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
~Destructor~
|
|
|
|
Destroy this instance and calls DeleteIfAllowed for all
|
|
non processed tuples.
|
|
|
|
|
|
*/
|
|
~KSmallestLocalInfo(){
|
|
for(unsigned int i=0;i<elems.size();i++){
|
|
if(elems[i]){
|
|
elems[i]->DeleteIfAllowed();
|
|
elems[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int getK()
|
|
{
|
|
return k;
|
|
}
|
|
|
|
/*
|
|
~nextTuple~
|
|
|
|
Returns the next tuple within the buffer, or 0 if no tuple is
|
|
available.
|
|
|
|
*/
|
|
Tuple* nextTuple(Word& stream){
|
|
if(firstRequest){
|
|
Word elem;
|
|
qp->Request(stream.addr,elem);
|
|
Tuple* tuple;
|
|
while(qp->Received(stream.addr)){
|
|
tuple = static_cast<Tuple*>(elem.addr);
|
|
insertTuple(tuple);
|
|
qp->Request(stream.addr,elem);
|
|
firstRequest = false;
|
|
}
|
|
}
|
|
|
|
if(pos==0){
|
|
// sort the elements
|
|
if(elems.size()< k){
|
|
initializeHeap();
|
|
}
|
|
if(elems.size()==0){
|
|
return 0;
|
|
}
|
|
for(unsigned int i=elems.size()-1; i>0; i--){
|
|
Tuple* top = elems[0];
|
|
Tuple* last = elems[i];
|
|
elems[0] = last;
|
|
elems[i] = top;
|
|
sink(0,i);
|
|
}
|
|
}
|
|
if(pos<elems.size()){
|
|
Tuple* res = elems[pos];
|
|
elems[pos] = 0;
|
|
pos++;
|
|
return res;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private:
|
|
vector<Tuple*> elems;
|
|
vector<int> numbers;
|
|
unsigned int k;
|
|
unsigned int pos;
|
|
bool smallest;
|
|
bool firstRequest;
|
|
|
|
|
|
/*
|
|
~insertTuple~
|
|
|
|
Inserts a tuple into the local buffer. If the buffer would be
|
|
overflow (size [>] k) , the maximum element is removed from the buffer.
|
|
|
|
*/
|
|
void insertTuple(Tuple* tuple){
|
|
if(elems.size() < k){
|
|
elems.push_back(tuple);
|
|
if(elems.size()==k){
|
|
initializeHeap();
|
|
}
|
|
} else {
|
|
Tuple* maxTuple = elems[0];
|
|
|
|
int cmp = compareTuples(tuple,maxTuple);
|
|
if(cmp>=0){ // tuple >= maxTuple
|
|
tuple->DeleteIfAllowed();
|
|
} else {
|
|
maxTuple->DeleteIfAllowed();
|
|
elems[0] = tuple;
|
|
sink(0,elems.size());
|
|
}
|
|
}
|
|
}
|
|
|
|
inline int compareTuples(Tuple* t1, Tuple* t2){
|
|
return smallest?compareTuplesSmaller(t1,t2): compareTuplesSmaller(t2,t1);
|
|
}
|
|
|
|
int compareTuplesSmaller(Tuple* t1, Tuple* t2){
|
|
for(unsigned int i=0;i<numbers.size();i++){
|
|
Attribute* a1 = t1->GetAttribute(numbers[i]);
|
|
Attribute* a2 = t2->GetAttribute(numbers[i]);
|
|
int cmp = a1->Compare(a2);
|
|
if(cmp!=0){
|
|
return cmp;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void initializeHeap(){
|
|
int s = elems.size()/2;
|
|
for(int i=s; i>=0; i--){
|
|
sink(i,elems.size());
|
|
}
|
|
}
|
|
|
|
void sink(unsigned int i, unsigned int max){
|
|
unsigned int root = i;
|
|
unsigned int son1 = ((i+1)*2) - 1;
|
|
unsigned int son2 = ((i+1)*2+1) - 1;
|
|
unsigned int swapWith;
|
|
|
|
bool done = false;
|
|
do{
|
|
swapWith = root;
|
|
son1 = ((root+1)*2) - 1;
|
|
son2 = ((root+1)*2+1) - 1;
|
|
if(son1<max){
|
|
int cmp = compareTuples(elems[root],elems[son1]);
|
|
if(cmp<0){
|
|
swapWith = son1;
|
|
}
|
|
}
|
|
if(son2 < max){
|
|
int cmp = compareTuples(elems[swapWith],elems[son2]);
|
|
if(cmp<0){
|
|
swapWith = son2;
|
|
}
|
|
}
|
|
if(swapWith!=root){
|
|
Tuple* t1 = elems[root];
|
|
Tuple* t2 = elems[swapWith];
|
|
elems[swapWith] = t1;
|
|
elems[root] = t2;
|
|
}
|
|
done = (swapWith == root);
|
|
root = swapWith;
|
|
|
|
}while(!done);
|
|
}
|
|
};
|
|
|
|
template<bool smaller>
|
|
int ksmallestVM(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
switch( message )
|
|
{
|
|
case OPEN:{
|
|
qp->Open(args[0].addr);
|
|
CcInt* cck = static_cast<CcInt*>(args[1].addr);
|
|
int attrNum = (static_cast<CcInt*>(args[3].addr))->GetIntval();
|
|
Supplier son;
|
|
Word elem;
|
|
vector<int> attrPos;
|
|
for(int i=0;i<attrNum;i++){
|
|
son = qp->GetSupplier(args[4].addr,i);
|
|
qp->Request(son,elem);
|
|
int anum = (static_cast<CcInt*>(elem.addr))->GetIntval();
|
|
attrPos.push_back(anum);
|
|
}
|
|
int k = cck->IsDefined()?cck->GetIntval():0;
|
|
KSmallestLocalInfo* linfo = new KSmallestLocalInfo(k,attrPos,smaller);
|
|
local.addr = linfo;
|
|
return 0;
|
|
}
|
|
case REQUEST:{
|
|
KSmallestLocalInfo* linfo;
|
|
linfo = static_cast<KSmallestLocalInfo*>(local.addr);
|
|
if(linfo){
|
|
Tuple* tuple = linfo->nextTuple(args[0]);
|
|
if(tuple){
|
|
result.setAddr(tuple);
|
|
return YIELD;
|
|
}
|
|
}
|
|
return CANCEL;
|
|
|
|
}
|
|
case CLOSE:{
|
|
qp->Close(args[0].addr);
|
|
#ifndef USE_PROGRESS
|
|
KSmallestLocalInfo* linfo;
|
|
linfo = static_cast<KSmallestLocalInfo*>(local.addr);
|
|
if(linfo){
|
|
delete linfo;
|
|
local.addr=0;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
case CLOSEPROGRESS: {
|
|
KSmallestLocalInfo* linfo;
|
|
linfo = static_cast<KSmallestLocalInfo*>(local.addr);
|
|
if(linfo){
|
|
delete linfo;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUESTPROGRESS: {
|
|
|
|
ProgressInfo p1;
|
|
ProgressInfo *pRes;
|
|
KSmallestLocalInfo* linfo;
|
|
|
|
const double uKsmallest = 0.0001287348; //millisecs per tuple
|
|
const double vKsmallest = 0.0000021203; //millisecs per tuple
|
|
const double wKsmallest = 0.0000000661; //millisecs per k
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
linfo = (KSmallestLocalInfo*) local.addr;
|
|
if(!linfo){
|
|
return CANCEL;
|
|
}
|
|
|
|
if (qp->RequestProgress(args[0].addr, &p1))
|
|
{
|
|
pRes->CopySizes(p1);
|
|
int k = linfo->getK();
|
|
if (k < p1.Card)
|
|
{
|
|
pRes->Card = k;
|
|
}
|
|
else
|
|
{
|
|
pRes->Card = p1.Card;
|
|
}
|
|
pRes->Time = p1.Time + p1.Card *
|
|
(uKsmallest + p1.SizeExt * vKsmallest + k * wKsmallest);
|
|
pRes->Progress = (p1.Progress*p1.Time + linfo->read *
|
|
(uKsmallest + p1.SizeExt * vKsmallest + k * wKsmallest))/pRes->Time;
|
|
pRes->BTime=pRes->Time;
|
|
pRes->BProgress=pRes->Progress;
|
|
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
}
|
|
|
|
default:{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
const string ksmallestSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(tuple([a1:d1, ... ,an:dn])))"
|
|
" x int x a_k x ... a_m -> "
|
|
"(stream (tuple(...)))</text--->"
|
|
"<text>_ ksmallest [k ; list ]</text--->"
|
|
"<text>returns the k smallest elements from the input stream"
|
|
"</text--->"
|
|
"<text>query employee feed ksmallest[10; Salary] consume "
|
|
"</text--->"
|
|
") )";
|
|
|
|
const string kbiggestSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(tuple([a1:d1, ... ,an:dn])))"
|
|
" x int x a_k x ... a_m -> "
|
|
"(stream (tuple(...)))</text--->"
|
|
"<text>_ kbiggest [k ; list ]</text--->"
|
|
"<text>returns the k biggest elements from the input stream"
|
|
"</text--->"
|
|
"<text>query employee feed kbiggest[10; Salary] consume "
|
|
"</text--->"
|
|
") )";
|
|
|
|
Operator ksmallest (
|
|
"ksmallest",
|
|
ksmallestSpec,
|
|
ksmallestVM<true>,
|
|
Operator::SimpleSelect,
|
|
ksmallestTM
|
|
);
|
|
|
|
Operator kbiggest (
|
|
"kbiggest",
|
|
kbiggestSpec,
|
|
ksmallestVM<false>,
|
|
Operator::SimpleSelect,
|
|
ksmallestTM
|
|
);
|
|
|
|
/*
|
|
2.11 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.
|
|
|
|
2.11.1 Type mapping function of operator ~sortBy~
|
|
|
|
Type mapping for ~sortBy~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) ((xi1 asc/desc) ... (xij asc/desc)))
|
|
-> (stream (tuple ((x1 t1)...(xn tn)))
|
|
APPEND (j i1 asc/desc i2 asc/desc ... ij asc/desc)
|
|
----
|
|
|
|
*/
|
|
|
|
static const char* sortAscending = "asc";
|
|
static const char* sortDescending = "desc";
|
|
|
|
ListExpr SortByTypeMap( ListExpr args )
|
|
{
|
|
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
if(!listutils::isTupleStream(stream)){
|
|
return listutils::typeError("first argument must be a tuple stream");
|
|
}
|
|
ListExpr attrtype;
|
|
string attrname, argstr;
|
|
ListExpr sortSpecification = nl->Second(args);
|
|
int numberOfSortAttrs = nl->ListLength(sortSpecification);
|
|
|
|
if(numberOfSortAttrs < 0){
|
|
return listutils::typeError("Attribute list may not be enpty!");
|
|
}
|
|
|
|
ListExpr sortOrderDescription =
|
|
nl->OneElemList(nl->IntAtom(numberOfSortAttrs));
|
|
ListExpr sortOrderDescriptionLastElement = sortOrderDescription;
|
|
|
|
ListExpr rest = sortSpecification;
|
|
while(!nl->IsEmpty(rest))
|
|
{
|
|
ListExpr attributeSpecification = nl->First(rest);
|
|
rest = nl->Rest(rest);
|
|
|
|
nl->WriteToString(argstr, attributeSpecification);
|
|
|
|
int length = nl->ListLength(attributeSpecification);
|
|
string err = "second argument must be a list with"
|
|
" elements of form: (attrname [asc, desc])|attrname";
|
|
if(!nl->IsAtom(attributeSpecification) && (length != 2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
bool isAscending=true;
|
|
if(length==2) {
|
|
if(nl->AtomType(nl->First(attributeSpecification))!=SymbolType){
|
|
return listutils::typeError(err);
|
|
}
|
|
attrname = nl->SymbolValue(nl->First(attributeSpecification));
|
|
ListExpr order = nl->Second(attributeSpecification);
|
|
if(listutils::isSymbol(order, sortAscending)){
|
|
isAscending = true;
|
|
} else if(listutils::isSymbol(order, sortDescending)){
|
|
isAscending = false;
|
|
} else {
|
|
return listutils::typeError("invalid sorting criterion");
|
|
}
|
|
} else {
|
|
if(nl->AtomType(attributeSpecification)!=SymbolType){
|
|
return listutils::typeError(err);
|
|
}
|
|
attrname = nl->SymbolValue(attributeSpecification);
|
|
}
|
|
|
|
int j = FindAttribute(nl->Second(nl->Second(stream)),
|
|
attrname, attrtype);
|
|
if (j > 0) {
|
|
sortOrderDescriptionLastElement =
|
|
nl->Append(sortOrderDescriptionLastElement, nl->IntAtom(j));
|
|
sortOrderDescriptionLastElement =
|
|
nl->Append(sortOrderDescriptionLastElement,
|
|
nl->BoolAtom(isAscending));
|
|
} else {
|
|
return listutils::typeError("Unknown attribute name found");
|
|
}
|
|
}
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
sortOrderDescription, stream);
|
|
}
|
|
/*
|
|
2.11.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.
|
|
|
|
*/
|
|
template<bool lexicographically> int
|
|
sortby_vm(Word* args, Word& result, int message, Word& local, Supplier s);
|
|
/*
|
|
|
|
2.11.3 Specification of operator ~sortBy~
|
|
|
|
*/
|
|
const string SortBySpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn])))"
|
|
" ((xi1 asc/desc) ... (xij [asc/desc]))) -> "
|
|
"(stream (tuple([a1:d1, ... ,an:dn])))</text--->"
|
|
"<text>_ sortby [list]</text--->"
|
|
"<text>Sorts input stream according to a list "
|
|
"of attributes ai1 ... aij. \n"
|
|
"If no order is specified, ascending is assumed."
|
|
"</text--->"
|
|
"<text>query employee feed sortby[DeptNo asc] "
|
|
"consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.11.4 Definition of operator ~sortBy~
|
|
|
|
*/
|
|
Operator extrelsortby (
|
|
"sortby_old", // name
|
|
SortBySpec, // specification
|
|
sortby_vm<false>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SortByTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.12 Operator ~sort~
|
|
|
|
This operator sorts a stream of tuples lexicographically.
|
|
|
|
2.12.1 Type mapping function of operator ~sort~
|
|
|
|
Type mapping for ~sort~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))))
|
|
-> (stream (tuple ((x1 t1)...(xn tn)))
|
|
----
|
|
|
|
*/
|
|
template<bool isSort> ListExpr
|
|
IdenticalTypeMap( ListExpr args )
|
|
{
|
|
if(nl->ListLength(args)!=1){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
if(!listutils::isTupleStream(arg)){
|
|
return listutils::typeError("tuple stream expected");
|
|
}
|
|
return arg;
|
|
|
|
}
|
|
/*
|
|
2.12.2 Specification of operator ~sort\_old~
|
|
|
|
*/
|
|
const string SortSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn]"
|
|
")))) -> (stream (tuple([a1:d1, ... ,an:dn])))"
|
|
"</text--->"
|
|
"<text>_ sort_old</text--->"
|
|
"<text>Sorts input stream lexicographically."
|
|
"</text--->"
|
|
"<text>query cities feed sort_old consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.12.3 Definition of operator ~sort\_old~
|
|
|
|
*/
|
|
Operator extrelsort (
|
|
"sort_old", // name
|
|
SortSpec, // specification
|
|
sortby_vm<true>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
IdenticalTypeMap<true> // type mapping
|
|
);
|
|
|
|
/*
|
|
2.13 Operator ~rdup~
|
|
|
|
This operator removes duplicates from a sorted stream.
|
|
|
|
2.13.1 Value mapping function of operator ~rdup~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
// standard version
|
|
|
|
int RdupValueMapping(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
Word tuple(Address(0));
|
|
LexicographicalTupleCmpAlmost cmp;
|
|
Tuple* current = 0;
|
|
RTuple* lastOutput = 0;
|
|
|
|
switch(message)
|
|
{
|
|
case OPEN: {
|
|
qp->Open(args[0].addr);
|
|
local.addr = 0;
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
while(true)
|
|
{
|
|
qp->Request(args[0].addr, tuple);
|
|
if(qp->Received(args[0].addr))
|
|
{
|
|
// stream delivered a new tuple
|
|
if(local.addr != 0)
|
|
{
|
|
// there is a last tuple
|
|
current = static_cast<Tuple*>(tuple.addr);
|
|
lastOutput = static_cast<RTuple*>(local.addr);
|
|
if(cmp(current, lastOutput->tuple)
|
|
|| cmp(lastOutput->tuple, current))
|
|
{
|
|
// tuples are not equal. Return the tuple
|
|
// stored in local info and store the current one
|
|
// there.
|
|
|
|
*lastOutput = RTuple( current );
|
|
result = tuple;
|
|
return YIELD;
|
|
}
|
|
else
|
|
{
|
|
// tuples are equal
|
|
current->DeleteIfAllowed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no last tuple stored
|
|
local.addr = new RTuple( static_cast<Tuple*>(tuple.addr) );
|
|
result = tuple;
|
|
return YIELD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.addr = 0;
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
case CLOSE: {
|
|
if( local.addr != 0 ){ // check if local is present
|
|
lastOutput = static_cast<RTuple*>(local.addr);
|
|
delete lastOutput;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
# else
|
|
|
|
// progress version
|
|
|
|
|
|
struct RdupLocalInfo
|
|
{
|
|
RdupLocalInfo(): localTuple(0),read(0),returned(0),stableValue(50),cmp1(){}
|
|
~RdupLocalInfo(){
|
|
if(localTuple){
|
|
localTuple->DeleteIfAllowed();
|
|
localTuple=0;
|
|
}
|
|
}
|
|
Tuple* localTuple;
|
|
int read, returned, stableValue;
|
|
LexicographicalTupleCmpAlmost cmp1;
|
|
};
|
|
|
|
|
|
int RdupValueMapping(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
|
|
RdupLocalInfo* rli = (RdupLocalInfo*) local.addr;
|
|
|
|
switch(message)
|
|
{
|
|
case OPEN: {
|
|
|
|
if ( rli ) {
|
|
delete rli;
|
|
}
|
|
rli = new RdupLocalInfo();
|
|
local.setAddr(rli);
|
|
qp->Open(args[0].addr);
|
|
return 0;
|
|
} case REQUEST: {
|
|
if(!rli){
|
|
return CANCEL;
|
|
}
|
|
Tuple* currentTuple;
|
|
Tuple* lastOutputTuple;
|
|
Word tuple;
|
|
|
|
while(true)
|
|
{
|
|
qp->Request(args[0].addr, tuple);
|
|
if(qp->Received(args[0].addr))
|
|
{
|
|
// stream deliverd a new tuple
|
|
|
|
rli->read++;
|
|
if(rli->localTuple != 0)
|
|
{
|
|
// there is a last tuple
|
|
|
|
currentTuple = (Tuple*)tuple.addr;
|
|
lastOutputTuple = rli->localTuple;
|
|
if(rli->cmp1(currentTuple, lastOutputTuple)!=0)
|
|
{
|
|
// tuples are not equal. Return the tuple
|
|
// stored in local info and store the current one
|
|
// there.
|
|
lastOutputTuple->DeleteIfAllowed();
|
|
|
|
rli->returned++;
|
|
rli->localTuple = currentTuple;
|
|
|
|
currentTuple->IncReference();
|
|
result.setAddr(currentTuple);
|
|
return YIELD;
|
|
}
|
|
else
|
|
{
|
|
// tuples are equal
|
|
currentTuple->DeleteIfAllowed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no last tuple stored
|
|
currentTuple = (Tuple*)tuple.addr;
|
|
rli->returned++;
|
|
rli->localTuple = currentTuple;
|
|
currentTuple->IncReference();
|
|
result.setAddr(currentTuple);
|
|
return YIELD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// last tuple of the stream
|
|
lastOutputTuple = rli->localTuple;
|
|
if(lastOutputTuple != 0)
|
|
{
|
|
lastOutputTuple->DeleteIfAllowed();
|
|
rli->localTuple = 0;
|
|
}
|
|
return CANCEL;
|
|
}
|
|
}
|
|
} case CLOSE: {
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
|
|
} case CLOSEPROGRESS: {
|
|
if ( rli )
|
|
{
|
|
delete rli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
|
|
} case REQUESTPROGRESS: {
|
|
ProgressInfo p1;
|
|
ProgressInfo* pRes;
|
|
const double uRdup = 0.01; // time per tuple
|
|
const double vRdup = 0.1; // time per comparison
|
|
const double wRdup = 0.9; // default selectivity (= 10% duplicates)
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
|
|
if ( qp->RequestProgress(args[0].addr, &p1) )
|
|
{
|
|
pRes->CopySizes(p1);
|
|
pRes->Time = p1.Time + p1.Card * uRdup * vRdup;
|
|
|
|
pRes->CopyBlocking(p1); //non-blocking operator
|
|
|
|
if (rli) {
|
|
if (rli->returned > rli->stableValue) {
|
|
pRes->Card = p1.Card *
|
|
((double) rli->returned / (double) (rli->read));
|
|
|
|
if ( p1.BTime < 0.1 && pipelinedProgress ){ //non-blocking,
|
|
//use pipelining
|
|
pRes->Progress = p1.Progress;
|
|
} else {
|
|
pRes->Progress = (p1.Progress * p1.Time
|
|
+ rli->read * uRdup * vRdup) / pRes->Time;
|
|
}
|
|
|
|
return YIELD;
|
|
}
|
|
}
|
|
|
|
pRes->Card = p1.Card * wRdup;
|
|
|
|
if ( p1.BTime < 0.1 && pipelinedProgress ){ //non-blocking,
|
|
//use pipelining
|
|
pRes->Progress = p1.Progress;
|
|
} else {
|
|
pRes->Progress = (p1.Progress * p1.Time) / pRes->Time;
|
|
}
|
|
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
2.13.2 Specification of operator ~rdup~
|
|
|
|
*/
|
|
const string RdupSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn]))))"
|
|
" -> (stream (tuple([a1:d1, ... ,an:dn])))"
|
|
"</text--->"
|
|
"<text>_ rdup</text--->"
|
|
"<text>Removes duplicates from a sorted "
|
|
"stream.</text--->"
|
|
"<text>query twenty feed ten feed concat sort "
|
|
"rdup consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.13.3 Definition of operator ~rdup~
|
|
|
|
*/
|
|
Operator extrelrdup (
|
|
"rdup", // name
|
|
RdupSpec, // specification
|
|
RdupValueMapping, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
IdenticalTypeMap<false> // type mapping
|
|
);
|
|
|
|
/*
|
|
2.14 Set Operators
|
|
|
|
These operators compute set operations on two sorted stream.
|
|
|
|
2.14.1 Generic Type Mapping for Set Operations
|
|
|
|
*/
|
|
template<int errorMessageIdx> ListExpr
|
|
SetOpTypeMap( ListExpr args )
|
|
{
|
|
string err = " stream(tuple(X) x stream(tuple(X)) expected";
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!listutils::isTupleStream(nl->First(args)) ||
|
|
!nl->Equal(nl->First(args), nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return nl->First(args);
|
|
}
|
|
/*
|
|
2.14.2 Auxiliary Class for Set Operations
|
|
|
|
*/
|
|
|
|
class SetOperation
|
|
{
|
|
public:
|
|
bool outputAWithoutB;
|
|
bool outputBWithoutA;
|
|
bool outputMatches;
|
|
|
|
private:
|
|
LexicographicalTupleCmp tupleCmp;
|
|
|
|
Word streamA;
|
|
Word streamB;
|
|
|
|
Tuple* currentATuple;
|
|
Tuple* currentBTuple;
|
|
|
|
/*
|
|
~NextATuple~
|
|
|
|
Stores the next tuple from stream A into currentATuple which is
|
|
unequal to the currently store A tuple.
|
|
|
|
*/
|
|
void NextATuple()
|
|
{
|
|
Word tuple;
|
|
qp->Request(streamA.addr,tuple);
|
|
if(!currentATuple){ // first tuple
|
|
if(qp->Received(streamA.addr)){
|
|
currentATuple = static_cast<Tuple*>(tuple.addr);
|
|
}
|
|
} else { // currentAtuple already exists
|
|
while(qp->Received(streamA.addr) &&
|
|
TuplesEqual(currentATuple, static_cast<Tuple*>(tuple.addr))){
|
|
(static_cast<Tuple*>(tuple.addr))->DeleteIfAllowed();
|
|
qp->Request(streamA.addr,tuple);
|
|
}
|
|
currentATuple->DeleteIfAllowed(); // remove from inputstream or buffer
|
|
if(qp->Received(streamA.addr)){
|
|
currentATuple = static_cast<Tuple*>(tuple.addr);
|
|
} else {
|
|
currentATuple=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void NextBTuple()
|
|
{
|
|
Word tuple;
|
|
if(!currentBTuple){ // first tuple
|
|
qp->Request(streamB.addr,tuple);
|
|
if(qp->Received(streamB.addr)){
|
|
currentBTuple = static_cast<Tuple*>( tuple.addr);
|
|
}
|
|
} else { // currentAtuple already exists
|
|
qp->Request(streamB.addr,tuple);
|
|
while(qp->Received(streamB.addr) &&
|
|
TuplesEqual(currentBTuple, static_cast<Tuple*>(tuple.addr))){
|
|
(static_cast<Tuple*>(tuple.addr))->DeleteIfAllowed();
|
|
qp->Request(streamB.addr,tuple);
|
|
}
|
|
currentBTuple->DeleteIfAllowed();
|
|
if(qp->Received(streamB.addr)){
|
|
currentBTuple = static_cast<Tuple*>(tuple.addr);
|
|
} else {
|
|
currentBTuple=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool TuplesEqual(Tuple* a, Tuple* b)
|
|
{
|
|
return (tupleCmp(a,b)==0);
|
|
}
|
|
|
|
public:
|
|
|
|
SetOperation(Word streamA, Word streamB)
|
|
{
|
|
this->streamA = streamA;
|
|
this->streamB = streamB;
|
|
|
|
currentATuple = 0;
|
|
currentBTuple = 0;
|
|
|
|
qp->Open(streamA.addr);
|
|
qp->Open(streamB.addr);
|
|
NextATuple();
|
|
NextBTuple();
|
|
}
|
|
|
|
virtual ~SetOperation()
|
|
{
|
|
qp->Close(streamA.addr);
|
|
qp->Close(streamB.addr);
|
|
if(currentATuple){
|
|
currentATuple->DeleteIfAllowed();
|
|
currentATuple=0;
|
|
}
|
|
if(currentBTuple){
|
|
currentBTuple->DeleteIfAllowed();
|
|
currentBTuple=0;
|
|
}
|
|
}
|
|
|
|
Tuple* NextResultTuple()
|
|
{
|
|
Tuple* result = 0;
|
|
while(result == 0)
|
|
{
|
|
if(currentATuple == 0)
|
|
{
|
|
if(currentBTuple == 0)
|
|
{ // stream exhausted
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if(outputBWithoutA)
|
|
{
|
|
result = currentBTuple;
|
|
result->IncReference();
|
|
NextBTuple();
|
|
}
|
|
else
|
|
{
|
|
currentBTuple->DeleteIfAllowed();
|
|
currentBTuple = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(currentBTuple == 0)
|
|
{
|
|
if(outputAWithoutB)
|
|
{
|
|
result = currentATuple;
|
|
result->IncReference();
|
|
NextATuple();
|
|
}
|
|
else
|
|
{
|
|
currentATuple->DeleteIfAllowed();
|
|
currentATuple=0;
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* both current tuples != 0 */
|
|
int cmp = tupleCmp(currentATuple, currentBTuple);
|
|
if(cmp < 0)
|
|
{
|
|
if(outputAWithoutB)
|
|
{
|
|
result = currentATuple;
|
|
result->IncReference();
|
|
}
|
|
NextATuple();
|
|
}
|
|
else if(cmp > 0)
|
|
{
|
|
if(outputBWithoutA)
|
|
{
|
|
result = currentBTuple;
|
|
result->IncReference();
|
|
}
|
|
NextBTuple();
|
|
}
|
|
else
|
|
{
|
|
/* found match */
|
|
Tuple* match = currentATuple;
|
|
if(outputMatches)
|
|
{
|
|
result = match;
|
|
result->IncReference();
|
|
}
|
|
NextATuple();
|
|
NextBTuple();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
|
|
/*
|
|
2.14.3 Generic Value Mapping Function for Set Operations
|
|
|
|
*/
|
|
|
|
template<bool outputAWithoutB,
|
|
bool outputBWithoutA,
|
|
bool outputMatches>
|
|
int
|
|
SetOpValueMapping(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
SetOperation* localInfo;
|
|
|
|
switch(message)
|
|
{
|
|
case OPEN:
|
|
localInfo = new SetOperation(args[0], args[1]);
|
|
localInfo->outputBWithoutA = outputBWithoutA;
|
|
localInfo->outputAWithoutB = outputAWithoutB;
|
|
localInfo->outputMatches = outputMatches;
|
|
|
|
local.setAddr(localInfo);
|
|
return 0;
|
|
case REQUEST:
|
|
localInfo = (SetOperation*)local.addr;
|
|
result.setAddr(localInfo->NextResultTuple());
|
|
return result.addr != 0 ? YIELD : CANCEL;
|
|
case CLOSE:
|
|
if(local.addr){
|
|
localInfo = (SetOperation*)local.addr;
|
|
delete localInfo;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
2.14.4 Specification of Operator ~mergesec~
|
|
|
|
*/
|
|
const string MergeSecSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple ((x1 t1) ... "
|
|
"(xn tn)))) stream (tuple ((x1 t1) ... (xn tn)"
|
|
")))) -> (stream (tuple ((x1 t1) ... (xn tn))))"
|
|
"</text--->"
|
|
"<text>_ _ mergesec</text--->"
|
|
"<text>Computes the intersection of two sorted "
|
|
"streams.</text--->"
|
|
"<text>query twenty feed oddtwenty feed mergesec"
|
|
" consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.14.5 Definition of Operator ~mergesec~
|
|
|
|
*/
|
|
Operator extrelmergesec(
|
|
"mergesec", // name
|
|
MergeSecSpec, // specification
|
|
SetOpValueMapping<false, false, true>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SetOpTypeMap<0> // type mapping
|
|
);
|
|
|
|
/*
|
|
2.14.6 Specification of Operator ~mergediff~
|
|
|
|
*/
|
|
const string MergeDiffSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple ((x1 t1) ... (xn tn)"
|
|
"))) stream (tuple ((x1 t1) ... (xn tn))))) ->"
|
|
" (stream (tuple ((x1 t1) ... (xn tn))))"
|
|
"</text--->"
|
|
"<text>_ _ mergediff</text--->"
|
|
"<text>Computes the difference of two sorted "
|
|
"streams.</text--->"
|
|
"<text>query twenty feed oddtwenty feed"
|
|
" mergediff consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.14.7 Definition of Operator ~mergediff~
|
|
|
|
*/
|
|
Operator extrelmergediff(
|
|
"mergediff", // name
|
|
MergeDiffSpec, // specification
|
|
SetOpValueMapping<true, false, false>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SetOpTypeMap<1> // type mapping
|
|
);
|
|
|
|
/*
|
|
2.14.8 Specification of Operator ~mergeunion~
|
|
|
|
*/
|
|
const string
|
|
MergeUnionSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple ((x1 t1) ... (xn tn))))"
|
|
"stream (tuple ((x1 t1) ... (xn tn))))) -> (stream"
|
|
" (tuple ((x1 t1) ... (xn tn))))</text--->"
|
|
"<text>_ _ mergeunion</text--->"
|
|
"<text>Computes the union of two sorted streams."
|
|
"</text--->"
|
|
"<text>query twenty feed oddtwenty feed "
|
|
"mergeunion consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.14.9 Definition of Operator ~mergeunion~
|
|
|
|
*/
|
|
Operator extrelmergeunion(
|
|
"mergeunion", // name
|
|
MergeUnionSpec, // specification
|
|
SetOpValueMapping<true, true, true>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SetOpTypeMap<2> // type mapping
|
|
);
|
|
|
|
/*
|
|
2.15 Operator ~mergejoin~
|
|
|
|
This operator computes the equijoin two streams.
|
|
|
|
2.15.1 Type mapping function of operators ~mergejoin~ and ~hashjoin~
|
|
|
|
Type mapping for ~mergejoin~ is
|
|
|
|
---- ((stream (tuple ((x1 t1) ... (xn tn))))
|
|
(stream (tuple ((y1 d1) ... (ym dm)))) xi yj)
|
|
-> (stream (tuple ((x1 t1) ... (xn tn) (y1 d1) ... (ym dm))))
|
|
APPEND (i j)
|
|
----
|
|
|
|
Type mapping for ~hashjoin~ is
|
|
|
|
---- ((stream (tuple ((x1 t1) ... (xn tn))))
|
|
(stream (tuple ((y1 d1) ... (ym dm)))) xi yj int)
|
|
-> (stream (tuple ((x1 t1) ... (xn tn) (y1 d1) ... (ym dm))))
|
|
APPEND (i j)
|
|
----
|
|
|
|
|
|
*/
|
|
bool arelTypeEqual (NList& t1, NList& t2)
|
|
{
|
|
NList firstA, firstB;
|
|
NList attrA = t1.second().second();
|
|
NList attrB = t2.second().second();
|
|
if (attrA.length() != attrB.length())
|
|
return false;
|
|
else
|
|
{
|
|
bool equal = true;
|
|
while (!attrA.isEmpty() && equal)
|
|
{
|
|
firstA = attrA.first().second();
|
|
firstB = attrB.first().second();
|
|
if (firstA.hasLength(2) && firstB.hasLength(2) &&
|
|
firstA.first().isSymbol() && firstB.first().isSymbol() &&
|
|
firstA.first().str() == "arel" && firstB.first().str() == "arel")
|
|
equal = arelTypeEqual(firstA, firstB);
|
|
else
|
|
equal = nl->Equal(firstA.listExpr(), firstB.listExpr());
|
|
attrA.rest();
|
|
attrB.rest();
|
|
}
|
|
return equal;
|
|
}
|
|
}
|
|
|
|
template<bool OptionalIntAllowed, int defaultValue>
|
|
ListExpr JoinTypeMap (ListExpr args)
|
|
{
|
|
string err = "stream(tuple[y1 : d1, ..., yn : dn]) x "
|
|
"stream(tuple[z1 : e1, ..., zn : en]) x di x e1 ";
|
|
|
|
|
|
|
|
if(OptionalIntAllowed){
|
|
err += " [ x int]";
|
|
}
|
|
|
|
int len = nl->ListLength(args);
|
|
|
|
// check for correct number of arguments
|
|
if( (len!=4) && (len!=5)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if((len==5) && !OptionalIntAllowed){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
// check the first 4 arguments stream x stream x attrname x attrname
|
|
ListExpr stream1 = nl->First(args);
|
|
ListExpr stream2 = nl->Second(args);
|
|
ListExpr attr1 = nl->Third(args);
|
|
ListExpr attr2 = nl->Fourth(args);
|
|
if(!listutils::isTupleStream(stream1) ||
|
|
!listutils::isTupleStream(stream2) ||
|
|
nl->AtomType(attr1)!=SymbolType ||
|
|
nl->AtomType(attr2)!=SymbolType){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
// check the last element if present
|
|
if(len==5){
|
|
ListExpr size = nl->Fifth(args);
|
|
if(!listutils::isSymbol(size,CcInt::BasicType())){
|
|
return listutils::typeError(err + "(last arg is not an int");
|
|
}
|
|
}
|
|
|
|
// check for correct naming of attributes
|
|
ListExpr list1 = nl->Second(nl->Second(stream1));
|
|
ListExpr list2 = nl->Second(nl->Second(stream2));
|
|
if(!listutils::disjointAttrNames(list1,list2)){
|
|
return listutils::typeError("Attribute lists are not disjoint");
|
|
}
|
|
|
|
|
|
ListExpr list = ConcatLists(list1, list2);
|
|
ListExpr outlist = nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()), list));
|
|
|
|
string attrAName = nl->SymbolValue(attr1);
|
|
string attrBName = nl->SymbolValue(attr2);
|
|
|
|
ListExpr attrTypeA, attrTypeB;
|
|
|
|
int attrAIndex = listutils::findAttribute(list1, attrAName, attrTypeA);
|
|
if(attrAIndex<1){
|
|
return listutils::typeError("Attributename " + attrAName +
|
|
" not found in the first argument");
|
|
}
|
|
|
|
int attrBIndex = listutils::findAttribute(list2, attrBName, attrTypeB);
|
|
if(attrBIndex<1){
|
|
return listutils::typeError("Attributename " + attrBName +
|
|
" not found in the second argument");
|
|
}
|
|
|
|
NList attrA(attrTypeA);
|
|
NList attrB(attrTypeB);
|
|
if (attrA.hasLength(2) && attrB.hasLength(2) &&
|
|
attrA.first().isSymbol() && attrB.first().isSymbol() &&
|
|
attrA.first().str() == "arel" && attrB.first().str() == "arel")
|
|
{
|
|
if (!arelTypeEqual(attrA, attrB))
|
|
return listutils::typeError("different types selected for operation");
|
|
}
|
|
else
|
|
{
|
|
if(!nl->Equal(attrTypeA, attrTypeB)){
|
|
return listutils::typeError("different types selected for operation");
|
|
}
|
|
}
|
|
|
|
ListExpr joinAttrDescription;
|
|
|
|
if(!OptionalIntAllowed || len == 5){
|
|
joinAttrDescription = nl->TwoElemList(nl->IntAtom(attrAIndex),
|
|
nl->IntAtom(attrBIndex));
|
|
} else { // additionally add the default value
|
|
joinAttrDescription = nl->ThreeElemList( nl->IntAtom(defaultValue),
|
|
nl->IntAtom(attrAIndex),
|
|
nl->IntAtom(attrBIndex));
|
|
}
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
joinAttrDescription, outlist);
|
|
}
|
|
|
|
/*
|
|
Some explicit instantiations in order to use them also
|
|
outside of the implementation file.
|
|
|
|
*/
|
|
|
|
template ListExpr
|
|
JoinTypeMap<false,1>(ListExpr args);
|
|
|
|
|
|
template ListExpr
|
|
JoinTypeMap<true,1>(ListExpr args);
|
|
|
|
|
|
|
|
/*
|
|
2.15.1 Create Cost Estimation
|
|
|
|
*/
|
|
template<bool expectSorted>
|
|
CostEstimation* MergeJoinCostEstimationFunc() {
|
|
return new MergeJoinCostEstimation<expectSorted>();
|
|
}
|
|
|
|
/*
|
|
2.15.2 Value mapping functions of operator ~mergejoin~
|
|
|
|
*/
|
|
|
|
template<bool expectSorted> int
|
|
mergejoin_vm( Word* args, Word& result,
|
|
int message, Word& local, Supplier s );
|
|
|
|
template<bool expectSorted> int
|
|
mergejoin_vm( Word* args, Word& result,
|
|
int message, Word& local, Supplier s );
|
|
|
|
|
|
/*
|
|
|
|
2.15.3 Specification of operator ~mergejoin~
|
|
|
|
*/
|
|
const string MergeJoinSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple ((x1 t1) ... "
|
|
"(xn tn)))) (stream (tuple ((y1 d1) ... "
|
|
"(ym dm)))) xi yj) -> (stream (tuple ((x1 t1)"
|
|
" ... (xn tn) (y1 d1) ... (ym dm))))"
|
|
"</text--->"
|
|
"<text>_ _ mergejoin [_, _]</text--->"
|
|
"<text>Computes the equijoin two streams. "
|
|
"Expects that input streams are sorted."
|
|
"</text--->"
|
|
"<text>query duplicates feed ten feed "
|
|
"rename[A] mergejoin[no, no_A] sort rdup "
|
|
"consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.15.4 Definition of operator ~mergejoin~
|
|
|
|
*/
|
|
#ifndef USE_PROGRESS
|
|
Operator extrelmergejoin(
|
|
"mergejoin", // name
|
|
MergeJoinSpec, // specification
|
|
mergejoin_vm<true>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
JoinTypeMap<false,0> // type mapping
|
|
);
|
|
#else
|
|
Operator extrelmergejoin(
|
|
"mergejoin", // name
|
|
MergeJoinSpec, // specification
|
|
mergejoin_vm<true>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
JoinTypeMap<false,0>, // type mapping
|
|
MergeJoinCostEstimationFunc<true> // cost estimation
|
|
);
|
|
#endif
|
|
|
|
|
|
/*
|
|
2.16 Operator ~sortmergejoin~
|
|
|
|
This operator sorts two input streams and computes their equijoin.
|
|
|
|
2.16.1 Specification of operator ~sortmergejoin~
|
|
|
|
*/
|
|
const string SortMergeJoinSpec = "( ( \"Signature\" \"Syntax\" "
|
|
"\"Meaning\" \"Example\" ) "
|
|
"( <text>((stream (tuple ((x1 t1) ... "
|
|
"(xn tn)))) (stream (tuple ((y1 d1) ..."
|
|
" (ym dm)))) xi yj) -> (stream (tuple "
|
|
"((x1 t1) ... (xn tn) (y1 d1) ... (ym dm)"
|
|
")))</text--->"
|
|
"<text>_ _ sortmergejoin [ _ , _ ]"
|
|
"</text--->"
|
|
"<text>Computes the equijoin two streams."
|
|
"</text--->"
|
|
"<text>query duplicates feed ten feed "
|
|
"mergejoin[no, nr] consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.16.2 Definition of operator ~sortmergejoin~
|
|
|
|
*/
|
|
#ifndef USE_PROGRESS
|
|
Operator extrelsortmergejoin(
|
|
"sortmergejoin_old", // name
|
|
SortMergeJoinSpec, // specification
|
|
mergejoin_vm<false>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
JoinTypeMap<false,0> // type mapping
|
|
);
|
|
|
|
#else
|
|
|
|
Operator extrelsortmergejoin(
|
|
"sortmergejoin_old", // name
|
|
SortMergeJoinSpec, // specification
|
|
mergejoin_vm<false>, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
JoinTypeMap<false,0>, // type mapping
|
|
MergeJoinCostEstimationFunc<false> // cost estimation
|
|
);
|
|
#endif
|
|
|
|
/*
|
|
2.17 Operator ~hashjoin~
|
|
|
|
This operator computes the equijoin two streams via a hash join.
|
|
The user can specify the number of hash buckets.
|
|
|
|
2.17.2 Specification of Operator ~hashjoin~
|
|
|
|
*/
|
|
const string HashJoinSpec = "( ( \"Signature\" \"Syntax\" "
|
|
"\"Meaning\" \"Example\" \"Remark\" ) "
|
|
"( <text>((stream (tuple ((x1 t1) ... "
|
|
"(xn tn)))) (stream (tuple ((y1 d1) ... "
|
|
"(ym dm)))) xi yj [nbuckets]) -> (stream "
|
|
"(tuple ((x1 t1) ... (xn tn) (y1 d1) ..."
|
|
" (ym dm))))</text--->"
|
|
"<text> _ _ hashjoin [ _ , _ , _ ]"
|
|
"</text--->"
|
|
"<text>Computes the equijoin two streams "
|
|
"via a hash join. The number of hash buckets"
|
|
" is given by the parameter nBuckets. If the number"
|
|
" of buckets is omitted, a default value of 99997"
|
|
" is used"
|
|
"</text--->"
|
|
"<text>query Employee feed Dept feed "
|
|
"rename[A] hashjoin[DeptNr, DeptNr_A, 17] "
|
|
"sort consume</text--->"
|
|
"<text>The hash table is created from the 2nd "
|
|
"argument. If it does not fit into memory, "
|
|
"the 1st argument will also be materialized."
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.17.1 Value Mapping Function of Operator ~hashjoin~
|
|
|
|
*/
|
|
int HashJoin(Word* args, Word& result, int message, Word& local, Supplier s);
|
|
/*
|
|
|
|
2.17.3 Definition of Operator ~hashjoin~
|
|
|
|
*/
|
|
Operator extrelhashjoin(
|
|
"hashjoin", // name
|
|
HashJoinSpec, // specification
|
|
HashJoin, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
JoinTypeMap<true, 99997> // type mapping
|
|
);
|
|
|
|
|
|
/*
|
|
2.18 Operator ~extend~
|
|
|
|
Extends each input tuple by new attributes as specified in the parameter list.
|
|
|
|
2.18.1 Type mapping function of operator ~extend~
|
|
|
|
Type mapping for ~extend~ is
|
|
|
|
---- ((stream X) ((b1 (map x y1)) ... (bm (map x ym))))
|
|
|
|
-> (stream (tuple ((a1 x1) ... (an xn) (b1 y1) ... (bm ym)))))
|
|
|
|
where X = (tuple ((a1 x1) ... (an xn)))
|
|
----
|
|
|
|
*/
|
|
ListExpr ExtendTypeMap( ListExpr args )
|
|
{
|
|
ListExpr errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR"));
|
|
|
|
if(nl->ListLength(args)!=2){
|
|
ErrorReporter::ReportError("two elements expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
|
|
if(!IsStreamDescription(stream)){
|
|
ErrorReporter::ReportError("first argument is not a tuple stream");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr tuple = nl->Second(stream);
|
|
|
|
ListExpr functions = nl->Second(args);
|
|
if(nl->ListLength(functions)<1){
|
|
ErrorReporter::ReportError("at least one function expected");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
// copy attrlist to newattrlist
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
ListExpr newAttrList = nl->OneElemList(nl->First(attrList));
|
|
ListExpr lastlistn = newAttrList;
|
|
attrList = nl->Rest(attrList);
|
|
while (!(nl->IsEmpty(attrList)))
|
|
{
|
|
lastlistn = nl->Append(lastlistn,nl->First(attrList));
|
|
attrList = nl->Rest(attrList);
|
|
}
|
|
|
|
// reset attrList
|
|
attrList = nl->Second(nl->Second(stream));
|
|
ListExpr typeList;
|
|
|
|
|
|
// check functions
|
|
set<string> usedNames;
|
|
|
|
while (!(nl->IsEmpty(functions)))
|
|
{
|
|
ListExpr function = nl->First(functions);
|
|
functions = nl->Rest(functions);
|
|
if(nl->ListLength(function)!=2){
|
|
ErrorReporter::ReportError("invalid extension found");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr name = nl->First(function);
|
|
ListExpr map = nl->Second(function);
|
|
|
|
string errormsg;
|
|
if(!listutils::isValidAttributeName(name, errormsg)){
|
|
return listutils::typeError(errormsg);
|
|
}
|
|
|
|
string namestr = nl->SymbolValue(name);
|
|
int pos = FindAttribute(attrList,namestr,typeList);
|
|
if(pos!=0){
|
|
ErrorReporter::ReportError("Attribute "+ namestr +
|
|
" already member of the tuple");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
|
|
if(nl->ListLength(map)!=3){
|
|
ErrorReporter::ReportError("invalid function");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if(!nl->IsEqual(nl->First(map),Symbol::MAP())){
|
|
ErrorReporter::ReportError("invalid function");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr funResType = nl->Third(map);
|
|
if(!am->CheckKind(Kind::DATA(),funResType, errorInfo)){
|
|
ErrorReporter::ReportError("requested attribute " + namestr +
|
|
"not in kind DATA");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
ListExpr funArgType = nl->Second(map);
|
|
|
|
if(!nl->Equal(funArgType, tuple)){
|
|
ErrorReporter::ReportError("function type different to the tuple type");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if(usedNames.find(namestr)!=usedNames.end()){
|
|
ErrorReporter::ReportError("Name "+ namestr + "occurs twice");
|
|
return nl->TypeError();
|
|
}
|
|
usedNames.insert(namestr);
|
|
// append attribute
|
|
lastlistn = nl->Append(lastlistn, (nl->TwoElemList(name, funResType )));
|
|
}
|
|
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()),newAttrList));
|
|
}
|
|
/*
|
|
2.18.2 Value mapping function of operator ~extend~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
// standard version
|
|
|
|
int Extend(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Word t, value;
|
|
Tuple* tup;
|
|
Supplier supplier, supplier2, supplier3;
|
|
int nooffun;
|
|
ArgVectorPointer funargs;
|
|
TupleType *resultTupleType = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
|
|
switch (message)
|
|
{
|
|
case INIT : {
|
|
assert(!resultTupleType);
|
|
ListExpr resultType = GetTupleResultType( s );
|
|
resultTupleType = new TupleType( nl->Second( resultType ) );
|
|
qp->GetLocal2(s).addr = resultTupleType();
|
|
return 0;
|
|
}
|
|
|
|
case FINISH : {
|
|
resultTupleType->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr = 0;
|
|
}
|
|
|
|
case OPEN :
|
|
qp->Open(args[0].addr);
|
|
return 0;
|
|
|
|
case REQUEST :
|
|
|
|
qp->Request(args[0].addr,t);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
tup = (Tuple*)t.addr;
|
|
Tuple *newTuple = new Tuple( resultTupleType );
|
|
for( int i = 0; i < tup->GetNoAttributes(); i++ ) {
|
|
//cout << (void*) tup << endl;
|
|
newTuple->CopyAttribute( i, tup, i );
|
|
}
|
|
supplier = args[1].addr;
|
|
nooffun = qp->GetNoSons(supplier);
|
|
for (int i=0; i < nooffun;i++)
|
|
{
|
|
supplier2 = qp->GetSupplier(supplier, i);
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
funargs = qp->Argument(supplier3);
|
|
((*funargs)[0]).setAddr(tup);
|
|
qp->Request(supplier3,value);
|
|
newTuple->PutAttribute( tup->GetNoAttributes()+i,
|
|
((Attribute*)value.addr)->Clone() );
|
|
}
|
|
tup->DeleteIfAllowed();
|
|
result.setAddr(newTuple);
|
|
return YIELD;
|
|
}
|
|
else
|
|
return CANCEL;
|
|
|
|
case CLOSE :
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
# else
|
|
|
|
// progress version
|
|
|
|
|
|
class ExtendLocalInfo: public ProgressLocalInfo
|
|
{
|
|
public:
|
|
|
|
ExtendLocalInfo():resultTupleType(0),stableValue(0),
|
|
sizesFinal(false), noOldAttrs(0),
|
|
noNewAttrs(0), attrSizeTmp(0),
|
|
attrSizeExtTmp(0) {};
|
|
|
|
~ExtendLocalInfo() {
|
|
if(resultTupleType){
|
|
resultTupleType->DeleteIfAllowed();
|
|
}
|
|
if(attrSizeTmp){
|
|
delete [] attrSizeTmp;
|
|
}
|
|
if(attrSizeExtTmp){
|
|
delete [] attrSizeExtTmp;
|
|
}
|
|
}
|
|
|
|
TupleType *resultTupleType;
|
|
int stableValue;
|
|
bool sizesFinal; //true after stableValue reached
|
|
int noOldAttrs, noNewAttrs;
|
|
double *attrSizeTmp;
|
|
double *attrSizeExtTmp;
|
|
};
|
|
|
|
|
|
struct extendLI2{
|
|
TupleType* tt;
|
|
Word t;
|
|
Word value;
|
|
};
|
|
|
|
|
|
int Extend(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Tuple* tup;
|
|
Supplier supplier, supplier2, supplier3;
|
|
int nooffun;
|
|
ArgVectorPointer funargs;
|
|
ExtendLocalInfo *eli;
|
|
|
|
eli = (ExtendLocalInfo*) local.addr;
|
|
extendLI2* li2 = (extendLI2*) qp->GetLocal2(s).addr;
|
|
|
|
|
|
switch (message)
|
|
{
|
|
case INIT : {
|
|
assert(!li2);
|
|
li2 = new extendLI2;
|
|
li2->tt = new TupleType(nl->Second(
|
|
GetTupleResultType(s))
|
|
);
|
|
qp->GetLocal2(s).addr = li2;
|
|
return 0;
|
|
}
|
|
|
|
case OPEN :
|
|
|
|
if ( eli ) {
|
|
delete eli;
|
|
}
|
|
|
|
eli = new ExtendLocalInfo;
|
|
assert(li2->tt);
|
|
li2->tt->IncReference();
|
|
eli->resultTupleType = li2->tt;
|
|
eli->read = 0;
|
|
eli->stableValue = 50;
|
|
eli->sizesFinal = false;
|
|
eli->noNewAttrs = qp->GetNoSons(args[1].addr);
|
|
eli->attrSizeTmp = new double[eli->noNewAttrs];
|
|
eli->attrSizeExtTmp = new double[eli->noNewAttrs];
|
|
for (int i = 0; i < eli->noNewAttrs; i++)
|
|
{
|
|
eli->attrSizeTmp[i] = 0.0;
|
|
eli->attrSizeExtTmp[i] = 0.0;
|
|
}
|
|
|
|
local.setAddr(eli);
|
|
|
|
qp->Open(args[0].addr);
|
|
|
|
return 0;
|
|
|
|
case REQUEST :
|
|
if(!eli){
|
|
return CANCEL;
|
|
}
|
|
qp->Request(args[0].addr,li2->t);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
tup = (Tuple*)li2->t.addr;
|
|
Tuple *newTuple = new Tuple( eli->resultTupleType );
|
|
eli->read++;
|
|
for( int i = 0; i < tup->GetNoAttributes(); i++ ) {
|
|
//cout << (void*) tup << endl;
|
|
newTuple->CopyAttribute( i, tup, i );
|
|
}
|
|
supplier = args[1].addr;
|
|
nooffun = qp->GetNoSons(supplier);
|
|
for (int i=0; i < nooffun;i++)
|
|
{
|
|
supplier2 = qp->GetSupplier(supplier, i);
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
funargs = qp->Argument(supplier3);
|
|
((*funargs)[0]).setAddr(tup);
|
|
qp->Request(supplier3,li2->value);
|
|
Attribute* newAttr = ((Attribute*)li2->value.addr)->Clone();
|
|
newTuple->PutAttribute( tup->GetNoAttributes()+i,newAttr);
|
|
|
|
if (eli->read <= eli->stableValue)
|
|
{
|
|
eli->attrSizeTmp[i] += newTuple->GetSize(tup->GetNoAttributes()+i);
|
|
eli->attrSizeExtTmp[i] +=
|
|
newTuple->GetExtSize(tup->GetNoAttributes()+i);
|
|
}
|
|
|
|
}
|
|
tup->DeleteIfAllowed();
|
|
result.setAddr(newTuple);
|
|
return YIELD;
|
|
}
|
|
else
|
|
return CANCEL;
|
|
|
|
case CLOSE :
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
|
|
|
|
case FINISH:{
|
|
if(li2){
|
|
li2->tt->DeleteIfAllowed();
|
|
delete li2;
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case CLOSEPROGRESS:
|
|
if ( eli ){
|
|
delete eli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
|
|
case REQUESTPROGRESS:
|
|
|
|
ProgressInfo p1;
|
|
ProgressInfo *pRes;
|
|
const double uExtend = 0.0034; //millisecs per tuple
|
|
const double vExtend = 0.0067; //millisecs per tuple and attribute
|
|
|
|
// see determination of constants in file ConstantsExtendStream
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
|
|
if ( !eli ) {
|
|
return CANCEL;
|
|
}
|
|
|
|
if ( qp->RequestProgress(args[0].addr, &p1) )
|
|
{
|
|
eli->sizesChanged = false;
|
|
|
|
if ( !eli->sizesInitialized )
|
|
{
|
|
eli->noOldAttrs = p1.noAttrs;
|
|
eli->noAttrs = eli->noOldAttrs + eli->noNewAttrs;
|
|
eli->attrSize = new double[eli->noAttrs];
|
|
eli->attrSizeExt = new double[eli->noAttrs];
|
|
}
|
|
|
|
if ( !eli->sizesInitialized || p1.sizesChanged ||
|
|
( eli->read >= eli->stableValue && !eli->sizesFinal ) )
|
|
{
|
|
eli->Size = 0.0;
|
|
eli->SizeExt = 0.0;
|
|
|
|
//old attrs
|
|
for (int i = 0; i < eli->noOldAttrs; i++)
|
|
{
|
|
eli->attrSize[i] = p1.attrSize[i];
|
|
eli->attrSizeExt[i] = p1.attrSizeExt[i];
|
|
eli->Size += eli->attrSize[i];
|
|
eli->SizeExt += eli->attrSizeExt[i];
|
|
}
|
|
|
|
//new attrs
|
|
if ( eli->read < eli->stableValue )
|
|
{
|
|
for (int j = 0; j < eli->noNewAttrs; j++)
|
|
{
|
|
eli->attrSize[j + eli->noOldAttrs] = 12;
|
|
eli->attrSizeExt[j + eli->noOldAttrs] = 12;
|
|
eli->Size += eli->attrSize[j + eli->noOldAttrs];
|
|
eli->SizeExt += eli->attrSizeExt[j + eli->noOldAttrs];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int j = 0; j < eli->noNewAttrs; j++)
|
|
{
|
|
eli->attrSize[j + eli->noOldAttrs] = eli->attrSizeTmp[j] /
|
|
eli->stableValue;
|
|
eli->attrSizeExt[j + eli->noOldAttrs] = eli->attrSizeExtTmp[j] /
|
|
eli->stableValue;
|
|
eli->Size += eli->attrSize[j + eli->noOldAttrs];
|
|
eli->SizeExt += eli->attrSizeExt[j + eli->noOldAttrs];
|
|
}
|
|
}
|
|
eli->sizesInitialized = true;
|
|
eli->sizesChanged = true;
|
|
}
|
|
//ensure sizes are updated only once for passing the threshold
|
|
if ( eli->read >= eli->stableValue ) eli->sizesFinal = true;
|
|
|
|
|
|
pRes->Card = p1.Card;
|
|
|
|
pRes->CopySizes(eli);
|
|
|
|
pRes->Time = p1.Time + p1.Card * (uExtend + eli->noNewAttrs * vExtend);
|
|
|
|
|
|
if ( p1.BTime < 0.1 && pipelinedProgress ) //non-blocking,
|
|
//use pipelining
|
|
pRes->Progress = p1.Progress;
|
|
else
|
|
pRes->Progress =
|
|
(p1.Progress * p1.Time +
|
|
eli->read * (uExtend + eli->noNewAttrs * vExtend))
|
|
/ pRes->Time;
|
|
|
|
pRes->CopyBlocking(p1); //non-blocking operator
|
|
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
2.18.3 Specification of operator ~extend~
|
|
|
|
*/
|
|
const string ExtendSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>(stream(tuple(x)) x [(a1, (tuple(x)"
|
|
" -> d1)) ... (an, (tuple(x) -> dn))] -> "
|
|
"stream(tuple(x@[a1:d1, ... , an:dn])))"
|
|
"</text--->"
|
|
"<text>_ extend [funlist]</text--->"
|
|
"<text>Extends each input tuple by new "
|
|
"attributes as specified in the parameter"
|
|
" list.</text--->"
|
|
"<text>query ten feed extend [Mult5 : "
|
|
".nr * 5, Mod2 : .nr mod 2] consume"
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.18.4 Definition of operator ~extend~
|
|
|
|
*/
|
|
Operator extrelextend (
|
|
"extend", // name
|
|
ExtendSpec, // specification
|
|
Extend, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
ExtendTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.19 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 {s1} loopjoin[ fun(t1: TUPLE) plz feed
|
|
filter [ attr(t1, SName_s1) = .Ort]] count;
|
|
|
|
(query (count (loopjoin (rename (feed Staedte) s1)
|
|
(fun (t1 TUPLE) (filter (feed plz)
|
|
(fun (tuple1 TUPLE) (= (attr t1 SName_s1) (attr tuple1 Ort))))))))
|
|
----
|
|
|
|
The renaming is necessary whenever the underlying relations have at
|
|
least one common attribute name in order to assure that the output
|
|
tuple stream consists of different named attributes.
|
|
|
|
The type mapping function of the loopjoin operation is as follows:
|
|
|
|
---- ((stream (tuple x)) (map (tuple x) (stream (tuple y))))
|
|
-> (stream (tuple x * y))
|
|
where x = ((x1 t1) ... (xn tn)) and y = ((y1 d1) ... (ym dm))
|
|
----
|
|
|
|
*/
|
|
ListExpr LoopjoinTypeMap(ListExpr args)
|
|
{
|
|
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr map = nl->Second(args);
|
|
string err = "stream(tuple (a)) x ( tuple(a) -> stream(tuple(b))) expected";
|
|
if(!listutils::isTupleStream(stream) ||
|
|
!listutils::isMap<1>(map)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr maparg = nl->Second(map);
|
|
ListExpr mapres = nl->Third(map);
|
|
|
|
if(!nl->Equal(nl->Second(stream), maparg)){
|
|
return listutils::typeError( err +
|
|
" (function argument differs from tuple)");
|
|
}
|
|
|
|
if(!listutils::isTupleStream(mapres)){
|
|
return listutils::typeError(err +
|
|
" (function result is not a tuple stream");
|
|
}
|
|
|
|
ListExpr alist1 = nl->Second(nl->Second(stream));
|
|
ListExpr alist2 = nl->Second(nl->Second(mapres));
|
|
|
|
if(!listutils::disjointAttrNames(alist1, alist2)){
|
|
return listutils::typeError(err +
|
|
" ( name conflict in tuples");
|
|
}
|
|
ListExpr list = ConcatLists(alist1,alist2);
|
|
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()),
|
|
list));
|
|
}
|
|
|
|
/*
|
|
2.19.2 Value mapping function of operator ~loopjoin~
|
|
|
|
SPM: There is a problem when this operator is requested after
|
|
it has returned CANCEL it will chrash since it tryes to do a
|
|
request on a NULL pointer.
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
// standard version
|
|
|
|
struct LoopjoinLocalInfo
|
|
{
|
|
Word tuplex;
|
|
Word streamy;
|
|
TupleType *resultTupleType;
|
|
};
|
|
|
|
|
|
|
|
int Loopjoin(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funargs = 0;
|
|
|
|
Word tuplex(Address(0));
|
|
Word tupley(Address(0));
|
|
Word tuplexy(Address(0));
|
|
Word streamy(Address(0));
|
|
|
|
Tuple* ctuplex = 0;
|
|
Tuple* ctupley = 0;
|
|
Tuple* ctuplexy = 0;
|
|
|
|
LoopjoinLocalInfo *localinfo = 0;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch ( message )
|
|
{
|
|
case INIT: {
|
|
assert(!tt);
|
|
ListExpr resultType = GetTupleResultType( s );
|
|
qp->GetLocal2(s).addr = new TupleType(nl->Second(resultType));
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN:
|
|
qp->Open (args[0].addr);
|
|
qp->Request(args[0].addr, tuplex);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
funargs = qp->Argument(args[1].addr);
|
|
(*funargs)[0] = tuplex;
|
|
streamy=args[1];
|
|
qp->Open (streamy.addr);
|
|
|
|
localinfo = new LoopjoinLocalInfo;
|
|
tt->IncReference();
|
|
localinfo->resultTupleType = tt;
|
|
localinfo->tuplex = tuplex;
|
|
localinfo->streamy = streamy;
|
|
local.setAddr(localinfo);
|
|
}
|
|
else
|
|
{
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
if (local.addr ==0) return CANCEL;
|
|
localinfo=(LoopjoinLocalInfo *) local.addr;
|
|
tuplex=localinfo->tuplex;
|
|
ctuplex=(Tuple*)tuplex.addr;
|
|
streamy=localinfo->streamy;
|
|
tupley.setAddr(0);
|
|
while (tupley.addr==0)
|
|
{
|
|
qp->Request(streamy.addr, tupley);
|
|
if (!(qp->Received(streamy.addr)))
|
|
{
|
|
qp->Close(streamy.addr);
|
|
((Tuple*)tuplex.addr)->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, tuplex);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
funargs = qp->Argument(args[1].addr);
|
|
ctuplex=(Tuple*)tuplex.addr;
|
|
(*funargs)[0] = tuplex;
|
|
streamy=args[1];
|
|
qp->Open (streamy.addr);
|
|
tupley.setAddr(0);
|
|
|
|
localinfo->tuplex=tuplex;
|
|
localinfo->streamy=streamy;
|
|
local.setAddr(localinfo);
|
|
}
|
|
else
|
|
{
|
|
localinfo->streamy.setAddr(0);
|
|
localinfo->tuplex.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ctupley=(Tuple*)tupley.addr;
|
|
}
|
|
}
|
|
ctuplexy = new Tuple( localinfo->resultTupleType );
|
|
tuplexy.setAddr(ctuplexy);
|
|
Concat(ctuplex, ctupley, ctuplexy);
|
|
ctupley->DeleteIfAllowed();
|
|
result = tuplexy;
|
|
return YIELD;
|
|
|
|
case CLOSE:
|
|
if( local.addr != 0 )
|
|
{
|
|
localinfo=(LoopjoinLocalInfo *) local.addr;
|
|
if( localinfo->streamy.addr != 0 )
|
|
qp->Close( localinfo->streamy.addr );
|
|
|
|
if( localinfo->tuplex.addr != 0 )
|
|
((Tuple*)localinfo->tuplex.addr)->DeleteIfAllowed();
|
|
|
|
if( localinfo->resultTupleType != 0 )
|
|
localinfo->resultTupleType->DeleteIfAllowed();
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
// with support for progress queries
|
|
|
|
|
|
|
|
class LoopjoinLocalInfo: public ProgressLocalInfo
|
|
{
|
|
public:
|
|
LoopjoinLocalInfo():tuplex(Address(0)),streamy(Address(0)),
|
|
resultTupleType(0), costEstimation(0)
|
|
{}
|
|
|
|
~LoopjoinLocalInfo() {
|
|
if(resultTupleType){
|
|
resultTupleType->DeleteIfAllowed();
|
|
resultTupleType=0;
|
|
}
|
|
}
|
|
|
|
Word tuplex;
|
|
Word streamy;
|
|
TupleType *resultTupleType;
|
|
LoopJoinCostEstimation<true> *costEstimation;
|
|
};
|
|
|
|
CostEstimation* LoopJoinCostEstimationFunc() {
|
|
return new LoopJoinCostEstimation<true>();
|
|
}
|
|
|
|
|
|
|
|
struct loopJoinLI2{
|
|
TupleType* tt;
|
|
Word tuplex;
|
|
Word tupley;
|
|
Word tuplexy;
|
|
Word streamy;
|
|
};
|
|
|
|
|
|
int Loopjoin(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funargs = 0;
|
|
Tuple* ctuplex = 0;
|
|
Tuple* ctupley = 0;
|
|
Tuple* ctuplexy = 0;
|
|
|
|
|
|
LoopjoinLocalInfo* lli;
|
|
|
|
lli = (LoopjoinLocalInfo *) local.addr;
|
|
|
|
loopJoinLI2* li2 = (loopJoinLI2*) qp->GetLocal2(s).addr;
|
|
|
|
|
|
switch ( message )
|
|
{
|
|
case INIT : {
|
|
ListExpr resultType = GetTupleResultType(s);
|
|
li2 = new loopJoinLI2();
|
|
qp->GetLocal2(s).addr = li2;
|
|
li2->tt = new TupleType(nl->Second(resultType));
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(li2){
|
|
li2->tt->DeleteIfAllowed();
|
|
delete li2;
|
|
}
|
|
qp->GetLocal2(s).addr = 0;
|
|
return 0;
|
|
}
|
|
|
|
case OPEN:
|
|
|
|
if ( lli ) {
|
|
delete lli;
|
|
}
|
|
|
|
lli = new LoopjoinLocalInfo();
|
|
li2->tt->IncReference();
|
|
lli->resultTupleType = li2->tt;
|
|
|
|
local.setAddr(lli);
|
|
|
|
// cost estimation
|
|
lli->costEstimation =
|
|
(LoopJoinCostEstimation<true>*) qp->getCostEstimation(s);
|
|
lli->costEstimation->init(NULL, NULL);
|
|
|
|
qp->Open (args[0].addr);
|
|
qp->Request(args[0].addr, li2->tuplex);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
lli->readFirst++;
|
|
lli->costEstimation->processedTupleInStream1();
|
|
|
|
funargs = qp->Argument(args[1].addr);
|
|
(*funargs)[0] = li2->tuplex;
|
|
li2->streamy=args[1];
|
|
qp->Open (li2->streamy.addr);
|
|
|
|
lli->tuplex = li2->tuplex;
|
|
lli->streamy = li2->streamy;
|
|
|
|
}
|
|
else
|
|
{
|
|
lli->tuplex.setAddr(0);
|
|
lli->streamy.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
if(!lli){
|
|
return CANCEL;
|
|
}
|
|
if ( lli->tuplex.addr == 0) {
|
|
return CANCEL;
|
|
}
|
|
|
|
li2->tuplex=lli->tuplex;
|
|
ctuplex=(Tuple*)li2->tuplex.addr;
|
|
li2->streamy=lli->streamy;
|
|
li2->tupley.setAddr(0);
|
|
while (li2->tupley.addr==0)
|
|
{
|
|
qp->Request(li2->streamy.addr, li2->tupley);
|
|
if (!(qp->Received(li2->streamy.addr)))
|
|
{
|
|
qp->Close(li2->streamy.addr);
|
|
((Tuple*)li2->tuplex.addr)->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, li2->tuplex);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
lli->readFirst++;
|
|
lli->costEstimation->processedTupleInStream1();
|
|
funargs = qp->Argument(args[1].addr);
|
|
ctuplex=(Tuple*)li2->tuplex.addr;
|
|
(*funargs)[0] = li2->tuplex;
|
|
li2->streamy=args[1];
|
|
qp->Open (li2->streamy.addr);
|
|
li2->tupley.setAddr(0);
|
|
|
|
lli->tuplex=li2->tuplex;
|
|
lli->streamy=li2->streamy;
|
|
}
|
|
else
|
|
{
|
|
lli->costEstimation->setStream1Exhausted(true);
|
|
lli->streamy.setAddr(0);
|
|
lli->tuplex.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ctupley=(Tuple*)li2->tupley.addr;
|
|
}
|
|
}
|
|
ctuplexy = new Tuple( lli->resultTupleType );
|
|
li2->tuplexy.setAddr(ctuplexy);
|
|
Concat(ctuplex, ctupley, ctuplexy);
|
|
ctupley->DeleteIfAllowed();
|
|
|
|
lli->returned++;
|
|
result = li2->tuplexy;
|
|
return YIELD;
|
|
|
|
case CLOSE:
|
|
qp->Close(args[0].addr);
|
|
|
|
if ( lli )
|
|
{
|
|
if ( lli->tuplex.addr != 0 )
|
|
{
|
|
if( lli->streamy.addr != 0 )
|
|
qp->Close( lli->streamy.addr );
|
|
|
|
if( lli->tuplex.addr != 0 )
|
|
((Tuple*)lli->tuplex.addr)->DeleteIfAllowed();
|
|
|
|
}
|
|
delete lli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
2.19.3 Specification of operator ~loopjoin~
|
|
|
|
*/
|
|
const string LoopjoinSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream tuple1) (fun (tuple1 -> "
|
|
"stream(tuple2)))) -> (stream tuple1*tuple2)"
|
|
"</text--->"
|
|
"<text>_ loopjoin [ fun ]</text--->"
|
|
"<text>Creates the product of all tuples from the first argument "
|
|
"with all tuples from the stream created by the second (function) argument. "
|
|
"Note: The input tuples must "
|
|
"have different attribute names, hence renaming may be applied "
|
|
"to one of the input streams.</text--->"
|
|
"<text>query Staedte feed {s1} loopjoin[ fun(t1: TUPLE) plz feed"
|
|
" filter [ attr(t1, SName_s1) = .Ort]] count</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.19.4 Definition of operator ~loopjoin~
|
|
|
|
*/
|
|
#ifndef USE_PROGRESS
|
|
Operator extrelloopjoin (
|
|
"loopjoin", // name
|
|
LoopjoinSpec, // specification
|
|
Loopjoin, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
LoopjoinTypeMap // type mapping
|
|
);
|
|
#else
|
|
Operator extrelloopjoin (
|
|
"loopjoin", // name
|
|
LoopjoinSpec, // specification
|
|
Loopjoin, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
LoopjoinTypeMap, // type mapping
|
|
LoopJoinCostEstimationFunc // costestimation
|
|
);
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
2.20 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 loopsel [ fun(t1: TUPLE) plz feed
|
|
filter [ attr(t1, SName) = .Ort ]] count;
|
|
|
|
(query (count (loopsel (feed Staedte)
|
|
(fun (t1 TUPLE) (filter (feed plz)
|
|
(fun (tuple1 TUPLE) (= (attr t1 SName) (attr tuple1 Ort))))))))
|
|
|
|
----
|
|
|
|
7.4.1 Type mapping function of operator ~loopsel~
|
|
|
|
The type mapping function of the loopsel operation is as follows:
|
|
|
|
---- ((stream (tuple x)) (map (tuple x) (stream (tuple y))))
|
|
-> (stream (tuple y))
|
|
where x = ((x1 t1) ... (xn tn)) and y = ((y1 d1) ... (ym dm))
|
|
----
|
|
|
|
*/
|
|
ListExpr
|
|
LoopselectTypeMap(ListExpr args)
|
|
{
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr map = nl->Second(args);
|
|
string err = "stream(tuple (a)) x ( tuple(a) -> stream(tuple(b))) expected";
|
|
if(!listutils::isTupleStream(stream) ||
|
|
!listutils::isMap<1>(map)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr maparg = nl->Second(map);
|
|
ListExpr mapres = nl->Third(map);
|
|
|
|
if(!nl->Equal(nl->Second(stream), maparg)){
|
|
return listutils::typeError( err +
|
|
" (function argument differs from tuple)");
|
|
}
|
|
|
|
if(!listutils::isTupleStream(mapres)){
|
|
return listutils::typeError(err +
|
|
" (function result is not a tuple stream");
|
|
}
|
|
|
|
return mapres;
|
|
}
|
|
|
|
/*
|
|
|
|
4.1.2 Value mapping function of operator ~loopsel~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
struct LoopselectLocalInfo
|
|
{
|
|
Word tuplex;
|
|
Word streamy;
|
|
TupleType *resultTupleType;
|
|
};
|
|
|
|
int
|
|
Loopselect(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funargs;
|
|
Word tuplex, tupley, streamy;
|
|
Tuple* ctuplex;
|
|
Tuple* ctupley;
|
|
LoopselectLocalInfo *localinfo;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch ( message )
|
|
{
|
|
case INIT : {
|
|
ListExpr resultType = GetTupleResultType( s );
|
|
tt = new TupleType (nl->Second(resultType));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH : {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN:
|
|
// open the stream and initiate the variables
|
|
qp->Open (args[0].addr);
|
|
qp->Request(args[0].addr, tuplex);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
// compute the rely which corresponding to tuplex
|
|
funargs = qp->Argument(args[1].addr);
|
|
(*funargs)[0] = tuplex;
|
|
streamy = args[1];
|
|
qp->Open(streamy.addr);
|
|
|
|
// put the information of tuplex and rely into local
|
|
localinfo = new LoopselectLocalInfo;
|
|
tt->IncReference();
|
|
localinfo->resultTupleType = tt;
|
|
localinfo->tuplex=tuplex;
|
|
localinfo->streamy=streamy;
|
|
local.setAddr(localinfo);
|
|
}
|
|
else
|
|
{
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
if (local.addr ==0) return CANCEL;
|
|
|
|
// restore localinformation from the local variable.
|
|
localinfo = (LoopselectLocalInfo *) local.addr;
|
|
tuplex = localinfo->tuplex;
|
|
ctuplex = (Tuple*)tuplex.addr;
|
|
streamy = localinfo->streamy;
|
|
// prepare tuplex and tupley for processing.
|
|
// if rely is exausted: fetch next tuplex.
|
|
tupley.setAddr(0);
|
|
while (tupley.addr==0)
|
|
{
|
|
qp->Request(streamy.addr, tupley);
|
|
if (!(qp->Received(streamy.addr)))
|
|
{
|
|
qp->Close(streamy.addr);
|
|
((Tuple*)tuplex.addr)->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, tuplex);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
funargs = qp->Argument(args[1].addr);
|
|
ctuplex = (Tuple*)tuplex.addr;
|
|
(*funargs)[0] = tuplex;
|
|
streamy = args[1];
|
|
qp->Open(streamy.addr);
|
|
tupley.setAddr(0);
|
|
|
|
localinfo->tuplex=tuplex;
|
|
localinfo->streamy=streamy;
|
|
local.setAddr(localinfo);
|
|
}
|
|
else
|
|
{
|
|
localinfo->streamy.setAddr(0);
|
|
localinfo->tuplex.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ctupley = (Tuple*)tupley.addr;
|
|
}
|
|
}
|
|
|
|
result = tupley;
|
|
return YIELD;
|
|
|
|
case CLOSE:
|
|
if( local.addr != 0 )
|
|
{
|
|
localinfo=(LoopselectLocalInfo *) local.addr;
|
|
|
|
if( localinfo->streamy.addr != 0 )
|
|
qp->Close( localinfo->streamy.addr );
|
|
|
|
if( localinfo->tuplex.addr != 0 )
|
|
((Tuple*)localinfo->tuplex.addr)->DeleteIfAllowed();
|
|
|
|
if( localinfo->resultTupleType != 0 )
|
|
localinfo->resultTupleType->DeleteIfAllowed();
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
|
|
struct LoopselectLocalInfo: public ProgressLocalInfo
|
|
{
|
|
LoopselectLocalInfo(): ::ProgressLocalInfo(),tuplex(Address(0)),
|
|
streamy(Address(0)), resultTupleType(0),
|
|
costEstimation(0) {}
|
|
|
|
~LoopselectLocalInfo(){
|
|
if(resultTupleType){
|
|
resultTupleType->DeleteIfAllowed();
|
|
resultTupleType = 0;
|
|
}
|
|
}
|
|
|
|
Word tuplex;
|
|
Word streamy;
|
|
TupleType *resultTupleType;
|
|
LoopJoinCostEstimation<false> *costEstimation;
|
|
};
|
|
|
|
CostEstimation* LoopSelCostEstimationFunc() {
|
|
return new LoopJoinCostEstimation<false>();
|
|
}
|
|
|
|
|
|
|
|
int Loopselect(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funargs;
|
|
Word tuplex, tupley, streamy;
|
|
LoopselectLocalInfo *localinfo;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch ( message )
|
|
{
|
|
case INIT : {
|
|
ListExpr resultType = GetTupleResultType( s );
|
|
tt = new TupleType(nl->Second(resultType));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH : {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN:
|
|
// open the stream and initiate the variables
|
|
qp->Open (args[0].addr);
|
|
qp->Request(args[0].addr, tuplex);
|
|
localinfo = static_cast<LoopselectLocalInfo*>(local.addr);
|
|
if(localinfo){
|
|
delete localinfo;
|
|
localinfo=0;
|
|
}
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
// compute the rely which corresponding to tuplex
|
|
funargs = qp->Argument(args[1].addr);
|
|
(*funargs)[0] = tuplex;
|
|
streamy = args[1];
|
|
qp->Open(streamy.addr);
|
|
|
|
// put the information of tuplex and rely into local
|
|
localinfo = new LoopselectLocalInfo;
|
|
tt->IncReference();
|
|
localinfo->resultTupleType = tt;
|
|
localinfo->tuplex=tuplex;
|
|
localinfo->streamy=streamy;
|
|
localinfo->readFirst=1; // first tuple read
|
|
localinfo->returned=0;
|
|
local.setAddr(localinfo);
|
|
|
|
// CostEstimation
|
|
localinfo -> costEstimation =
|
|
(LoopJoinCostEstimation<false>*) qp->getCostEstimation(s);
|
|
localinfo->costEstimation->init(NULL, NULL);
|
|
localinfo->costEstimation->processedTupleInStream1();
|
|
}
|
|
else
|
|
{
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
if (local.addr ==0){
|
|
return CANCEL;
|
|
}
|
|
|
|
// restore localinformation from the local variable.
|
|
localinfo = (LoopselectLocalInfo *) local.addr;
|
|
tuplex = localinfo->tuplex;
|
|
// ctuplex = (Tuple*)tuplex.addr;
|
|
streamy = localinfo->streamy;
|
|
// prepare tuplex and tupley for processing.
|
|
// if rely is exausted: fetch next tuplex.
|
|
tupley.setAddr(0);
|
|
while (tupley.addr==0)
|
|
{
|
|
qp->Request(streamy.addr, tupley);
|
|
if (!(qp->Received(streamy.addr)))
|
|
{
|
|
qp->Close(streamy.addr);
|
|
((Tuple*)tuplex.addr)->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, tuplex);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
localinfo->readFirst++; // another tuple read
|
|
localinfo->costEstimation->processedTupleInStream1();
|
|
funargs = qp->Argument(args[1].addr);
|
|
//ctuplex = (Tuple*)tuplex.addr;
|
|
(*funargs)[0] = tuplex;
|
|
streamy = args[1];
|
|
qp->Open(streamy.addr);
|
|
tupley.setAddr(0);
|
|
|
|
localinfo->tuplex=tuplex;
|
|
localinfo->streamy=streamy;
|
|
local.setAddr(localinfo);
|
|
}
|
|
else
|
|
{
|
|
localinfo->streamy.setAddr(0);
|
|
localinfo->tuplex.setAddr(0);
|
|
localinfo->costEstimation->setStream1Exhausted(true);
|
|
return CANCEL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//ctupley = (Tuple*)tupley.addr;
|
|
}
|
|
}
|
|
localinfo->returned++;
|
|
result = tupley;
|
|
return YIELD;
|
|
|
|
case CLOSE:
|
|
if( local.addr != 0 )
|
|
{
|
|
localinfo=(LoopselectLocalInfo *) local.addr;
|
|
|
|
if( localinfo->streamy.addr != 0 )
|
|
qp->Close( localinfo->streamy.addr );
|
|
|
|
if( localinfo->tuplex.addr != 0 )
|
|
((Tuple*)localinfo->tuplex.addr)->DeleteIfAllowed();
|
|
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
#endif
|
|
/*
|
|
|
|
4.1.3 Specification of operator ~loopsel~
|
|
|
|
*/
|
|
const string LoopselectSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream tuple1) (map tuple1 "
|
|
"rel(tuple2))) -> (stream tuple2)"
|
|
"</text--->"
|
|
"<text>_ loopselect [ fun ]</text--->"
|
|
"<text>Only tuples in the cartesian product "
|
|
"which satisfy certain conditions are passed on"
|
|
" to the output stream.</text--->"
|
|
"<text>query Staedte feed loopsel [ fun(t1: TUPLE) plz feed "
|
|
"filter [ attr(t1, SName) = .Ort ]] count</text--->"
|
|
") )";
|
|
|
|
/*
|
|
|
|
4.1.3 Definition of operator ~loopsel~
|
|
|
|
*/
|
|
#ifndef USE_PROGRESS
|
|
Operator extrelloopsel (
|
|
"loopsel", // name
|
|
LoopselectSpec, // specification
|
|
Loopselect, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
LoopselectTypeMap // type mapping
|
|
);
|
|
#else
|
|
Operator extrelloopsel (
|
|
"loopsel", // name
|
|
LoopselectSpec, // specification
|
|
Loopselect, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
LoopselectTypeMap, // type mapping
|
|
LoopSelCostEstimationFunc // CostEstimation
|
|
);
|
|
#endif
|
|
|
|
/*
|
|
2.21 Operator ~extendstream~
|
|
|
|
This operator is implemented by the idea of LOOPJOIN and EXTEND
|
|
|
|
The type mapping function of the extendstream operation is as follows:
|
|
|
|
---- ((stream (tuple x)) (map (tuple x) (stream (y))))
|
|
-> (stream (tuple x * y))
|
|
where x = ((x1 t1) ... (xn tn)) and
|
|
y is a simple data type such as string, integer, or real ...
|
|
----
|
|
|
|
---- ((stream (tuple x)) ( (b1 (map (tuple x) (stream (y)))))
|
|
-> (stream (tuple x * y))
|
|
where x = ((x1 t1) ... (xn tn)) and
|
|
y is a simple data type such as string, integer, or real ...
|
|
----
|
|
|
|
For instance,
|
|
|
|
---- query people feed extendstream [parts : components (.name) ] consume;
|
|
----
|
|
|
|
*/
|
|
|
|
|
|
|
|
ListExpr ExtendStreamTypeMap(ListExpr args)
|
|
{
|
|
NList Args(args);
|
|
if(!Args.hasLength(2)){
|
|
return NList::typeError("expecting two arguments");
|
|
}
|
|
NList Stream = Args.first();
|
|
|
|
|
|
// check the tuple stream
|
|
if(!Stream.hasLength(2) ||
|
|
!Stream.first().isSymbol(Symbol::STREAM()) ||
|
|
!Stream.second().hasLength(2) ||
|
|
!Stream.second().first().isSymbol(Tuple::BasicType()) ||
|
|
!IsTupleDescription(Stream.second().second().listExpr())
|
|
){
|
|
return NList::typeError("first argument is not a tuple stream");
|
|
}
|
|
|
|
// second argument must be a list of length one
|
|
NList Second = Args.second();
|
|
if(!Second.isNoAtom() ||
|
|
!Second.hasLength(1)){
|
|
return NList::typeError("error in second argument");
|
|
}
|
|
|
|
// check for ( <attrname> <map> )
|
|
NList NameMap = Second.first();
|
|
if(!NameMap.hasLength(2)){
|
|
return NList::typeError("expecting (attrname map) as second argument");
|
|
}
|
|
|
|
// attrname must be an identifier
|
|
if(!NameMap.first().isSymbol()){
|
|
return NList::typeError("invalid representation of <attrname>");
|
|
}
|
|
|
|
// check map
|
|
NList Map = NameMap.second();
|
|
|
|
// Map must have format (map <tuple> <stream>)
|
|
if(!Map.hasLength(3) ||
|
|
!Map.first().isSymbol(Symbol::MAP()) ||
|
|
!Map.second().hasLength(2) ||
|
|
!Map.second().first().isSymbol(Tuple::BasicType()) ||
|
|
!Map.third().hasLength(2) ||
|
|
!Map.third().first().isSymbol(Symbol::STREAM())){
|
|
return NList::typeError("expecting (map ( (tuple(...) (stream DATA))))"
|
|
" as second argument");
|
|
}
|
|
|
|
// the argumenttype for map must be equal to the tuple type from the stream
|
|
if(Stream.second() != Map.second()){
|
|
return NList::typeError("different tuple descriptions detected");
|
|
}
|
|
|
|
NList typelist = Map.third().second();
|
|
|
|
ListExpr errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR"));
|
|
// check for Kind Data
|
|
if(!am->CheckKind(Kind::DATA(),typelist.listExpr(),errorInfo)){
|
|
return NList::typeError("stream elements not in kind DATA");
|
|
}
|
|
|
|
// check if argname is already used in the original tuple
|
|
ListExpr attrNameList = NameMap.first().listExpr();
|
|
string errormsg;
|
|
if(!listutils::isValidAttributeName(attrNameList,errormsg)){
|
|
return listutils::typeError(errormsg);
|
|
}
|
|
string attrname = NameMap.first().str();
|
|
ListExpr attrtype=nl->TheEmptyList();
|
|
int index = FindAttribute(Stream.second().second().listExpr(),
|
|
attrname,attrtype);
|
|
if(index){ // attrname already used
|
|
return NList::typeError("attrname already used in the original stream");
|
|
}
|
|
|
|
ListExpr attrlist = ConcatLists(Stream.second().second().listExpr(),
|
|
nl->OneElemList(
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom(attrname),
|
|
Map.third().second().listExpr())));
|
|
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList( nl->SymbolAtom(Tuple::BasicType()),attrlist));
|
|
}
|
|
|
|
/*
|
|
2.19.2 Value mapping function of operator ~extendstream~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
// standard version
|
|
|
|
struct ExtendStreamLocalInfo
|
|
{
|
|
Tuple *tupleX;
|
|
Word streamY;
|
|
TupleType *resultTupleType;
|
|
};
|
|
|
|
int ExtendStream(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funargs;
|
|
Word wTupleX, wValueY;
|
|
Tuple* tupleXY;
|
|
ExtendStreamLocalInfo *localinfo;
|
|
|
|
TupleType *resultTupleType;
|
|
|
|
Supplier supplier, supplier2, supplier3;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch( message )
|
|
{
|
|
case INIT: {
|
|
ListExpr resultType = GetTupleResultType( s );
|
|
tt = new TupleType(nl->Second(resulType);
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN:
|
|
{
|
|
//1. open the input stream and initiate the arguments
|
|
qp->Open( args[0].addr );
|
|
qp->Request( args[0].addr, wTupleX );
|
|
if( qp->Received( args[0].addr ) )
|
|
{
|
|
//2. compute the result "stream y" from tuple x
|
|
//funargs = qp->Argument(args[1].addr);
|
|
//here should be changed to the following...
|
|
supplier = args[1].addr;
|
|
supplier2 = qp->GetSupplier( supplier, 0 );
|
|
supplier3 = qp->GetSupplier( supplier2, 1 );
|
|
funargs = qp->Argument( supplier3 );
|
|
|
|
(*funargs)[0] = wTupleX;
|
|
qp->Open( supplier3 );
|
|
|
|
//3. save the local information
|
|
localinfo = new ExtendStreamLocalInfo;
|
|
tt->IncReference();
|
|
localinfo->resultTupleType = tt;
|
|
localinfo->tupleX = (Tuple*)wTupleX.addr;
|
|
localinfo->streamY.setAddr( supplier3 );
|
|
local.setAddr(localinfo);
|
|
}
|
|
else
|
|
{
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
{
|
|
if( local.addr == 0 )
|
|
return CANCEL;
|
|
|
|
//1. recover local information
|
|
localinfo=(ExtendStreamLocalInfo *) local.addr;
|
|
resultTupleType = (TupleType *)localinfo->resultTupleType;
|
|
|
|
//2. prepare tupleX and wValueY.
|
|
//If wValueY is empty, then get next tupleX
|
|
wValueY.setAddr(0);
|
|
while( wValueY.addr == 0 )
|
|
{
|
|
qp->Request( localinfo->streamY.addr, wValueY );
|
|
if( !(qp->Received( localinfo->streamY.addr )) )
|
|
{
|
|
qp->Close( localinfo->streamY.addr );
|
|
localinfo->tupleX->DeleteIfAllowed();
|
|
qp->Request( args[0].addr, wTupleX );
|
|
if( qp->Received(args[0].addr) )
|
|
{
|
|
supplier = args[1].addr;
|
|
supplier2 = qp->GetSupplier(supplier, 0);
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
funargs = qp->Argument(supplier3);
|
|
|
|
localinfo->tupleX = (Tuple*)wTupleX.addr;
|
|
(*funargs)[0] = wTupleX;
|
|
qp->Open( supplier3 );
|
|
|
|
localinfo->tupleX = (Tuple*)wTupleX.addr;
|
|
localinfo->streamY.setAddr(supplier3);
|
|
|
|
wValueY.setAddr(0);
|
|
}
|
|
else //streamx is exausted
|
|
{
|
|
localinfo->streamY.setAddr(0);
|
|
localinfo->tupleX = 0;
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//3. compute tupleXY from tupleX and wValueY
|
|
tupleXY = new Tuple( localinfo->resultTupleType );
|
|
|
|
for( int i = 0; i < localinfo->tupleX->GetNoAttributes(); i++ )
|
|
tupleXY->CopyAttribute( i, localinfo->tupleX, i );
|
|
|
|
tupleXY->PutAttribute( localinfo->tupleX->GetNoAttributes(),
|
|
(Attribute*)wValueY.addr );
|
|
|
|
// setting the result
|
|
result.setAddr( tupleXY );
|
|
return YIELD;
|
|
}
|
|
case CLOSE:
|
|
{
|
|
if( local.addr != 0 )
|
|
{
|
|
localinfo = (ExtendStreamLocalInfo *)local.addr;
|
|
|
|
if( localinfo->streamY.addr != 0 )
|
|
qp->Close( localinfo->streamY.addr );
|
|
|
|
if( localinfo->tupleX != 0 )
|
|
localinfo->tupleX->DeleteIfAllowed();
|
|
|
|
if( localinfo->resultTupleType != 0 )
|
|
localinfo->resultTupleType->DeleteIfAllowed();
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close( args[0].addr );
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
# else
|
|
|
|
// progress version
|
|
|
|
struct ExtendStreamLocalInfo: public ProgressLocalInfo
|
|
{
|
|
public:
|
|
|
|
ExtendStreamLocalInfo():
|
|
tupleX(0), resultTupleType(0) {};
|
|
|
|
~ExtendStreamLocalInfo() {
|
|
if( streamY.addr != 0 )
|
|
qp->Close( streamY.addr );
|
|
|
|
if( tupleX != 0 )
|
|
tupleX->DeleteIfAllowed();
|
|
|
|
if( resultTupleType != 0 )
|
|
resultTupleType->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple *tupleX;
|
|
Word streamY;
|
|
TupleType *resultTupleType;
|
|
|
|
double newAttrSize, newAttrSizeExt;
|
|
int stableValue;
|
|
bool sizesFinal;
|
|
};
|
|
|
|
int ExtendStream(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funargs;
|
|
Word wTupleX, wValueY;
|
|
Tuple* tupleXY;
|
|
|
|
ExtendStreamLocalInfo *eli;
|
|
eli = (ExtendStreamLocalInfo*) local.addr;
|
|
|
|
Supplier supplier, supplier2, supplier3;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
|
|
switch( message )
|
|
{
|
|
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH : {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN:
|
|
{
|
|
if ( eli ) {
|
|
delete eli;
|
|
}
|
|
|
|
eli = new ExtendStreamLocalInfo;
|
|
tt->IncReference();
|
|
eli->resultTupleType = tt;
|
|
eli->newAttrSize = 0.0;
|
|
eli->newAttrSizeExt = 0.0;
|
|
eli->stableValue = 50;
|
|
eli->sizesFinal = false;
|
|
|
|
// 1. open the input stream and initiate the arguments
|
|
qp->Open( args[0].addr );
|
|
qp->Request( args[0].addr, wTupleX );
|
|
if( qp->Received( args[0].addr ) )
|
|
{
|
|
eli->read = 1;
|
|
|
|
// 2. compute the result "stream y" from tuple x
|
|
// funargs = qp->Argument(args[1].addr);
|
|
// here should be changed to the following...
|
|
supplier = args[1].addr;
|
|
supplier2 = qp->GetSupplier( supplier, 0 );
|
|
supplier3 = qp->GetSupplier( supplier2, 1 );
|
|
funargs = qp->Argument( supplier3 );
|
|
|
|
(*funargs)[0] = wTupleX;
|
|
qp->Open( supplier3 );
|
|
|
|
// 3. save the local information
|
|
|
|
eli->tupleX = (Tuple*)wTupleX.addr;
|
|
eli->streamY.setAddr( supplier3 );
|
|
local.setAddr(eli);
|
|
}
|
|
else
|
|
{
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
{
|
|
if( local.addr == 0 )
|
|
return CANCEL;
|
|
|
|
// prepare tupleX and wValueY.
|
|
// if wValueY is empty, then get next tupleX
|
|
wValueY.setAddr(0);
|
|
while( wValueY.addr == 0 )
|
|
{
|
|
qp->Request( eli->streamY.addr, wValueY );
|
|
if( !(qp->Received( eli->streamY.addr )) )
|
|
{
|
|
qp->Close( eli->streamY.addr );
|
|
eli->tupleX->DeleteIfAllowed();
|
|
qp->Request( args[0].addr, wTupleX );
|
|
if( qp->Received(args[0].addr) )
|
|
{
|
|
eli->read++;
|
|
|
|
supplier = args[1].addr;
|
|
supplier2 = qp->GetSupplier(supplier, 0);
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
funargs = qp->Argument(supplier3);
|
|
|
|
eli->tupleX = (Tuple*)wTupleX.addr;
|
|
(*funargs)[0] = wTupleX;
|
|
qp->Open( supplier3 );
|
|
|
|
eli->tupleX = (Tuple*)wTupleX.addr;
|
|
eli->streamY.setAddr(supplier3);
|
|
|
|
wValueY.setAddr(0);
|
|
}
|
|
else //streamx is exausted
|
|
{
|
|
eli->streamY.setAddr(0);
|
|
eli->tupleX = 0;
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. compute tupleXY from tupleX and wValueY
|
|
tupleXY = new Tuple( eli->resultTupleType );
|
|
|
|
for( int i = 0; i < eli->tupleX->GetNoAttributes(); i++ )
|
|
tupleXY->CopyAttribute( i, eli->tupleX, i );
|
|
|
|
tupleXY->PutAttribute( eli->tupleX->GetNoAttributes(),
|
|
(Attribute*)wValueY.addr );
|
|
|
|
// for the first 50 tuples returned, observe attribute sizes of the
|
|
// new attribute.
|
|
if ( eli->returned <= eli->stableValue )
|
|
{
|
|
eli->newAttrSize +=
|
|
tupleXY->GetSize( eli->tupleX->GetNoAttributes() );
|
|
eli->newAttrSizeExt +=
|
|
tupleXY->GetExtSize( eli->tupleX->GetNoAttributes() );
|
|
}
|
|
|
|
// setting the result
|
|
result.setAddr( tupleXY );
|
|
eli->returned++;
|
|
return YIELD;
|
|
}
|
|
case CLOSE:
|
|
{
|
|
qp->Close( args[0].addr );
|
|
return 0;
|
|
}
|
|
|
|
case CLOSEPROGRESS:
|
|
{
|
|
if ( eli ) {
|
|
delete eli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case REQUESTPROGRESS:
|
|
|
|
ProgressInfo p1;
|
|
ProgressInfo *pRes;
|
|
|
|
// for the determination of constants see file ConstantsExtendStream
|
|
// and file bin/UpdateProgressConstants
|
|
|
|
static const double wExtendStream =
|
|
ProgressConstants::getValue("ExtRelation-C++", "extendstream",
|
|
"wExtendStream"); // 0.005963; millisecs per tuple read
|
|
static const double vExtendStream =
|
|
ProgressConstants::getValue("ExtRelation-C++", "extendstream",
|
|
"vExtendStream"); // 0.00014405; millisecs per attr returned
|
|
static const double uExtendStream =
|
|
ProgressConstants::getValue("ExtRelation-C++", "extendstream",
|
|
"uExtendStream"); // 0.0067; millisecs per tuple returned
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
|
|
if ( !eli ) {
|
|
return CANCEL;
|
|
}
|
|
|
|
if ( qp->RequestProgress(args[0].addr, &p1) )
|
|
{
|
|
|
|
// 1. attribute sizes
|
|
eli->sizesChanged = false;
|
|
|
|
if ( !eli->sizesInitialized )
|
|
{
|
|
eli->noAttrs = p1.noAttrs + 1;
|
|
eli->attrSize = new double[eli->noAttrs];
|
|
eli->attrSizeExt = new double[eli->noAttrs];
|
|
}
|
|
|
|
if ( !eli->sizesInitialized || p1.sizesChanged ||
|
|
( eli->returned >= eli->stableValue && !eli->sizesFinal ) )
|
|
{
|
|
eli->Size = 0.0;
|
|
eli->SizeExt = 0.0;
|
|
|
|
for( int i = 0; i < eli->noAttrs - 1; i++) //old attrs
|
|
{
|
|
eli->attrSize[i] = p1.attrSize[i];
|
|
eli->attrSizeExt[i] = p1.attrSizeExt[i];
|
|
eli->Size += eli->attrSize[i];
|
|
eli->SizeExt += eli->attrSizeExt[i];
|
|
}
|
|
|
|
if ( eli->returned < eli->stableValue ) //new attrs
|
|
{
|
|
eli->attrSize[eli->noAttrs - 1] = 144;
|
|
eli->attrSizeExt[eli->noAttrs - 1] = 144;
|
|
// size yet unknown. The default 144 is taken from
|
|
// the size of a upoint
|
|
}
|
|
else
|
|
{
|
|
eli->attrSize[eli->noAttrs - 1] =
|
|
eli->newAttrSize / ( eli->stableValue + 1 );
|
|
eli->attrSizeExt[eli->noAttrs - 1] =
|
|
eli->newAttrSizeExt / ( eli->stableValue + 1 );
|
|
}
|
|
eli->Size += eli->attrSize[eli->noAttrs - 1];
|
|
eli->SizeExt += eli->attrSizeExt[eli->noAttrs - 1];
|
|
|
|
eli->sizesInitialized = true;
|
|
eli->sizesChanged = true;
|
|
}
|
|
if ( eli->returned >= eli->stableValue ) eli->sizesFinal = true;
|
|
|
|
pRes->CopySizes(eli);
|
|
|
|
|
|
// 2. result cardinality
|
|
|
|
if ( eli->returned > enoughSuccessesSelection )
|
|
pRes->Card =
|
|
p1.Card * ( (double) (eli->returned + 1) / (eli->read + 1) );
|
|
else
|
|
{
|
|
if ( qp->GetSelectivity(s) != 0.1 )
|
|
pRes->Card = p1.Card * qp->GetSelectivity(s);
|
|
else
|
|
pRes->Card = p1.Card;
|
|
}
|
|
|
|
|
|
// 3. total time
|
|
|
|
pRes->Time = p1.Time +
|
|
p1.Card * wExtendStream + // time per input tuple wo results
|
|
pRes->Card * (uExtendStream + eli->noAttrs * vExtendStream);
|
|
// time per output tuple created
|
|
|
|
// 4. progress
|
|
if ( p1.BTime < 0.1 && pipelinedProgress ) //non-blocking,
|
|
//use pipelining
|
|
pRes->Progress = p1.Progress;
|
|
else
|
|
pRes->Progress =
|
|
(p1.Progress * p1.Time +
|
|
eli->read * wExtendStream +
|
|
eli->returned * (uExtendStream + eli->noAttrs * vExtendStream) )
|
|
/ pRes->Time;
|
|
|
|
|
|
// 5. blocking time and progress
|
|
|
|
pRes->CopyBlocking(p1); //non-blocking operator
|
|
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
2.19.3 Specification of operator ~extendstream~
|
|
|
|
*/
|
|
const string ExtendStreamSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream tuple1) (map tuple1 "
|
|
"stream(type))) -> (stream tuple1*tuple2)"
|
|
"</text--->"
|
|
"<text>_ extendstream [ fun ]</text--->"
|
|
"<text>This operator do the loopjoin between"
|
|
"a stream of tuples and a stream of objects of a certain type."
|
|
" the result is a stream of tuples.</text--->"
|
|
"<text>query UBahn feed extendstream"
|
|
"[ newattr: units(.Trajectory)] consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.19.4 Definition of operator ~extendstream~
|
|
|
|
*/
|
|
Operator extrelextendstream (
|
|
"extendstream", // name
|
|
ExtendStreamSpec, // specification
|
|
ExtendStream, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
ExtendStreamTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.20 Operator ~projectextend~
|
|
|
|
This operator does the same as a combination of ~extend~ with subsequent
|
|
~project~, but in a single operation. This will saves a lot of time, because
|
|
attributes are only copied once and attributes to be removed will not be
|
|
copied at all!
|
|
|
|
The signature of the ~projectextend~ operation is as follows:
|
|
|
|
----
|
|
|
|
stream(tuple((a1 t1) ... (an tn)))
|
|
x (ai1, ... aij)
|
|
x ( (bk1 (tuple((a1 t1) ... (an tn)) --> tk1))
|
|
...
|
|
(bkm (tuple((a1 t1) ... (an tn)) --> tkm))
|
|
)
|
|
--> stream(tuple((ai1 ti1) ... (aij tij) (bk1 tk1) ... (bkm tkm)))
|
|
|
|
where ai, ..., aij in {a1, ..., an}.
|
|
|
|
----
|
|
|
|
For instance,
|
|
|
|
----
|
|
query people feed
|
|
projectext[id;
|
|
wood_perimeter: perimeter(.wood),
|
|
wood_size: area(.wood)
|
|
]
|
|
consume;
|
|
|
|
----
|
|
|
|
*/
|
|
|
|
/*
|
|
2.20.1 Type Mapping for operator ~projectextend~
|
|
|
|
*/
|
|
ListExpr ExtProjectExtendTypeMap(ListExpr args)
|
|
{
|
|
|
|
string err = "stream(tuple(...)) x (a1,..., an) x "
|
|
"( (b1 map1) ... (bm : mapm)) expected";
|
|
|
|
if(nl->ListLength(args)!=3){
|
|
return listutils::typeError("wrong number of arguments, expected: 3");
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr attrList = nl->Second(args);
|
|
ListExpr extList = nl->Third(args);
|
|
|
|
if(!listutils::isTupleStream(stream)){
|
|
return listutils::typeError(err + " first arg is not a tuple stream");
|
|
}
|
|
|
|
// check whether all elements of attrlist are attrnames in the
|
|
// first argument
|
|
if(nl->AtomType(attrList)!=NoAtom){
|
|
return listutils::typeError(err +
|
|
" second arg is not a list of attribute names");
|
|
}
|
|
|
|
ListExpr streamattr = nl->Second(nl->Second(stream));
|
|
set<string> names;
|
|
int noAttrs=0;
|
|
ListExpr numberList=nl->TheEmptyList();
|
|
|
|
ListExpr newAttrList=nl->TheEmptyList();
|
|
ListExpr lastNewAttrList = nl->TheEmptyList();
|
|
ListExpr lastNumberList = nl->TheEmptyList();
|
|
bool firstCall = true;
|
|
|
|
if(nl->IsEmpty(attrList)){
|
|
noAttrs = 0;
|
|
numberList = nl->TheEmptyList();
|
|
newAttrList = nl->TheEmptyList();
|
|
} else {
|
|
noAttrs = nl->ListLength(attrList);
|
|
ListExpr attrType;
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr attr = nl->First(attrList);
|
|
attrList = nl->Rest(attrList);
|
|
if(nl->AtomType(attr)!=SymbolType){
|
|
return listutils::typeError(err + "(invalid attr name found)");
|
|
}
|
|
string attrName = nl->SymbolValue(attr);
|
|
if(names.find(attrName)!=names.end()){
|
|
return listutils::typeError(err + "(attr name found twice)");
|
|
}
|
|
names.insert(attrName);
|
|
int j = listutils::findAttribute(streamattr, attrName, attrType);
|
|
if(j==0){
|
|
return listutils::typeError(err + "(attr name "+
|
|
attrName + " not found)");
|
|
}
|
|
if (firstCall) {
|
|
firstCall = false;
|
|
newAttrList = nl->OneElemList(nl->TwoElemList(attr, attrType));
|
|
lastNewAttrList = newAttrList;
|
|
numberList = nl->OneElemList(nl->IntAtom(j));
|
|
lastNumberList = numberList;
|
|
} else {
|
|
lastNewAttrList = nl->Append(lastNewAttrList,
|
|
nl->TwoElemList(attr, attrType));
|
|
lastNumberList = nl->Append(lastNumberList, nl->IntAtom(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
// check the third argument (must be a list of pairs (attrname map))
|
|
// the argument of map must be the tuple type in the stream and the
|
|
// result must be in kind DATA
|
|
if(nl->IsAtom(extList)){
|
|
return listutils::typeError(err + " (wrong extension list)");
|
|
}
|
|
|
|
ListExpr tupletype = nl->Second(stream);
|
|
while(!nl->IsEmpty(extList)){
|
|
ListExpr ext = nl->First(extList);
|
|
extList = nl->Rest(extList);
|
|
if(nl->ListLength(ext)!=2){
|
|
return listutils::typeError(err + " (problem in extension list");
|
|
}
|
|
ListExpr nameL = nl->First(ext);
|
|
ListExpr map = nl->Second(ext);
|
|
string err;
|
|
if(!listutils::isValidAttributeName(nameL,err) ||
|
|
!listutils::isMap<1>(map)){
|
|
return listutils::typeError(err + " (problem in extension list");
|
|
}
|
|
string name = nl->SymbolValue(nameL);
|
|
if(names.find(name)!=names.end()){
|
|
return listutils::typeError(err +
|
|
"( conflicting names found " + name+")");
|
|
}
|
|
names.insert(name);
|
|
if(!nl->Equal(tupletype,nl->Second(map))){
|
|
return listutils::typeError(err + "( invalid argument type for map)");
|
|
}
|
|
ListExpr mapres = nl->Third(map);
|
|
if(!listutils::isDATA(mapres)){
|
|
return listutils::typeError(err + " (map result for " + name
|
|
+ " not in kind DATA)");
|
|
}
|
|
if(firstCall) {
|
|
firstCall = false;
|
|
newAttrList = nl->OneElemList(nl->TwoElemList(nameL, mapres));
|
|
lastNewAttrList = newAttrList;
|
|
} else {
|
|
lastNewAttrList =
|
|
nl->Append(lastNewAttrList, nl->TwoElemList(nameL,mapres));
|
|
}
|
|
}
|
|
|
|
if(nl->IsEmpty(newAttrList)){
|
|
return listutils::typeError(err + "(resulting tuple would be empty");
|
|
}
|
|
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->TwoElemList( nl->IntAtom(noAttrs),
|
|
numberList),
|
|
nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList( nl->SymbolAtom(Tuple::BasicType()),
|
|
newAttrList)));
|
|
|
|
}
|
|
|
|
/*
|
|
2.20.2 Value Mapping for operator ~projectextend~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
// standard version
|
|
|
|
int
|
|
ExtProjectExtendValueMap(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
// cout << "ExtProjectExtendValueMap() called." << endl;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch (message)
|
|
{
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN :
|
|
{
|
|
tt->IncReference();
|
|
TupleType *tupleType = tt;
|
|
local.addr = tupleType;
|
|
qp->Open(args[0].addr);
|
|
return 0;
|
|
}
|
|
case REQUEST :
|
|
{
|
|
Word elem1, elem2, value;
|
|
int i=0, noOfAttrs=0, index=0, nooffun=0;
|
|
Supplier son, supplier, supplier2, supplier3;
|
|
ArgVectorPointer extFunArgs;
|
|
|
|
qp->Request(args[0].addr, elem1);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
TupleType *tupleType = (TupleType *)local.addr;
|
|
Tuple *currTuple = (Tuple*) elem1.addr;
|
|
Tuple *resultTuple = new Tuple( tupleType );
|
|
|
|
// copy attrs from projection list
|
|
noOfAttrs = ((CcInt*)args[3].addr)->GetIntval();
|
|
for(i = 0; i < noOfAttrs; i++)
|
|
{
|
|
son = qp->GetSupplier(args[4].addr, i);
|
|
qp->Request(son, elem2);
|
|
index = ((CcInt*)elem2.addr)->GetIntval();
|
|
resultTuple->CopyAttribute(index-1, currTuple, i);
|
|
}
|
|
|
|
// evaluate and add attrs from extension list
|
|
supplier = args[2].addr; // get list of ext-functions
|
|
nooffun = qp->GetNoSons(supplier); // get num of ext-functions
|
|
for(i = 0; i< nooffun; i++)
|
|
{
|
|
supplier2 = qp->GetSupplier(supplier, i); // get an ext-function
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
extFunArgs = qp->Argument(supplier3);
|
|
((*extFunArgs)[0]).setAddr(currTuple); // pass argument
|
|
qp->Request(supplier3,value); // call extattr mapping
|
|
// The original implementation tried to avoid copying the function result,
|
|
// but somehow, this results in a strongly growing tuplebuffer on disk:
|
|
// resultTuple->PutAttribute(
|
|
// noOfAttrs + i, (Attribute*)value.addr );
|
|
// qp->ReInitResultStorage( supplier3 );
|
|
resultTuple->PutAttribute( noOfAttrs + i,
|
|
((Attribute*)value.addr)->Clone() );
|
|
}
|
|
currTuple->DeleteIfAllowed();
|
|
result.setAddr(resultTuple);
|
|
return YIELD;
|
|
}
|
|
else return CANCEL;
|
|
}
|
|
case CLOSE :
|
|
{
|
|
if(local.addr)
|
|
{
|
|
((TupleType *)local.addr)->DeleteIfAllowed();
|
|
qp->Close(args[0].addr);
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
# else
|
|
|
|
// progress version
|
|
|
|
class ProjectExtendLocalInfo: public ProgressLocalInfo
|
|
{
|
|
public:
|
|
|
|
ProjectExtendLocalInfo():
|
|
resultTupleType(0),stableValue(0), sizesFinal(false),
|
|
noOldAttrs(0),noNewAttrs(0),attrSizeTmp(0), attrSizeExtTmp(0) {};
|
|
|
|
~ProjectExtendLocalInfo() {
|
|
if(resultTupleType){
|
|
resultTupleType->DeleteIfAllowed();
|
|
resultTupleType=0;
|
|
}
|
|
if(attrSizeTmp){
|
|
delete [] attrSizeTmp;
|
|
attrSizeTmp =0;
|
|
}
|
|
if(attrSizeExtTmp){
|
|
delete [] attrSizeExtTmp;
|
|
attrSizeExtTmp =0;
|
|
}
|
|
}
|
|
|
|
TupleType *resultTupleType;
|
|
int stableValue;
|
|
bool sizesFinal;
|
|
int noOldAttrs, noNewAttrs;
|
|
double *attrSizeTmp;
|
|
double *attrSizeExtTmp;
|
|
};
|
|
|
|
|
|
struct projectextendLI2{
|
|
TupleType* tt;
|
|
Word elem1;
|
|
Word elem2;
|
|
Word value;
|
|
};
|
|
|
|
|
|
int
|
|
ExtProjectExtendValueMap(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
// cout << "ExtProjectExtendValueMap() called." << endl;
|
|
int index=0;
|
|
Supplier son, supplier, supplier2, supplier3;
|
|
ArgVectorPointer extFunArgs;
|
|
|
|
ProjectExtendLocalInfo *eli;
|
|
eli = (ProjectExtendLocalInfo*) local.addr;
|
|
|
|
projectextendLI2* li2 = (projectextendLI2*) qp->GetLocal2(s).addr;
|
|
|
|
switch (message)
|
|
{
|
|
|
|
case INIT: {
|
|
li2 = new projectextendLI2();
|
|
li2->tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = li2;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(li2){
|
|
li2->tt->DeleteIfAllowed();
|
|
delete li2;
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN :
|
|
{
|
|
if ( eli ) {
|
|
delete eli;
|
|
}
|
|
|
|
eli = new ProjectExtendLocalInfo;
|
|
li2->tt->IncReference();
|
|
eli->resultTupleType = li2->tt;
|
|
eli->read = 0;
|
|
eli->stableValue = 50;
|
|
eli->sizesFinal = false;
|
|
eli->noOldAttrs = ((CcInt*)args[3].addr)->GetIntval();
|
|
eli->noNewAttrs = qp->GetNoSons(args[2].addr);
|
|
eli->attrSizeTmp = new double[eli->noNewAttrs];
|
|
eli->attrSizeExtTmp = new double[eli->noNewAttrs];
|
|
for (int i = 0; i < eli->noNewAttrs; i++)
|
|
{
|
|
eli->attrSizeTmp[i] = 0.0;
|
|
eli->attrSizeExtTmp[i] = 0.0;
|
|
}
|
|
|
|
local.setAddr(eli);
|
|
|
|
qp->Open(args[0].addr);
|
|
|
|
return 0;
|
|
}
|
|
case REQUEST :
|
|
{
|
|
if(!eli){
|
|
return CANCEL;
|
|
}
|
|
qp->Request(args[0].addr, li2->elem1);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
eli->read++;
|
|
|
|
Tuple *currTuple = (Tuple*) li2->elem1.addr;
|
|
Tuple *resultTuple = new Tuple( eli->resultTupleType );
|
|
|
|
// copy attrs from projection list
|
|
|
|
for(int i = 0; i < eli->noOldAttrs; i++)
|
|
{
|
|
son = qp->GetSupplier(args[4].addr, i);
|
|
qp->Request(son, li2->elem2);
|
|
index = ((CcInt*)li2->elem2.addr)->GetIntval();
|
|
resultTuple->CopyAttribute(index-1, currTuple, i);
|
|
}
|
|
|
|
// evaluate and add attrs from extension list
|
|
supplier = args[2].addr; // get list of ext-functions
|
|
for(int i = 0; i < eli->noNewAttrs; i++)
|
|
{
|
|
supplier2 = qp->GetSupplier(supplier, i); // get an ext-function
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
extFunArgs = qp->Argument(supplier3);
|
|
((*extFunArgs)[0]).setAddr(currTuple); // pass argument
|
|
qp->Request(supplier3,li2->value); // call extattr mapping
|
|
|
|
resultTuple->PutAttribute( eli->noOldAttrs + i,
|
|
((Attribute*)li2->value.addr)->Clone() );
|
|
|
|
if (eli->read <= eli->stableValue)
|
|
{
|
|
eli->attrSizeTmp[i] += resultTuple->GetSize( eli->noOldAttrs + i );
|
|
eli->attrSizeExtTmp[i] +=
|
|
resultTuple->GetExtSize( eli->noOldAttrs + i );
|
|
}
|
|
|
|
}
|
|
currTuple->DeleteIfAllowed();
|
|
result.setAddr(resultTuple);
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
}
|
|
case CLOSE :
|
|
{
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
case CLOSEPROGRESS:
|
|
{
|
|
if ( eli ) {
|
|
delete eli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUESTPROGRESS:
|
|
|
|
ProgressInfo p1;
|
|
ProgressInfo *pRes;
|
|
const double uProjectExtend = 0.0012; //millisecs per tuple
|
|
const double vProjectExtend = 0.00085; //millisecs per tuple
|
|
//and attribute
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
|
|
if ( !eli ) {
|
|
return CANCEL;
|
|
}
|
|
|
|
if ( qp->RequestProgress(args[0].addr, &p1) )
|
|
{
|
|
eli->sizesChanged = false;
|
|
|
|
if ( !eli->sizesInitialized )
|
|
{
|
|
eli->noAttrs = eli->noOldAttrs + eli->noNewAttrs;
|
|
eli->attrSize = new double[eli->noAttrs];
|
|
eli->attrSizeExt = new double[eli->noAttrs];
|
|
}
|
|
|
|
if ( !eli->sizesInitialized || p1.sizesChanged ||
|
|
( eli->read >= eli->stableValue && !eli->sizesFinal ) )
|
|
{
|
|
eli->Size = 0.0;
|
|
eli->SizeExt = 0.0;
|
|
|
|
for( int i = 0; i < eli->noOldAttrs; i++) //old attrs
|
|
{
|
|
son = qp->GetSupplier(args[4].addr, i);
|
|
qp->Request(son, li2->elem2);
|
|
index = ((CcInt*)li2->elem2.addr)->GetIntval();
|
|
eli->attrSize[i] = p1.attrSize[index-1];
|
|
eli->attrSizeExt[i] = p1.attrSizeExt[index-1];
|
|
eli->Size += eli->attrSize[i];
|
|
eli->SizeExt += eli->attrSizeExt[i];
|
|
}
|
|
|
|
if ( eli->read < eli->stableValue ) //new attrs
|
|
{
|
|
for (int j = 0; j < eli->noNewAttrs; j++)
|
|
{
|
|
eli->attrSize[j + eli->noOldAttrs] = 12; //size yet unknown
|
|
eli->attrSizeExt[j + eli->noOldAttrs] = 12;
|
|
eli->Size += eli->attrSize[j + eli->noOldAttrs];
|
|
eli->SizeExt += eli->attrSizeExt[j + eli->noOldAttrs];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int j = 0; j < eli->noNewAttrs; j++)
|
|
{
|
|
eli->attrSize[j + eli->noOldAttrs] = eli->attrSizeTmp[j] /
|
|
eli->stableValue;
|
|
eli->attrSizeExt[j + eli->noOldAttrs] = eli->attrSizeExtTmp[j] /
|
|
eli->stableValue;
|
|
eli->Size += eli->attrSize[j + eli->noOldAttrs];
|
|
eli->SizeExt += eli->attrSizeExt[j + eli->noOldAttrs];
|
|
}
|
|
}
|
|
eli->sizesInitialized = true;
|
|
eli->sizesChanged = true;
|
|
}
|
|
if ( eli->read >= eli->stableValue ) eli->sizesFinal = true;
|
|
|
|
pRes->Card = p1.Card;
|
|
|
|
pRes->CopySizes(eli);
|
|
|
|
pRes->Time = p1.Time +
|
|
p1.Card * (uProjectExtend + eli->noAttrs * vProjectExtend);
|
|
|
|
|
|
if ( p1.BTime < 0.1 && pipelinedProgress ) //non-blocking,
|
|
//use pipelining
|
|
pRes->Progress = p1.Progress;
|
|
else
|
|
pRes->Progress =
|
|
(p1.Progress * p1.Time +
|
|
eli->read * (uProjectExtend + eli->noAttrs * vProjectExtend))
|
|
/ pRes->Time;
|
|
|
|
pRes->CopyBlocking(p1); //non-blocking operator
|
|
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
2.20.3 Specification of operator ~projectextend~
|
|
|
|
*/
|
|
const string ExtProjectExtendSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>"
|
|
"stream(tuple((a1 t1) ... (an tn))) \n"
|
|
" x (ai1, ... aij)\n"
|
|
" x ( (bk1 (tuple((a1 t1) ... (an tn)) --> tk1))\n"
|
|
" ...\n"
|
|
" (bkm (tuple((a1 t1) ... (an tn)) --> tkm))\n"
|
|
" )\n"
|
|
" --> stream(tuple((ai1 ti1) ... (aij tij) (bk1 tk1) ... (bkm tkm)))\n"
|
|
" where ai, ..., aij in {a1, ..., an}"
|
|
"</text--->"
|
|
"<text>_ projectextend[ list; funlist ]</text--->"
|
|
"<text>Project tuple to list of attributes and extend with "
|
|
"new ones from funlist. The projection list may be empty, "
|
|
"but the extension list must contain at least one entry."
|
|
"You may even 'replace' an attribute (but be careful with that)."
|
|
"</text--->"
|
|
"<text>query Orte feed projectextend"
|
|
"[Ort, Vorwahl; BevT2: .BevT*2, BevT: (.BevT + 30)] consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.20.4 Definition of operator ~projectextend~
|
|
|
|
*/
|
|
Operator extrelprojectextend (
|
|
"projectextend", // name
|
|
ExtProjectExtendSpec, // specification
|
|
ExtProjectExtendValueMap, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
ExtProjectExtendTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.21 Operator ~projectextendstream~
|
|
|
|
This operator does the same as ~extendstream~ with a projection on some
|
|
attributes. It is very often that a big attribute is converted into a
|
|
stream of a small pieces, for example, a text into keywords. When
|
|
applying the ~extendstream~ operator, the big attribute belongs to the
|
|
result type, and is copied for every occurrence of its smaller pieces.
|
|
A projection is a normal operation after such operation. To avoid this
|
|
copying, the projection operation is now built in the operation.
|
|
|
|
The type mapping function of the ~projectextendstream~ operation is
|
|
as follows:
|
|
|
|
---- ((stream (tuple ((x1 T1) ... (xn Tn)))) (ai1 ... aik)
|
|
(map (tuple ((x1 T1) ... (xn Tn))) (b stream(Tb)))) ->
|
|
(APPEND
|
|
(k (i1 ... ik))
|
|
(stream (tuple ((ai1 Ti1) ... (aik Tik)(b Tb)))))
|
|
----
|
|
|
|
For instance,
|
|
|
|
---- query people feed
|
|
projectextendstream [id, age; parts : .name keywords]
|
|
consume;
|
|
----
|
|
|
|
*/
|
|
ListExpr ProjectExtendStreamTypeMap(ListExpr args)
|
|
{
|
|
|
|
if(nl->ListLength(args)!=3){
|
|
return listutils::typeError("three arguments expected");
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr attrList = nl->Second(args);
|
|
ListExpr namedMapL = nl->Third(args);
|
|
string err ="stream(tuple(K) x (a1..an) x "
|
|
"(b (tuple(K) -> stream(L))) expected";
|
|
|
|
if(!listutils::isTupleStream(stream) ||
|
|
nl->IsAtom(attrList) ||
|
|
nl->ListLength(namedMapL)!=1){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr namedMap = nl->First(namedMapL);
|
|
if(nl->ListLength(namedMap)!=2){
|
|
return listutils::typeError(err +
|
|
"(third argument must be a pair of name , map)");
|
|
}
|
|
|
|
// process the projection list
|
|
ListExpr streamattr = nl->Second(nl->Second(stream));
|
|
set<string> names;
|
|
// int noAttrs;
|
|
ListExpr numberList = nl->TheEmptyList();
|
|
|
|
ListExpr newAttrList = nl->TheEmptyList();
|
|
ListExpr lastNewAttrList = nl->TheEmptyList();
|
|
ListExpr lastNumberList = nl->TheEmptyList();
|
|
bool firstCall = true;
|
|
|
|
int attrno = nl->ListLength(attrList);
|
|
if(nl->IsEmpty(attrList)){
|
|
//noAttrs = 0;
|
|
numberList = nl->TheEmptyList();
|
|
newAttrList = nl->TheEmptyList();
|
|
} else {
|
|
//noAttrs = nl->ListLength(attrList);
|
|
ListExpr attrType;
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr attr = nl->First(attrList);
|
|
attrList = nl->Rest(attrList);
|
|
if(nl->AtomType(attr)!=SymbolType){
|
|
return listutils::typeError(err + "(invalid attr name found)");
|
|
}
|
|
string attrName = nl->SymbolValue(attr);
|
|
if(names.find(attrName)!=names.end()){
|
|
return listutils::typeError(err + "(attr name found twice)");
|
|
}
|
|
names.insert(attrName);
|
|
int j = listutils::findAttribute(streamattr, attrName, attrType);
|
|
if(j==0){
|
|
return listutils::typeError(err + "(attr name "+
|
|
attrName + " not found)");
|
|
}
|
|
if (firstCall) {
|
|
firstCall = false;
|
|
newAttrList = nl->OneElemList(nl->TwoElemList(attr, attrType));
|
|
lastNewAttrList = newAttrList;
|
|
numberList = nl->OneElemList(nl->IntAtom(j));
|
|
lastNumberList = numberList;
|
|
} else {
|
|
lastNewAttrList = nl->Append(lastNewAttrList,
|
|
nl->TwoElemList(attr, attrType));
|
|
lastNumberList = nl->Append(lastNumberList, nl->IntAtom(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
ListExpr tupletype = nl->Second(stream);
|
|
ListExpr mapname = nl->First(namedMap);
|
|
ListExpr map = nl->Second(namedMap);
|
|
if(nl->AtomType(mapname)!=SymbolType ||
|
|
!listutils::isMap<1>(map)){
|
|
return listutils::typeError(err +
|
|
"(third argument must be a pair of name and map)");
|
|
}
|
|
string errmsg;
|
|
if(!listutils::isValidAttributeName(mapname, errmsg)){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if(!nl->Equal(tupletype,nl->Second(map))){
|
|
return listutils::typeError(err + "(map argument differs from tuple type");
|
|
}
|
|
if(!listutils::isDATAStream(nl->Third(map))){
|
|
return listutils::typeError(err + " (result of mpa is not a DATA stream");
|
|
}
|
|
|
|
ListExpr mapData = nl->Second(nl->Third(map));
|
|
if(firstCall){
|
|
newAttrList = nl->OneElemList(nl->TwoElemList(mapname,mapData));
|
|
} else {
|
|
lastNewAttrList = nl->Append(lastNewAttrList,
|
|
nl->TwoElemList(mapname,mapData));
|
|
}
|
|
|
|
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->TwoElemList( nl->IntAtom( attrno ),
|
|
numberList),
|
|
nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom(Tuple::BasicType()),
|
|
newAttrList)));
|
|
|
|
}
|
|
|
|
/*
|
|
2.19.2 Value mapping function of operator ~projectextendstream~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
// standard version
|
|
|
|
struct ProjectExtendStreamLocalInfo
|
|
{
|
|
Tuple *tupleX;
|
|
Word streamY;
|
|
TupleType *resultTupleType;
|
|
vector<long> attrs;
|
|
};
|
|
|
|
int ProjectExtendStream(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funargs;
|
|
Word wTupleX, wValueY;
|
|
Tuple* tupleXY;
|
|
ProjectExtendStreamLocalInfo *localinfo;
|
|
|
|
TupleType *resultTupleType;
|
|
|
|
Supplier supplier, supplier2, supplier3;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch( message )
|
|
{
|
|
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN:
|
|
{
|
|
//1. open the input stream and initiate the arguments
|
|
qp->Open( args[0].addr );
|
|
qp->Request( args[0].addr, wTupleX );
|
|
if( qp->Received( args[0].addr ) )
|
|
{
|
|
//2. compute the result "stream y" from tuple x
|
|
supplier = args[2].addr;
|
|
supplier2 = qp->GetSupplier( supplier, 0 );
|
|
supplier3 = qp->GetSupplier( supplier2, 1 );
|
|
funargs = qp->Argument( supplier3 );
|
|
|
|
(*funargs)[0] = wTupleX;
|
|
qp->Open( supplier3 );
|
|
|
|
//3. save the local information
|
|
localinfo = new ProjectExtendStreamLocalInfo;
|
|
tt->IncReference();
|
|
localinfo->resultTupleType = tt;
|
|
localinfo->tupleX = (Tuple*)wTupleX.addr;
|
|
localinfo->streamY.setAddr( supplier3 );
|
|
|
|
//4. get the attribute numbers
|
|
int noOfAttrs = ((CcInt*)args[3].addr)->GetIntval();
|
|
for( int i = 0; i < noOfAttrs; i++)
|
|
{
|
|
Supplier son = qp->GetSupplier(args[4].addr, i);
|
|
Word elem2;
|
|
qp->Request(son, elem2);
|
|
localinfo->attrs.push_back( ((CcInt*)elem2.addr)->GetIntval()-1 );
|
|
}
|
|
|
|
local.setAddr(localinfo);
|
|
}
|
|
else
|
|
{
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
{
|
|
if( local.addr == 0 )
|
|
return CANCEL;
|
|
|
|
//1. recover local information
|
|
localinfo=(ProjectExtendStreamLocalInfo *) local.addr;
|
|
resultTupleType = (TupleType *)localinfo->resultTupleType;
|
|
|
|
//2. prepare tupleX and wValueY.
|
|
//If wValueY is empty, then get next tupleX
|
|
wValueY.setAddr(0);
|
|
while( wValueY.addr == 0 )
|
|
{
|
|
qp->Request( localinfo->streamY.addr, wValueY );
|
|
if( !(qp->Received( localinfo->streamY.addr )) )
|
|
{
|
|
qp->Close( localinfo->streamY.addr );
|
|
localinfo->tupleX->DeleteIfAllowed();
|
|
qp->Request( args[0].addr, wTupleX );
|
|
if( qp->Received(args[0].addr) )
|
|
{
|
|
supplier = args[2].addr;
|
|
supplier2 = qp->GetSupplier(supplier, 0);
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
funargs = qp->Argument(supplier3);
|
|
|
|
localinfo->tupleX = (Tuple*)wTupleX.addr;
|
|
(*funargs)[0] = wTupleX;
|
|
qp->Open( supplier3 );
|
|
|
|
localinfo->tupleX = (Tuple*)wTupleX.addr;
|
|
localinfo->streamY.setAddr(supplier3);
|
|
|
|
wValueY.setAddr(0);
|
|
}
|
|
else //streamx is exausted
|
|
{
|
|
localinfo->streamY.setAddr(0);
|
|
localinfo->tupleX = 0;
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//3. compute tupleXY from tupleX and wValueY
|
|
tupleXY = new Tuple( localinfo->resultTupleType );
|
|
|
|
size_t i;
|
|
for( i = 0; i < localinfo->attrs.size(); i++ )
|
|
tupleXY->CopyAttribute( localinfo->attrs[i], localinfo->tupleX, i );
|
|
|
|
tupleXY->PutAttribute( i, (Attribute*)wValueY.addr );
|
|
|
|
// setting the result
|
|
result.setAddr( tupleXY );
|
|
return YIELD;
|
|
}
|
|
case CLOSE:
|
|
{
|
|
if( local.addr != 0 )
|
|
{
|
|
localinfo = (ProjectExtendStreamLocalInfo *)local.addr;
|
|
|
|
if( localinfo->streamY.addr != 0 )
|
|
qp->Close( localinfo->streamY.addr );
|
|
|
|
if( localinfo->tupleX != 0 )
|
|
localinfo->tupleX->DeleteIfAllowed();
|
|
|
|
if( localinfo->resultTupleType != 0 )
|
|
localinfo->resultTupleType->DeleteIfAllowed();
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close( args[0].addr );
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
# else
|
|
|
|
// progress version
|
|
|
|
|
|
struct ProjectExtendStreamLocalInfo: public ProgressLocalInfo
|
|
{
|
|
public:
|
|
|
|
ProjectExtendStreamLocalInfo():
|
|
tupleX(0), resultTupleType(0), noOldAttrs(0) { streamY.setAddr(0); };
|
|
|
|
~ProjectExtendStreamLocalInfo() {
|
|
if( streamY.addr != 0 )
|
|
qp->Close( streamY.addr );
|
|
|
|
if( tupleX != 0 )
|
|
tupleX->DeleteIfAllowed();
|
|
|
|
if( resultTupleType != 0 )
|
|
resultTupleType->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple *tupleX;
|
|
Word streamY;
|
|
TupleType *resultTupleType;
|
|
vector<long> attrs;
|
|
|
|
int noOldAttrs;
|
|
double newAttrSize, newAttrSizeExt;
|
|
int stableValue;
|
|
bool sizesFinal;
|
|
};
|
|
|
|
|
|
int ProjectExtendStream(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
ArgVectorPointer funargs;
|
|
Word wTupleX, wValueY, elem2;
|
|
int index = 0;
|
|
Tuple* tupleXY;
|
|
|
|
ProjectExtendStreamLocalInfo *eli;
|
|
eli = (ProjectExtendStreamLocalInfo*) local.addr;
|
|
|
|
Supplier son, supplier, supplier2, supplier3;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch( message )
|
|
{
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN:
|
|
{
|
|
if ( eli ) {
|
|
delete eli;
|
|
}
|
|
|
|
eli = new ProjectExtendStreamLocalInfo;
|
|
tt->IncReference();
|
|
eli->resultTupleType = tt;
|
|
eli->newAttrSize = 0.0;
|
|
eli->newAttrSizeExt = 0.0;
|
|
eli->stableValue = 50;
|
|
eli->sizesFinal = false;
|
|
eli->noOldAttrs = ((CcInt*)args[3].addr)->GetIntval();
|
|
|
|
// 1. open the input stream and initiate the arguments
|
|
qp->Open( args[0].addr );
|
|
qp->Request( args[0].addr, wTupleX );
|
|
if( qp->Received( args[0].addr ) )
|
|
{
|
|
eli->read = 1;
|
|
|
|
// 2. compute the result "stream y" from tuple x
|
|
supplier = args[2].addr;
|
|
supplier2 = qp->GetSupplier( supplier, 0 );
|
|
supplier3 = qp->GetSupplier( supplier2, 1 );
|
|
funargs = qp->Argument( supplier3 );
|
|
|
|
(*funargs)[0] = wTupleX;
|
|
qp->Open( supplier3 );
|
|
|
|
// 3. save the local information
|
|
|
|
eli->tupleX = (Tuple*)wTupleX.addr;
|
|
eli->streamY.setAddr( supplier3 );
|
|
|
|
// 4. get the attribute numbers
|
|
int noOfAttrs = ((CcInt*)args[3].addr)->GetIntval();
|
|
eli->noOldAttrs = noOfAttrs;
|
|
for( int i = 0; i < noOfAttrs; i++)
|
|
{
|
|
Supplier son = qp->GetSupplier(args[4].addr, i);
|
|
Word elem2;
|
|
qp->Request(son, elem2);
|
|
eli->attrs.push_back( ((CcInt*)elem2.addr)->GetIntval()-1 );
|
|
}
|
|
|
|
local.setAddr(eli);
|
|
}
|
|
else
|
|
{
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
{
|
|
if( local.addr == 0 )
|
|
return CANCEL;
|
|
|
|
// 1. prepare tupleX and wValueY.
|
|
// if wValueY is empty, then get next tupleX
|
|
wValueY.setAddr(0);
|
|
while( wValueY.addr == 0 )
|
|
{
|
|
qp->Request( eli->streamY.addr, wValueY );
|
|
if( !(qp->Received( eli->streamY.addr )) )
|
|
{
|
|
qp->Close( eli->streamY.addr );
|
|
eli->tupleX->DeleteIfAllowed();
|
|
qp->Request( args[0].addr, wTupleX );
|
|
if( qp->Received(args[0].addr) )
|
|
{
|
|
eli->read++;
|
|
|
|
supplier = args[2].addr;
|
|
supplier2 = qp->GetSupplier(supplier, 0);
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
funargs = qp->Argument(supplier3);
|
|
|
|
eli->tupleX = (Tuple*)wTupleX.addr;
|
|
(*funargs)[0] = wTupleX;
|
|
qp->Open( supplier3 );
|
|
|
|
eli->tupleX = (Tuple*)wTupleX.addr;
|
|
eli->streamY.setAddr(supplier3);
|
|
|
|
wValueY.setAddr(0);
|
|
}
|
|
else //streamx is exausted
|
|
{
|
|
eli->streamY.setAddr(0);
|
|
eli->tupleX = 0;
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. compute tupleXY from tupleX and wValueY
|
|
tupleXY = new Tuple( eli->resultTupleType );
|
|
|
|
size_t i;
|
|
for(i = 0; i < eli->attrs.size(); i++ )
|
|
tupleXY->CopyAttribute( eli->attrs[i], eli->tupleX, i );
|
|
|
|
tupleXY->PutAttribute( eli->attrs.size(),
|
|
(Attribute*)wValueY.addr );
|
|
|
|
// for the first 50 tuples returned, observe attribute sizes of the
|
|
// new attribute.
|
|
if ( eli->returned <= eli->stableValue )
|
|
{
|
|
eli->newAttrSize +=
|
|
tupleXY->GetSize( eli->attrs.size() );
|
|
eli->newAttrSizeExt +=
|
|
tupleXY->GetExtSize( eli->attrs.size() );
|
|
}
|
|
|
|
// setting the result
|
|
result.setAddr( tupleXY );
|
|
eli->returned++;
|
|
return YIELD;
|
|
}
|
|
|
|
case CLOSE:
|
|
{
|
|
qp->Close( args[0].addr );
|
|
return 0;
|
|
}
|
|
|
|
case CLOSEPROGRESS:
|
|
{
|
|
if ( eli ) {
|
|
delete eli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUESTPROGRESS:
|
|
|
|
ProgressInfo p1;
|
|
ProgressInfo *pRes;
|
|
|
|
// For the determination of constants see file ConstantsExtendStream.
|
|
// We assume the same constants are valid here.
|
|
|
|
const double wExtendStream = 0.005963; //millisecs per tuple read
|
|
const double uExtendStream = 0.0067; //millisecs per tuple returned
|
|
const double vExtendStream = 0.00014405; //millisec per attr returned
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
|
|
if ( !eli ) {
|
|
return CANCEL;
|
|
}
|
|
|
|
if ( qp->RequestProgress(args[0].addr, &p1) )
|
|
{
|
|
|
|
// 1. attribute sizes
|
|
eli->sizesChanged = false;
|
|
|
|
if ( !eli->sizesInitialized )
|
|
{
|
|
eli->noAttrs = eli->noOldAttrs + 1;
|
|
eli->attrSize = new double[eli->noAttrs];
|
|
eli->attrSizeExt = new double[eli->noAttrs];
|
|
}
|
|
|
|
if ( !eli->sizesInitialized || p1.sizesChanged ||
|
|
( eli->returned >= eli->stableValue && !eli->sizesFinal ) )
|
|
{
|
|
eli->Size = 0.0;
|
|
eli->SizeExt = 0.0;
|
|
|
|
for( int i = 0; i < eli->noOldAttrs; i++) //old attrs
|
|
{
|
|
son = qp->GetSupplier(args[4].addr, i);
|
|
qp->Request(son, elem2);
|
|
index = ((CcInt*)elem2.addr)->GetIntval();
|
|
eli->attrSize[i] = p1.attrSize[index-1];
|
|
eli->attrSizeExt[i] = p1.attrSizeExt[index-1];
|
|
eli->Size += eli->attrSize[i];
|
|
eli->SizeExt += eli->attrSizeExt[i];
|
|
}
|
|
|
|
if ( eli->returned < eli->stableValue ) //new attr
|
|
{
|
|
eli->attrSize[eli->noAttrs - 1] = 144;
|
|
eli->attrSizeExt[eli->noAttrs - 1] = 144;
|
|
// size yet unknown. The default 144 is taken from
|
|
// the size of a upoint
|
|
}
|
|
else
|
|
{
|
|
eli->attrSize[eli->noAttrs - 1] =
|
|
eli->newAttrSize / ( eli->stableValue + 1 );
|
|
eli->attrSizeExt[eli->noAttrs - 1] =
|
|
eli->newAttrSizeExt / ( eli->stableValue + 1 );
|
|
}
|
|
eli->Size += eli->attrSize[eli->noAttrs - 1];
|
|
eli->SizeExt += eli->attrSizeExt[eli->noAttrs - 1];
|
|
|
|
eli->sizesInitialized = true;
|
|
eli->sizesChanged = true;
|
|
}
|
|
if ( eli->returned >= eli->stableValue ) eli->sizesFinal = true;
|
|
|
|
pRes->CopySizes(eli);
|
|
|
|
|
|
// 2. result cardinality
|
|
|
|
pRes->Card =
|
|
p1.Card * ( (double) (eli->returned + 1) / (eli->read + 1) );
|
|
|
|
|
|
// 3. total time
|
|
|
|
pRes->Time = p1.Time +
|
|
p1.Card * wExtendStream + // time per input tuple wo results
|
|
pRes->Card * (uExtendStream + eli->noAttrs * vExtendStream);
|
|
// time per output tuple created
|
|
|
|
// 4. progress
|
|
if ( p1.BTime < 0.1 && pipelinedProgress ) //non-blocking,
|
|
//use pipelining
|
|
pRes->Progress = p1.Progress;
|
|
else
|
|
pRes->Progress =
|
|
(p1.Progress * p1.Time +
|
|
eli->read * wExtendStream +
|
|
eli->returned * (uExtendStream + eli->noAttrs * vExtendStream) )
|
|
/ pRes->Time;
|
|
|
|
|
|
// 5. blocking time and progress
|
|
|
|
pRes->CopyBlocking(p1); //non-blocking operator
|
|
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
/*
|
|
2.19.3 Specification of operator ~projectextendstream~
|
|
|
|
*/
|
|
const string ProjectExtendStreamSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream tuple1) (ai1 ... aik) (map tuple1 "
|
|
"stream(type))) -> (stream tuple1[ai1 ... aik]*type)"
|
|
"</text--->"
|
|
"<text>_ projectextendstream [ list; funlist ]</text--->"
|
|
"<text>This operator does the same as the extendstream does,"
|
|
" projecting the result stream of tuples to some specified"
|
|
" list of attribute names.</text--->"
|
|
"<text>query UBahn feed projectextendstream"
|
|
"[ Id, Up; newattr: units(.Trajectory)] consume</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.19.4 Definition of operator ~projectextendstream~
|
|
|
|
*/
|
|
Operator extrelprojectextendstream (
|
|
"projectextendstream", // name
|
|
ProjectExtendStreamSpec, // specification
|
|
ProjectExtendStream, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
ProjectExtendStreamTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.20 Operator ~concat~
|
|
|
|
2.20.1 Type mapping function of operator ~concat~
|
|
|
|
Type mapping for ~concat~ is
|
|
|
|
---- ((stream (tuple (a1:d1 ... an:dn)))
|
|
(stream (tuple (b1:d1 ... bn:dn))))
|
|
-> (stream (tuple (a1:d1 ... an:dn)))
|
|
----
|
|
|
|
*/
|
|
ListExpr GetAttrTypeList (ListExpr l)
|
|
{
|
|
ListExpr first = nl->TheEmptyList();
|
|
ListExpr olist = first,
|
|
lastolist = first;
|
|
|
|
ListExpr attrlist = l;
|
|
while (!nl->IsEmpty(attrlist))
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
ListExpr ConcatTypeMap( ListExpr args )
|
|
{
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two streams expected");
|
|
}
|
|
if(!nl->Equal(nl->First(args),nl->Second(args))){
|
|
return listutils::typeError("both arguments must be of the same type");
|
|
}
|
|
if(!listutils::isStream(nl->First(args))){
|
|
return listutils::typeError("arguments are no streams");
|
|
}
|
|
return nl->First(args);
|
|
}
|
|
/*
|
|
2.20.2 Value mapping function of operator ~concat~
|
|
|
|
*/
|
|
int Concat(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Word t;
|
|
|
|
switch (message)
|
|
{
|
|
case OPEN :
|
|
|
|
qp->Open(args[0].addr);
|
|
qp->Open(args[1].addr);
|
|
// start using 1st stream
|
|
if(local.addr){
|
|
*(static_cast<bool*>(local.addr)) = false;
|
|
} else {
|
|
local.setAddr(new bool(false));
|
|
}
|
|
return 0;
|
|
|
|
case REQUEST :
|
|
if(!local.addr) {
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
}
|
|
if ( !(*static_cast<bool*>(local.addr)) ){
|
|
qp->Request(args[0].addr, t);
|
|
if (qp->Received(args[0].addr)){
|
|
result.setAddr(t.addr);
|
|
return YIELD;
|
|
} else {
|
|
*(static_cast<bool*>(local.addr)) = true; // start using 2nd stream
|
|
}
|
|
}
|
|
qp->Request(args[1].addr, t);
|
|
if (qp->Received(args[1].addr)){
|
|
result.setAddr(t.addr);
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
|
|
case CLOSE :
|
|
|
|
qp->Close(args[0].addr);
|
|
qp->Close(args[1].addr);
|
|
if( local.addr ) {
|
|
delete (static_cast<bool*>(local.addr));
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
2.20.3 Specification of operator ~concat~
|
|
|
|
*/
|
|
const string ConcatSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"(<text>stream(X) x stream(X) -> stream(X)</text--->"
|
|
"<text>_ _ concat</text--->"
|
|
"<text>Returns all elements of the first argument "
|
|
"followed by all elements from the second argument."
|
|
"</text--->"
|
|
"<text>query ten feed five feed concat consume"
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.20.4 Definition of operator ~concat~
|
|
|
|
*/
|
|
Operator extrelconcat (
|
|
"concat", // name
|
|
ConcatSpec, // specification
|
|
Concat, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
ConcatTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.21 Operator ~groupby~
|
|
|
|
2.21.1 Type mapping function of operator ~groupby~
|
|
|
|
Result type of ~groupby~ operation.
|
|
|
|
---- Let X = tuple ((x1 t1) ... (xn tn)), R = rel(X):
|
|
|
|
( (stream X) (xi1 ... xik) ( (y1 (map R T1)) ... (ym (map R Tm)) ) )
|
|
|
|
-> ( APPEND (m p1 ... pm)
|
|
(stream (tuple (xj1 tj1)... (xjl tjl) (y1 T1) ... (ym Tm))))
|
|
|
|
with tj,Ti in kind DATA, xi <> xj and k+l=n, pi <> pj and 1 <= pi <= m.
|
|
This means attributes xi ... xik are removed from the stream and
|
|
attributes y1 ... ym are appended. These new attributes represent
|
|
aggregated values computed by maps of R -> Ti which must have a
|
|
result type of kind DATA.
|
|
----
|
|
|
|
*/
|
|
ListExpr GroupByTypeMap(ListExpr args)
|
|
{
|
|
|
|
string err = "stream(tuple(X)) x attrlist x funlist expected";
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError(err + " ( wrong number of args");
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr groupList = nl->Second(args);
|
|
ListExpr funList = nl->Third(args);
|
|
|
|
if(!Stream<Tuple>::checkType(stream) ||
|
|
(nl->AtomType(groupList) != NoAtom) ||
|
|
(nl->AtomType(funList) != NoAtom)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
|
|
//
|
|
set<string> usedNames;
|
|
|
|
ListExpr indexList = nl->TheEmptyList();
|
|
ListExpr indexLast = nl->TheEmptyList();
|
|
ListExpr resAttrList = nl->TheEmptyList();
|
|
ListExpr resAttrLast = nl->TheEmptyList();
|
|
bool first = true;
|
|
|
|
// process group list
|
|
|
|
while(!nl->IsEmpty(groupList)){
|
|
ListExpr attrNameList = nl->First(groupList);
|
|
groupList = nl->Rest(groupList);
|
|
if(!listutils::isSymbol(attrNameList)){
|
|
return listutils::typeError(" invalid attribute name: "
|
|
+ nl->ToString(attrNameList));
|
|
}
|
|
string attrName = nl->SymbolValue(attrNameList);
|
|
if(usedNames.find(attrName)!=usedNames.end()){
|
|
return listutils::typeError("Attribute " + attrName + " used twice");
|
|
}
|
|
usedNames.insert(attrName);
|
|
ListExpr type;
|
|
int index = listutils::findAttribute(attrList, attrName, type);
|
|
if(!index){
|
|
return listutils::typeError("attribute " + attrName
|
|
+ " not part of tuple");
|
|
}
|
|
ListExpr newAttr = nl->TwoElemList(attrNameList, type);
|
|
if(first){
|
|
indexList = nl->OneElemList(nl->IntAtom(index));
|
|
indexLast = indexList;
|
|
resAttrList = nl->OneElemList(newAttr);
|
|
resAttrLast = resAttrList;
|
|
first = false;
|
|
} else {
|
|
indexLast = nl->Append(indexLast, nl->IntAtom(index));
|
|
resAttrLast = nl->Append(resAttrLast, newAttr);
|
|
}
|
|
}
|
|
|
|
// process funlist
|
|
ListExpr tupleType = nl->Second(stream);
|
|
ListExpr relType = nl->TwoElemList(
|
|
listutils::basicSymbol<Relation>(),
|
|
tupleType);
|
|
|
|
while(!nl->IsEmpty(funList)) {
|
|
ListExpr fun = nl->First(funList);
|
|
funList = nl->Rest(funList);
|
|
if(!nl->HasLength(fun,2)){ // (name map)
|
|
return listutils::typeError("invalid function definition "
|
|
"found (missing name or map)");
|
|
}
|
|
// check attribute name
|
|
ListExpr attrNameList = nl->First(fun);
|
|
if(!listutils::isValidAttributeName(attrNameList,err)){
|
|
return listutils::typeError("Attribute name "
|
|
+ nl->ToString(attrNameList)
|
|
+ " is not valid:" + err);
|
|
}
|
|
string attrname = nl->SymbolValue(attrNameList);
|
|
if(usedNames.find(attrname)!=usedNames.end()){
|
|
return listutils::typeError("Attribute " + attrname + " used twice");
|
|
}
|
|
usedNames.insert(attrname);
|
|
// check map
|
|
ListExpr fundef = nl->Second(fun);
|
|
if(!listutils::isMap<1>(fundef)){
|
|
return listutils::typeError(nl->ToString(fundef)
|
|
+ " is not a valid function definition");
|
|
}
|
|
// check argument
|
|
if(!nl->Equal(relType, nl->Second(fundef))){
|
|
return listutils::typeError("invalid argument type for attribute "
|
|
+ attrname + " expected " + nl->ToString(relType)
|
|
+ " but got " + nl->ToString(nl->Second(fundef) ));
|
|
}
|
|
// check result
|
|
if(!Attribute::checkType(nl->Third(fundef))){
|
|
return listutils::typeError("result of function for " + attrname
|
|
+ " is not an attribute");
|
|
}
|
|
ListExpr newAttr = nl->TwoElemList(attrNameList, nl->Third(fundef));
|
|
if(first){
|
|
indexList = nl->TheEmptyList();
|
|
resAttrList = nl->OneElemList(newAttr);
|
|
resAttrLast = resAttrList;
|
|
first = false;
|
|
} else {
|
|
resAttrLast = nl->Append(resAttrLast, newAttr);
|
|
}
|
|
}
|
|
// create result list
|
|
ListExpr resList = nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Tuple>(),
|
|
resAttrList));
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->Cons(nl->IntAtom(nl->ListLength(indexList)), indexList),
|
|
resList);
|
|
}
|
|
|
|
|
|
/*
|
|
2.22 Operator ~slidingwindow~
|
|
|
|
2.21.1 Type mapping function of operator ~slidingwindow~
|
|
|
|
Result type of ~slidingwindow~ operation.
|
|
|
|
---- Let X = tuple ((x1 t1) ... (xn tn)), R = rel(X):
|
|
|
|
( (stream X) (xi1 ... xik) ( (y1 (map R T1)) ... (ym (map R Tm)) ) )
|
|
|
|
-> ( APPEND (m p1 ... pm)
|
|
(stream (tuple (xj1 tj1)... (xjl tjl) (y1 T1) ... (ym Tm))))
|
|
|
|
with tj,Ti in kind DATA, xi <> xj and k+l=n, pi <> pj and 1 <= pi <= m.
|
|
This means attributes xi ... xik are removed from the stream and
|
|
attributes y1 ... ym are appended. These new attributes represent
|
|
aggregated values computed by maps of R -> Ti which must have a
|
|
result type of kind DATA.
|
|
----
|
|
|
|
*/
|
|
ListExpr SlidingWindowTypeMap(ListExpr args)
|
|
{
|
|
bool debugme= true;
|
|
ListExpr first, second, third, fourth; // list used for analysing input
|
|
ListExpr listn, lastlistn, listp; // list used for constructing output
|
|
|
|
first = second = third = nl->TheEmptyList();
|
|
listn = lastlistn = listp = nl->TheEmptyList();
|
|
|
|
string tupleSymbolStr = Tuple::BasicType();
|
|
|
|
bool listOk = true;
|
|
listOk = listOk && ( nl->ListLength(args) == 4 );
|
|
|
|
if ( listOk ) {
|
|
first = nl->First(args);
|
|
second = nl->Second(args);
|
|
third = nl->Third(args);
|
|
fourth= nl->Fourth(args);
|
|
|
|
// check input list structure
|
|
listOk = listOk && (nl->ListLength(first) == 2);
|
|
listOk = listOk && !nl->IsEmpty( fourth );
|
|
}
|
|
|
|
if( !listOk )
|
|
{
|
|
stringstream errMsg;
|
|
errMsg << "slidingwindow: Invalid input list structure. "
|
|
<< "The structure should be a four elem list "
|
|
<< "like (stream (" << tupleSymbolStr
|
|
<< "((x1 t1) ... (xn tn)) int int "
|
|
<< "( (y1 (map R T1)) ... (ym (map R Tm))!";
|
|
|
|
ErrorReporter::ReportError(errMsg.str());
|
|
return nl->SymbolAtom(Symbol::TYPEERROR());
|
|
}
|
|
|
|
|
|
// check for tuple stream
|
|
listOk = listOk && Stream<Tuple>::checkType(first);
|
|
|
|
if ( !listOk ) {
|
|
|
|
ErrorReporter::ReportError( "slidingwindow: Input is not of type (stream "
|
|
+ tupleSymbolStr + "(...))." );
|
|
return nl->SymbolAtom(Symbol::TYPEERROR());
|
|
}
|
|
|
|
// list seems to be ok. Check the second and the third arguments
|
|
if(! nl->IsEqual(second, CcInt::BasicType()) )
|
|
{
|
|
ErrorReporter::ReportError(
|
|
"slidingwindow: expected int as a second argument");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
if(! nl->IsEqual(third, CcInt::BasicType()) )
|
|
{
|
|
ErrorReporter::ReportError(
|
|
"slidingwindow: expected int as a third argument");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
// Check the last argument
|
|
ListExpr rest = fourth;
|
|
|
|
if(nl->IsAtom(rest)){
|
|
return listutils::typeError("fourth arg must be a list");
|
|
}
|
|
|
|
while (!(nl->IsEmpty(rest))) // check functions y1 .. ym
|
|
{
|
|
// iterate over elements of the 3rd input list
|
|
ListExpr firstr = nl->First(rest);
|
|
rest = nl->Rest(rest);
|
|
|
|
ListExpr newAttr = nl->First(firstr);
|
|
ListExpr mapDef = nl->Second(firstr);
|
|
ListExpr mapOut = nl->Third(mapDef);
|
|
|
|
// check list structure
|
|
bool listOk = true;
|
|
listOk = listOk && ( nl->IsAtom(newAttr) );
|
|
listOk = listOk && ( nl->ListLength(mapDef) == 3 );
|
|
listOk = listOk && ( nl->AtomType(newAttr) == SymbolType );
|
|
listOk = listOk && ( listutils::isAnyMap(mapDef));
|
|
// listOk = listOk && ( nl->Equal(groupType, nl->Second(mapDef)) );
|
|
|
|
if( !listOk )
|
|
// Todo: there could be more fine grained error messages
|
|
{
|
|
ErrorReporter::ReportError(
|
|
"slidingwindow: Function definition is not correct!");
|
|
return nl->SymbolAtom(Symbol::TYPEERROR());
|
|
}
|
|
|
|
// check if the Type Constructor belongs to KIND DATA
|
|
// If the functions result type is typeerror this check will also fail
|
|
ListExpr errorInfo = nl->OneElemList(nl->SymbolAtom("ErrorInfo"));
|
|
if ( !am->CheckKind(Kind::DATA(), mapOut, errorInfo) ) {
|
|
|
|
stringstream errMsg;
|
|
errMsg << "slidingwindow: The aggregate function for attribute \""
|
|
<< nl->SymbolValue(newAttr) << "\""
|
|
<< " returns a type which is not usable in tuples."
|
|
<< " The type constructor \""
|
|
<< nl->ToString(mapOut) << "\""
|
|
<< " belongs not to kind DATA!"
|
|
<< ends;
|
|
|
|
ErrorReporter::ReportError(errMsg.str());
|
|
return nl->SymbolAtom(Symbol::TYPEERROR());
|
|
|
|
}
|
|
|
|
if ( (nl->EndOfList( lastlistn ) == true)
|
|
&& (nl->IsEmpty( lastlistn ) == false)
|
|
&& (nl->IsAtom( lastlistn ) == false)
|
|
)
|
|
{ // list already contains group-attributes (not empty)
|
|
lastlistn = nl->Append(lastlistn,(nl->TwoElemList(newAttr,mapOut)));
|
|
}
|
|
else
|
|
{ // no group attribute (list is still empty)
|
|
listn = nl->OneElemList(nl->TwoElemList(newAttr,mapOut));
|
|
lastlistn = listn;
|
|
}
|
|
} // end of while check functions
|
|
|
|
if ( !CompareNames(listn) )
|
|
{ // check if attribute names are unique
|
|
ErrorReporter::ReportError("slidingwindow: Attribute names are not unique");
|
|
return nl->SymbolAtom(Symbol::TYPEERROR());
|
|
}
|
|
|
|
// Type mapping is correct, return result type.
|
|
ListExpr groupType = nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()), listn) );
|
|
|
|
if(debugme)
|
|
{
|
|
string resstring;
|
|
nl->WriteToString(resstring, groupType);
|
|
cerr << "groupbyTypeMap: result = " << resstring << endl;
|
|
}
|
|
return groupType;
|
|
}
|
|
|
|
|
|
/*
|
|
2.21.2 Value mapping function of operator ~groupby~
|
|
|
|
*/
|
|
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
|
|
struct GroupByLocalInfo
|
|
{
|
|
Tuple *t;
|
|
TupleType *resultTupleType;
|
|
long MAX_MEMORY;
|
|
GroupByLocalInfo() : t(0), resultTupleType(0), MAX_MEMORY(0) {}
|
|
};
|
|
|
|
int GroupByValueMapping
|
|
(Word* args, Word& result, int message, Word& local, Supplier supplier)
|
|
{
|
|
Tuple *s = 0;
|
|
Word sWord(Address(0));
|
|
TupleBuffer* tp = 0;
|
|
GenericRelationIterator* relIter = 0;
|
|
int i = 0, j = 0, k = 0;
|
|
int numberatt = 0;
|
|
bool ifequal = false;
|
|
Word value(Address(0));
|
|
Supplier value2;
|
|
Supplier supplier1;
|
|
Supplier supplier2;
|
|
int noOffun = 0;
|
|
ArgVectorPointer vector;
|
|
const int indexOfCountArgument = 3;
|
|
const int startIndexOfExtraArguments = indexOfCountArgument +1;
|
|
int attribIdx = 0;
|
|
GroupByLocalInfo *gbli = 0;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
|
|
// The argument vector contains the following values:
|
|
// args[0] = stream of tuples
|
|
// args[1] = list of identifiers
|
|
// args[2] = list of functions
|
|
// args[3] = Number of extra arguments
|
|
// args[4 ...] = args added by APPEND
|
|
|
|
switch(message)
|
|
{
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN:
|
|
{
|
|
// Get the first tuple pointer and store it in the
|
|
// GroupBylocalInfo structure
|
|
qp->Open (args[0].addr);
|
|
qp->Request(args[0].addr, sWord);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
gbli = new GroupByLocalInfo();
|
|
gbli->t = (Tuple*)sWord.addr;
|
|
gbli->t->IncReference();
|
|
tt->IncReference();
|
|
gbli->resultTupleType = tt;
|
|
gbli->MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024);
|
|
local.setAddr(gbli);
|
|
|
|
cmsg.info("ERA:ShowMemInfo")
|
|
<< "GroupBy.MAX_MEMORY ("
|
|
<< gbli->MAX_MEMORY / 1024 << " MB): = "
|
|
<< gbli->MAX_MEMORY / gbli->t->GetExtSize()
|
|
<< " Tuples" << endl;
|
|
cmsg.send();
|
|
}
|
|
else
|
|
{
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
{
|
|
Counter::getRef("GroupBy:Request")++;
|
|
if(local.addr == 0)
|
|
{
|
|
return CANCEL;
|
|
}
|
|
gbli = (GroupByLocalInfo *)local.addr;
|
|
if( gbli->t == 0 ) { // Stream ends
|
|
return CANCEL;
|
|
}
|
|
tp = new TupleBuffer(gbli->MAX_MEMORY);
|
|
tp->AppendTuple(gbli->t);
|
|
|
|
|
|
// get number of attributes
|
|
numberatt = ((CcInt*)args[indexOfCountArgument].addr)->GetIntval();
|
|
|
|
ifequal = true;
|
|
// Get next tuple
|
|
qp->Request(args[0].addr, sWord);
|
|
while ((qp->Received(args[0].addr)) && ifequal)
|
|
{
|
|
s = (Tuple*)sWord.addr;
|
|
for (k = 0; k < numberatt; k++) // check if tuples t = s
|
|
{
|
|
// loop over all grouping attributes
|
|
attribIdx =
|
|
((CcInt*)args[startIndexOfExtraArguments+k].addr)->GetIntval();
|
|
j = attribIdx - 1;
|
|
if (((Attribute*)gbli->t->GetAttribute(j))->
|
|
Compare((Attribute *)s->GetAttribute(j)))
|
|
{
|
|
ifequal = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ifequal) // store in tuple buffer
|
|
{
|
|
tp->AppendTuple(s);
|
|
s->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, sWord);
|
|
// get next tuple
|
|
}
|
|
else
|
|
{
|
|
// store tuple pointer in local info
|
|
gbli->t->DecReference();
|
|
gbli->t->DeleteIfAllowed();
|
|
gbli->t = s;
|
|
gbli->t->IncReference();
|
|
}
|
|
}
|
|
if (ifequal)
|
|
// last group finished, stream ends
|
|
{
|
|
gbli->t->DecReference();
|
|
gbli->t->DeleteIfAllowed();
|
|
gbli->t = 0;
|
|
}
|
|
|
|
// create result tuple
|
|
Tuple *t = new Tuple( gbli->resultTupleType );
|
|
relIter = tp->MakeScan();
|
|
s = relIter->GetNextTuple();
|
|
|
|
// copy in grouping attributes
|
|
for(i = 0; i < numberatt; i++)
|
|
{
|
|
attribIdx =
|
|
((CcInt*)args[startIndexOfExtraArguments+i].addr)->GetIntval();
|
|
t->CopyAttribute(attribIdx - 1, s, i);
|
|
}
|
|
s->DeleteIfAllowed();
|
|
value2 = (Supplier)args[2].addr; // list of functions
|
|
noOffun = qp->GetNoSons(value2);
|
|
delete relIter;
|
|
|
|
for(i = 0; i < noOffun; i++)
|
|
{
|
|
// prepare arguments for function i
|
|
supplier1 = qp->GetSupplier(value2, i);
|
|
supplier2 = qp->GetSupplier(supplier1, 1);
|
|
vector = qp->Argument(supplier2);
|
|
// The group was stored in a relation identified by symbol group
|
|
// which is a typemap operator. Here it is stored in the
|
|
// argument vector
|
|
((*vector)[0]).setAddr(tp);
|
|
|
|
// compute value of function i and put it into the result tuple
|
|
qp->Request(supplier2, value);
|
|
t->PutAttribute(numberatt + i, (Attribute*)value.addr);
|
|
qp->ReInitResultStorage(supplier2);
|
|
}
|
|
result.setAddr(t);
|
|
delete tp;
|
|
return YIELD;
|
|
}
|
|
case CLOSE:
|
|
{
|
|
if( local.addr != 0 )
|
|
{
|
|
gbli = (GroupByLocalInfo *)local.addr;
|
|
if( gbli->resultTupleType != 0 )
|
|
gbli->resultTupleType->DeleteIfAllowed();
|
|
if( gbli->t != 0 )
|
|
{
|
|
gbli->t->DecReference();
|
|
gbli->t->DeleteIfAllowed();
|
|
}
|
|
delete gbli;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
//progress version
|
|
|
|
|
|
class GroupByLocalInfo: public ProgressLocalInfo
|
|
{
|
|
public:
|
|
Tuple *t;
|
|
TupleType *resultTupleType;
|
|
long MAX_MEMORY;
|
|
|
|
//new for progress
|
|
|
|
int stableValue;
|
|
bool sizesFinal;
|
|
|
|
int noGroupAttrs;
|
|
int noAggrAttrs;
|
|
double *attrSizeTmp;
|
|
double *attrSizeExtTmp;
|
|
|
|
// initialization
|
|
GroupByLocalInfo() : ProgressLocalInfo(),
|
|
t(0), resultTupleType(0), MAX_MEMORY(0),
|
|
stableValue(0),sizesFinal(false),
|
|
noGroupAttrs(0), noAggrAttrs(0),
|
|
attrSizeTmp(0), attrSizeExtTmp(0)
|
|
{}
|
|
|
|
~GroupByLocalInfo(){
|
|
if(t){
|
|
t->DeleteIfAllowed();
|
|
t = 0;
|
|
}
|
|
if(resultTupleType){
|
|
resultTupleType->DeleteIfAllowed();
|
|
resultTupleType = 0;
|
|
}
|
|
if(attrSizeTmp){
|
|
delete[] attrSizeTmp;
|
|
attrSizeTmp=0;
|
|
}
|
|
if(attrSizeExtTmp){
|
|
delete[] attrSizeExtTmp;
|
|
attrSizeExtTmp = 0;
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
int GroupByValueMapping
|
|
(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Tuple *ss = 0;
|
|
Word sWord(Address(0));
|
|
TupleBuffer* tp = 0;
|
|
GenericRelationIterator* relIter = 0;
|
|
int i = 0, j = 0, k = 0;
|
|
int numberatt = 0;
|
|
bool ifequal = false;
|
|
Word value(Address(0));
|
|
Supplier value2;
|
|
Supplier supplier1;
|
|
Supplier supplier2;
|
|
int noOffun = 0;
|
|
ArgVectorPointer vector;
|
|
const int indexOfCountArgument = 3;
|
|
const int startIndexOfExtraArguments = indexOfCountArgument +1;
|
|
int attribIdx = 0;
|
|
GroupByLocalInfo *gbli = (GroupByLocalInfo *)local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
|
|
// The argument vector contains the following values:
|
|
// args[0] = stream of tuples
|
|
// args[1] = list of identifiers
|
|
// args[2] = list of functions
|
|
// args[3] = Number of extra arguments
|
|
// args[4 ...] = args added by APPEND
|
|
|
|
switch(message)
|
|
{
|
|
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN:
|
|
{
|
|
|
|
if (gbli) {
|
|
delete gbli;
|
|
}
|
|
gbli = new GroupByLocalInfo();
|
|
gbli->stableValue = 5;
|
|
gbli->sizesFinal = false;
|
|
|
|
numberatt = ((CcInt*)args[indexOfCountArgument].addr)->GetIntval();
|
|
value2 = (Supplier)args[2].addr; // list of functions
|
|
noOffun = qp->GetNoSons(value2);
|
|
|
|
gbli->noAttrs = numberatt + noOffun;
|
|
gbli->noGroupAttrs = numberatt;
|
|
gbli->noAggrAttrs = noOffun;
|
|
gbli->attrSizeTmp = new double[gbli->noAttrs];
|
|
gbli->attrSizeExtTmp = new double[gbli->noAttrs];
|
|
|
|
for (int i = 0; i < gbli->noAttrs; i++)
|
|
{
|
|
gbli->attrSizeTmp[i] = 0.0;
|
|
gbli->attrSizeExtTmp[i] = 0.0;
|
|
}
|
|
|
|
local.setAddr(gbli); //from now, progress queries possible
|
|
|
|
// Get the first tuple pointer and store it in the
|
|
// GroupBylocalInfo structure
|
|
qp->Open (args[0].addr);
|
|
qp->Request(args[0].addr, sWord);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
gbli->read++;
|
|
gbli->t = (Tuple*)sWord.addr;
|
|
//gbli->t->IncReference();
|
|
tt->IncReference();
|
|
gbli->resultTupleType = tt;
|
|
gbli->MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024);
|
|
|
|
|
|
cmsg.info("ERA:ShowMemInfo")
|
|
<< "GroupBy.MAX_MEMORY ("
|
|
<< gbli->MAX_MEMORY / 1024 << " MB): = "
|
|
<< gbli->MAX_MEMORY / gbli->t->GetExtSize()
|
|
<< " Tuples" << endl;
|
|
cmsg.send();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
{
|
|
Counter::getRef("GroupBy:Request")++;
|
|
if(!gbli){
|
|
return CANCEL;
|
|
}
|
|
|
|
if(gbli->t == 0){
|
|
return CANCEL;
|
|
} else {
|
|
tp = new TupleBuffer(gbli->MAX_MEMORY);
|
|
tp->AppendTuple(gbli->t);
|
|
}
|
|
|
|
// get number of attributes
|
|
numberatt = ((CcInt*)args[indexOfCountArgument].addr)->GetIntval();
|
|
|
|
ifequal = true;
|
|
// Get next tuple
|
|
qp->Request(args[0].addr, sWord);
|
|
while ((qp->Received(args[0].addr)) && ifequal)
|
|
{
|
|
gbli->read++;
|
|
ss = (Tuple*)sWord.addr;
|
|
for (k = 0; k < numberatt; k++) // check if tuples t = s
|
|
{
|
|
// loop over all grouping attributes
|
|
attribIdx =
|
|
((CcInt*)args[startIndexOfExtraArguments+k].addr)->GetIntval();
|
|
j = attribIdx - 1;
|
|
if (((Attribute*)gbli->t->GetAttribute(j))->
|
|
Compare((Attribute *)ss->GetAttribute(j)))
|
|
{
|
|
ifequal = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ifequal) // store in tuple buffer
|
|
{
|
|
tp->AppendTuple(ss);
|
|
ss->DeleteIfAllowed();
|
|
qp->Request(args[0].addr, sWord);
|
|
// get next tuple
|
|
}
|
|
else
|
|
{
|
|
// store tuple pointer in local info
|
|
gbli->t->DeleteIfAllowed();
|
|
gbli->t = ss;
|
|
//gbli->t->IncReference();
|
|
}
|
|
}
|
|
if (ifequal)
|
|
// last group finished, stream ends
|
|
{
|
|
gbli->t->DeleteIfAllowed();
|
|
gbli->t = 0;
|
|
}
|
|
|
|
// create result tuple
|
|
Tuple *t = new Tuple( gbli->resultTupleType );
|
|
relIter = tp->MakeScan();
|
|
ss = relIter->GetNextTuple();
|
|
|
|
// copy in grouping attributes
|
|
for(i = 0; i < numberatt; i++)
|
|
{
|
|
attribIdx =
|
|
((CcInt*)args[startIndexOfExtraArguments+i].addr)->GetIntval();
|
|
t->CopyAttribute(attribIdx - 1, ss, i);
|
|
}
|
|
ss->DeleteIfAllowed();
|
|
value2 = (Supplier)args[2].addr; // list of functions
|
|
noOffun = qp->GetNoSons(value2);
|
|
delete relIter;
|
|
|
|
for(i = 0; i < noOffun; i++)
|
|
{
|
|
// prepare arguments for function i
|
|
supplier1 = qp->GetSupplier(value2, i);
|
|
supplier2 = qp->GetSupplier(supplier1, 1);
|
|
vector = qp->Argument(supplier2);
|
|
// The group was stored in a relation identified by symbol group
|
|
// which is a typemap operator. Here it is stored in the
|
|
// argument vector
|
|
((*vector)[0]) = SetWord(tp);
|
|
|
|
// compute value of function i and put it into the result tuple
|
|
qp->Request(supplier2, value);
|
|
t->PutAttribute(numberatt + i, (Attribute*)value.addr);
|
|
qp->ReInitResultStorage(supplier2);
|
|
}
|
|
|
|
if ( gbli->returned < gbli->stableValue )
|
|
{
|
|
for (int i = 0; i < gbli->noAttrs; i++)
|
|
{
|
|
gbli->attrSizeTmp[i] += t->GetSize(i);
|
|
gbli->attrSizeExtTmp[i] += t->GetExtSize(i);
|
|
}
|
|
}
|
|
|
|
gbli->returned++;
|
|
result.setAddr(t);
|
|
if(tp){
|
|
delete tp;
|
|
}
|
|
tp = 0;
|
|
return YIELD;
|
|
}
|
|
case CLOSE:
|
|
{
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
case CLOSEPROGRESS:
|
|
if (gbli)
|
|
{
|
|
delete gbli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
|
|
case REQUESTPROGRESS:
|
|
|
|
ProgressInfo p1;
|
|
ProgressInfo *pRes;
|
|
const double uGroup = 0.013; //millisecs per tuple and new attribute
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
|
|
if ( !gbli ){
|
|
return CANCEL;
|
|
}
|
|
|
|
if ( qp->RequestProgress(args[0].addr, &p1) )
|
|
{
|
|
gbli->sizesChanged = false;
|
|
|
|
if ( !gbli->sizesInitialized )
|
|
{
|
|
gbli->attrSize = new double[gbli->noAttrs];
|
|
gbli->attrSizeExt = new double[gbli->noAttrs];
|
|
}
|
|
|
|
if ( !gbli->sizesInitialized || p1.sizesChanged ||
|
|
( gbli->returned >= gbli->stableValue && !gbli->sizesFinal ) )
|
|
{
|
|
|
|
if ( gbli->returned < gbli->stableValue )
|
|
{
|
|
for (int i = 0; i < gbli->noGroupAttrs; i++)
|
|
{
|
|
gbli->attrSize[i] = 56; //guessing string attributes
|
|
gbli->attrSizeExt[i] = 56;
|
|
}
|
|
for (int j = 0; j < gbli->noAggrAttrs; j++)
|
|
{ //guessing int attributes
|
|
gbli->attrSize[j + gbli->noGroupAttrs] = 12;
|
|
gbli->attrSizeExt[j + gbli->noGroupAttrs] = 12;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < gbli->noAttrs; i++)
|
|
{
|
|
gbli->attrSize[i] = gbli->attrSizeTmp[i] / gbli->stableValue;
|
|
gbli->attrSizeExt[i] = gbli->attrSizeExtTmp[i] /
|
|
gbli->stableValue;
|
|
}
|
|
}
|
|
gbli->Size = 0.0;
|
|
gbli->SizeExt = 0.0;
|
|
for (int i = 0; i < gbli->noAttrs; i++)
|
|
{
|
|
gbli->Size += gbli->attrSize[i];
|
|
gbli->SizeExt += gbli->attrSizeExt[i];
|
|
}
|
|
gbli->sizesInitialized = true;
|
|
gbli->sizesChanged = true;
|
|
}
|
|
if ( gbli->returned >= gbli->stableValue ) gbli->sizesFinal = true;
|
|
|
|
|
|
|
|
//As long as we have not seen 5 result tuples (groups), we guess groups
|
|
//of size 10
|
|
|
|
pRes->Card = (gbli->returned < 5 ? p1.Card/10.0 :
|
|
p1.Card * (double) gbli->returned / (double) gbli->read);
|
|
|
|
pRes->CopySizes(gbli);
|
|
|
|
pRes->Time = p1.Time + p1.Card * gbli->noAggrAttrs * uGroup;
|
|
|
|
pRes->Progress = (p1.Progress * p1.Time
|
|
+ gbli->read * gbli->noAggrAttrs * uGroup)
|
|
/ pRes->Time;
|
|
|
|
pRes->CopyBlocking(p1); //non-blocking operator
|
|
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct SlidingWindowLocalInfo
|
|
{
|
|
TupleType *resultTupleType;
|
|
bool firstWindow;
|
|
CircularTupleBuffer* tb;
|
|
|
|
SlidingWindowLocalInfo() : resultTupleType(0), firstWindow(true), tb(0) {}
|
|
};
|
|
|
|
int SlidingWindowValueMapping
|
|
(Word* args, Word& result, int message, Word& local, Supplier supplier)
|
|
{
|
|
bool debugme= false;
|
|
if(debugme)
|
|
qp->ListOfTree(supplier, cerr);
|
|
Tuple *s = 0;
|
|
Word sWord(Address(0));
|
|
Word value(Address(0));
|
|
Supplier value2;
|
|
Supplier supplier1;
|
|
Supplier supplier2;
|
|
int noOffun = 0;
|
|
ArgVectorPointer vector;
|
|
SlidingWindowLocalInfo *gbli = 0;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(supplier).addr;
|
|
|
|
// The argument vector contains the following values:
|
|
// args[0] = stream of tuples
|
|
// args[1] = list of identifiers
|
|
// args[2] = list of functions
|
|
// args[3] = Number of extra arguments
|
|
// args[4 ...] = args added by APPEND
|
|
|
|
switch(message)
|
|
{
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(supplier)));
|
|
qp->GetLocal2(supplier).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH : {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(supplier).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN:
|
|
{
|
|
// Get the first tuple pointer and store it in the
|
|
// GroupBylocalInfo structure
|
|
gbli = new SlidingWindowLocalInfo();
|
|
tt->IncReference();
|
|
gbli->resultTupleType = tt;
|
|
gbli->firstWindow= true;
|
|
local.setAddr(gbli);
|
|
qp->Open (args[0].addr);
|
|
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
{
|
|
int windowSize, stepSize;
|
|
windowSize = static_cast<CcInt*>(args[1].addr)->GetIntval();
|
|
stepSize = static_cast<CcInt*>(args[2].addr)->GetIntval();
|
|
|
|
if(local.addr == 0)
|
|
{
|
|
return CANCEL;
|
|
}
|
|
gbli = (SlidingWindowLocalInfo *)local.addr;
|
|
|
|
int i= 0;
|
|
if(gbli->firstWindow)
|
|
{
|
|
gbli->firstWindow= false;
|
|
gbli->tb= new CircularTupleBuffer(
|
|
true, gbli->resultTupleType, windowSize);
|
|
qp->Request(args[0].addr, sWord);
|
|
while (i++ < windowSize && qp->Received(args[0].addr))
|
|
{
|
|
s= static_cast<Tuple*>(sWord.addr);
|
|
gbli->tb->AppendTuple(s);
|
|
if(i < windowSize){
|
|
qp->Request(args[0].addr, sWord);
|
|
}
|
|
s->DeleteIfAllowed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qp->Request(args[0].addr, sWord);
|
|
while (i++ < stepSize && qp->Received(args[0].addr))
|
|
{
|
|
s= static_cast<Tuple*>(sWord.addr);
|
|
gbli->tb->AppendTuple(s);
|
|
if(i < stepSize){
|
|
qp->Request(args[0].addr, sWord);
|
|
}
|
|
s->DeleteIfAllowed();
|
|
}
|
|
}
|
|
if(i == 1) //Steam end
|
|
return CANCEL;
|
|
|
|
// create result tuple
|
|
Tuple *t = new Tuple( gbli->resultTupleType );
|
|
value2 = (Supplier)args[3].addr; // list of functions
|
|
noOffun = qp->GetNoSons(value2);
|
|
|
|
for(i = 0; i < noOffun; i++)
|
|
{
|
|
// prepare arguments for function i
|
|
supplier1 = qp->GetSupplier(value2, i);
|
|
supplier2 = qp->GetSupplier(supplier1, 1);
|
|
vector = qp->Argument(supplier2);
|
|
// The group was stored in a relation identified by symbol group
|
|
// which is a typemap operator. Here it is stored in the
|
|
// argument vector
|
|
((*vector)[0]).setAddr(gbli->tb);
|
|
|
|
// compute value of function i and put it into the result tuple
|
|
qp->Request(supplier2, value);
|
|
t->PutAttribute(i, (Attribute*)value.addr);
|
|
qp->ReInitResultStorage(supplier2);
|
|
}
|
|
result.setAddr(t);
|
|
return YIELD;
|
|
}
|
|
case CLOSE:
|
|
{
|
|
if( local.addr != 0 )
|
|
{
|
|
gbli = (SlidingWindowLocalInfo *)local.addr;
|
|
if( gbli->resultTupleType != 0 )
|
|
gbli->resultTupleType->DeleteIfAllowed();
|
|
if( gbli->tb != 0 )
|
|
{
|
|
gbli->tb->Clear();
|
|
delete gbli->tb;
|
|
}
|
|
delete gbli;
|
|
local.setAddr(0);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
2.21.3 Specification of operator ~groupby~
|
|
|
|
*/
|
|
const string GroupBySpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple (a1:d1 ... an:dn))) "
|
|
"(ai1 ... aik) ((bj1 (fun (rel (tuple (a1:d1"
|
|
" ... an:dn))) (_))) ... (bjl (fun (rel (tuple"
|
|
" (a1:d1 ... an:dn))) (_))))) -> (stream (tuple"
|
|
" (ai1:di1 ... aik:dik bj1 ... bjl)))</text--->"
|
|
"<text>_ groupby [list; funlist]</text--->"
|
|
"<text>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. The empty "
|
|
"list is allowed for the grouping attributes (this "
|
|
"results in a single group with all input tuples)."
|
|
"</text--->"
|
|
"<text>query Employee feed sortby[DeptNr asc] "
|
|
"groupby[DeptNr; anz : group feed count] consume"
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.21.6 Specification of operator ~slidingwindow~
|
|
|
|
*/
|
|
const string SlidingWindowSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple (a1:d1 ... an:dn))) "
|
|
"int int ((bj1 (fun (rel (tuple (a1:d1"
|
|
" ... an:dn))) (_))) ... (bjl (fun (rel (tuple"
|
|
" (a1:d1 ... an:dn))) (_))))) -> (stream (tuple"
|
|
" (bj1 ... bjl)))</text--->"
|
|
"<text>_ slidingwindow [int, int; funlist]"
|
|
"</text--->"
|
|
"<text>Apply a sliding window on a stream of tuples"
|
|
" grouping together the tuples within the window."
|
|
" The window size and the step size are arguments."
|
|
" The groups are fed to the functions. The result"
|
|
" tuples are constructed from the results of those"
|
|
" functions. If the stream has tuples less than the"
|
|
" window size, these tuples are considered one "
|
|
" group.</text--->"
|
|
"<text>query Employee feed sortby[DeptNr asc] "
|
|
"groupby[DeptNr; anz : group feed count] consume"
|
|
"</text--->"
|
|
") )";
|
|
|
|
|
|
/*
|
|
2.21.4 Definition of operator ~groupby~
|
|
|
|
*/
|
|
Operator extrelgroupby (
|
|
"groupby", // name
|
|
GroupBySpec, // specification
|
|
GroupByValueMapping, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
GroupByTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.21.5 Definition of operator ~slidingwindow~
|
|
|
|
*/
|
|
Operator extrelslidingwindow (
|
|
"slidingwindow", // name
|
|
SlidingWindowSpec, // specification
|
|
SlidingWindowValueMapping, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SlidingWindowTypeMap // type mapping
|
|
);
|
|
|
|
|
|
/*
|
|
2.22 Operator ~aggregate~
|
|
|
|
2.22.1 Type mapping function of operator ~aggregate~
|
|
|
|
Type mapping for ~aggregate~ is
|
|
|
|
---- ((stream (tuple ((x1 T1) ... (xn Tn))))
|
|
xi (map Ti Ti Tx) Tx ->
|
|
|
|
(APPEND i Tx)
|
|
----
|
|
|
|
*/
|
|
ListExpr AggregateTypeMap( ListExpr args )
|
|
{
|
|
if(nl->ListLength(args)!=4){
|
|
return listutils::typeError("4 arguments expected");
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr attrNameList = nl->Second(args);
|
|
ListExpr map = nl->Third(args);
|
|
ListExpr defaultValue = nl->Fourth(args);
|
|
|
|
if(!listutils::isTupleStream(stream)){
|
|
return listutils::typeError("first argument must be a tuple stream");
|
|
}
|
|
if(nl->AtomType(attrNameList)!=SymbolType){
|
|
return listutils::typeError("second argument is not a valid attr name");
|
|
}
|
|
if(!listutils::isMap<2>(map)){
|
|
return listutils::typeError("third argument is not a map");
|
|
}
|
|
|
|
ListExpr attrType;
|
|
string attrName = nl->SymbolValue(attrNameList);
|
|
int j = listutils::findAttribute(nl->Second(nl->Second(stream)),
|
|
attrName, attrType);
|
|
if(j==0){
|
|
return listutils::typeError("attrname " + attrName +
|
|
" not known in the tuple");
|
|
}
|
|
if(!nl->Equal(defaultValue,attrType)){
|
|
return listutils::typeError("default value has another"
|
|
" type than the selected attribute");
|
|
}
|
|
|
|
if(nl->ListLength(map)!=4){
|
|
return listutils::typeError("map has the wrong number of arguments");
|
|
}
|
|
|
|
ListExpr maparg1 = nl->Second(map);
|
|
ListExpr maparg2 = nl->Third(map);
|
|
ListExpr mapres = nl->Fourth(map);
|
|
string err = "stream(tuple([a1 : t1, ..., an : tn])) x "
|
|
"ai x ( ti x ti -> ti) x ti expected";
|
|
|
|
if(!nl->Equal(maparg1, maparg2) ||
|
|
!nl->Equal(maparg1, mapres) ||
|
|
!nl->Equal(attrType,maparg1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom( Symbol::APPEND() ),
|
|
nl->OneElemList(nl->IntAtom(j)),
|
|
defaultValue );
|
|
}
|
|
|
|
/*
|
|
2.18.2 Value mapping function of operator ~aggregate~
|
|
|
|
*/
|
|
#ifndef USE_PROGRESS
|
|
|
|
|
|
int Aggregate(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
// The argument vector contains the following values:
|
|
// args[0] = stream of tuples
|
|
// args[1] = attribute name
|
|
// args[2] = mapping function
|
|
// args[3] = zero value
|
|
// args[4] = attribute index added by APPEND
|
|
|
|
Word t( Address(0) );
|
|
ArgVectorPointer vector;
|
|
|
|
qp->Open(args[0].addr);
|
|
int index = ((CcInt*)args[4].addr)->GetIntval();
|
|
result = qp->ResultStorage(s);
|
|
// Get the initial value
|
|
Attribute* tmpres = ((Attribute*)args[3].addr)->Clone();
|
|
qp->Request( args[0].addr, t );
|
|
Word fctres;
|
|
while( qp->Received( args[0].addr ) )
|
|
{
|
|
vector = qp->Argument(args[2].addr);
|
|
((*vector)[0]).setAddr(tmpres);
|
|
((*vector)[1]).setAddr(((Tuple*)t.addr)->GetAttribute( index-1 ) );
|
|
qp->Request(args[2].addr, fctres);
|
|
|
|
delete tmpres; //delete intermediate result
|
|
tmpres = (Attribute*) fctres.addr;
|
|
|
|
qp->ReInitResultStorage( args[2].addr );
|
|
|
|
((Tuple*)t.addr)->DeleteIfAllowed();
|
|
qp->Request( args[0].addr, t );
|
|
}
|
|
|
|
((Attribute*)result.addr)->
|
|
CopyFrom( (const Attribute*)tmpres );
|
|
|
|
delete tmpres;
|
|
qp->Close(args[0].addr);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
int Aggregate(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
// The argument vector contains the following values:
|
|
// args[0] = stream of tuples
|
|
// args[1] = attribute name
|
|
// args[2] = mapping function
|
|
// args[3] = zero value
|
|
// args[4] = attribute index added by APPEND
|
|
|
|
Word t( Address(0) );
|
|
ArgVectorPointer vector;
|
|
ProgressLocalInfo* ali=0;
|
|
ali = (ProgressLocalInfo*) local.addr;
|
|
switch ( message )
|
|
{
|
|
|
|
case OPEN :
|
|
case REQUEST:
|
|
case CLOSE: {
|
|
if(ali){
|
|
delete ali;
|
|
}
|
|
ali = new ProgressLocalInfo();
|
|
local.setAddr(ali);
|
|
qp->Open(args[0].addr);
|
|
int index = ((CcInt*)args[4].addr)->GetIntval();
|
|
result = qp->ResultStorage(s);
|
|
// Get the initial value
|
|
Attribute* tmpres = ((Attribute*)args[3].addr)->Clone();
|
|
qp->Request( args[0].addr, t );
|
|
Word fctres;
|
|
while( qp->Received( args[0].addr ) )
|
|
{
|
|
vector = qp->Argument(args[2].addr);
|
|
((*vector)[0]).setAddr(tmpres);
|
|
((*vector)[1]).setAddr(((Tuple*)t.addr)->GetAttribute( index-1 ) );
|
|
qp->Request(args[2].addr, fctres);
|
|
|
|
delete tmpres; //delete intermediate result
|
|
tmpres = (Attribute*) fctres.addr;
|
|
|
|
qp->ReInitResultStorage( args[2].addr );
|
|
|
|
((Tuple*)t.addr)->DeleteIfAllowed();
|
|
qp->Request( args[0].addr, t );
|
|
if (ali) ali->read++;
|
|
}
|
|
((Attribute*)result.addr)->CopyFrom( (const Attribute*)tmpres );
|
|
delete tmpres;
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
case CLOSEPROGRESS:{
|
|
if ( ali ) // if local info structure exists
|
|
{
|
|
delete ali; // remove it
|
|
local.setAddr(0); // and set adress to 0
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUESTPROGRESS :
|
|
{
|
|
ProgressInfo p1;
|
|
ProgressInfo *pRes;
|
|
pRes = (ProgressInfo*) result.addr;
|
|
double uAggregate=0.0121937626;
|
|
if (qp->RequestProgress(args[0].addr, &p1))
|
|
{
|
|
pRes->Card=1;
|
|
pRes->Time = p1.Time + p1.Card * uAggregate;
|
|
pRes->Progress = (p1.Progress * p1.Time + ali->read * uAggregate)/
|
|
pRes->Time;
|
|
return YIELD;
|
|
} else{
|
|
return CANCEL;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
2.19 ~aggregateC~
|
|
|
|
This operator works similar to the ~aggregate~ operator. In constrast, the
|
|
aggregateC operator uses a function tuple x t -> t, t in
|
|
DATA for aggregation.
|
|
|
|
2.19.1 Type Mapping
|
|
|
|
|
|
*/
|
|
ListExpr aggregateCTM(ListExpr args){
|
|
|
|
|
|
|
|
string err = "stream(tuple(X)) x (tuple(X) x t ->t ) x t"
|
|
"t in DATA expected";
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr stream = nl->First(args);
|
|
if(!Stream<Tuple>::checkType(stream)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
|
|
ListExpr start = nl->Third(args);
|
|
if(!listutils::isDATA(start)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr map = nl->Second(args);
|
|
if(!listutils::isMap<2>(map)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr t = nl->Second(stream);
|
|
if(!nl->Equal(t, nl->Second(map))){
|
|
return listutils::typeError("map argument 1 unequals to tuple in stream");
|
|
}
|
|
if(!nl->Equal(start,nl->Third(map))){
|
|
return listutils::typeError("second map argument unequal to start value");
|
|
}
|
|
|
|
if(!nl->Equal(start, nl->Fourth(map))){
|
|
return listutils::typeError("result of function differs from "
|
|
"start value");
|
|
}
|
|
|
|
|
|
return start;
|
|
}
|
|
|
|
/*
|
|
2.19.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int aggregateCVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
|
|
|
|
Stream<Tuple> stream(args[0]);
|
|
|
|
ArgVectorPointer funargs;
|
|
|
|
result = qp->ResultStorage(s);
|
|
Attribute* res = (Attribute*) result.addr;
|
|
|
|
Attribute* tmp = ((Attribute*) args[2].addr)->Clone();
|
|
|
|
stream.open();
|
|
Tuple* nextTuple = stream.request();
|
|
funargs= qp->Argument(args[1].addr);
|
|
while(nextTuple){
|
|
(*funargs)[0].setAddr(nextTuple);
|
|
(*funargs)[1].setAddr(tmp);
|
|
Word funres;
|
|
qp->Request(args[1].addr, funres);
|
|
tmp->DeleteIfAllowed();
|
|
tmp = ((Attribute*) funres.addr)->Clone();
|
|
nextTuple->DeleteIfAllowed();
|
|
nextTuple = stream.request();
|
|
}
|
|
|
|
res->CopyFrom(tmp);
|
|
tmp->DeleteIfAllowed();
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
|
|
|
|
const string aggregateCSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"('stream(tuple(X)) x (tuple(X) x t -> t ) x t, t in DATA -> t'"
|
|
"' _ aggregateC [ fun, t ] '"
|
|
"' Aggregates values of a tuple stream'"
|
|
"' query ten feed aggregateC[ fun( t : TUPLE, "
|
|
"i : int) attr(t,no) + i ; 5] '"
|
|
") )";
|
|
|
|
|
|
Operator aggregateC (
|
|
"aggregateC", // name
|
|
aggregateCSpec, // specification
|
|
aggregateCVM, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
aggregateCTM // type mapping
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
2.18.2 Value mapping function of operator ~aggregateB~
|
|
|
|
The ~aggregateB~ operator uses a stack to compute the aggregation
|
|
balanced. This will have advantages in geomatric aggregation.
|
|
It may also help to reduce numeric errors in aggregation using
|
|
double values.
|
|
|
|
|
|
2.18.2.1 ~StackEntry~
|
|
|
|
A stack entry consist of the level within the (simulated)
|
|
balanced tree and the corresponding value.
|
|
Note:
|
|
The attributes at level 0 come directly from the input stream.
|
|
We have to free them using the deleteIfAllowed function.
|
|
On all other levels, the attribute is computes using the
|
|
parameter function. Because this is outside of stream and
|
|
tuples, no reference counting is available and we have to delete
|
|
them using the usual delete function.
|
|
|
|
*/
|
|
struct AggrStackEntry
|
|
{
|
|
inline AggrStackEntry(): level(-1),value(0)
|
|
{ }
|
|
|
|
inline AggrStackEntry( long level, Attribute* value):
|
|
level( level )
|
|
{ this->value = value;}
|
|
|
|
inline AggrStackEntry( const AggrStackEntry& a ):
|
|
level( a.level )
|
|
{ this->value = a.value;}
|
|
|
|
inline AggrStackEntry& operator=( const AggrStackEntry& a )
|
|
{ level = a.level; value = a.value; return *this; }
|
|
|
|
inline ~AggrStackEntry(){ } // use destroy !!
|
|
|
|
|
|
inline void destroy(){
|
|
if(level<0){
|
|
return;
|
|
}
|
|
if(level==0){ // original from tuple
|
|
value->DeleteIfAllowed();
|
|
} else {
|
|
delete value;
|
|
}
|
|
value = 0;
|
|
level = -1;
|
|
}
|
|
|
|
long level;
|
|
Attribute* value;
|
|
};
|
|
|
|
int AggregateB(Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
// The argument vector contains the following values:
|
|
// args[0] = stream of tuples
|
|
// args[1] = attribute name
|
|
// args[2] = mapping function
|
|
// args[3] = zero value
|
|
// args[4] = attribute index added by APPEND
|
|
|
|
Word t1,resultWord;
|
|
ArgVectorPointer vector = qp->Argument(args[2].addr);
|
|
|
|
qp->Open(args[0].addr);
|
|
result = qp->ResultStorage(s);
|
|
int index = ((CcInt*)args[4].addr)->GetIntval();
|
|
|
|
// read the first tuple
|
|
qp->Request( args[0].addr, t1 );
|
|
|
|
|
|
if( !qp->Received( args[0].addr ) ){
|
|
// special case: stream is empty
|
|
// use the third argument as result
|
|
((Attribute*)result.addr)->
|
|
CopyFrom( (const Attribute*)args[3].addr );
|
|
|
|
} else {
|
|
// nonempty stream, consume it
|
|
stack<AggrStackEntry> theStack;
|
|
while( qp->Received(args[0].addr)){
|
|
// get the attribute from the current tuple
|
|
Attribute* attr = ((Tuple*)t1.addr)->GetAttribute(index-1)->Copy();
|
|
// the tuple is not longer needed here
|
|
((Tuple*)t1.addr)->DeleteIfAllowed();
|
|
// put the attribute on the stack merging with existing entries
|
|
// while possible
|
|
int level = 0;
|
|
while(!theStack.empty() && level==theStack.top().level){
|
|
// merging is possible
|
|
AggrStackEntry top = theStack.top();
|
|
theStack.pop();
|
|
// call the parameter function
|
|
((*vector)[0]).setAddr(top.value);
|
|
((*vector)[1]).setAddr(attr);
|
|
qp->Request(args[2].addr, resultWord);
|
|
qp->ReInitResultStorage(args[2].addr);
|
|
top.destroy(); // remove stack content
|
|
if(level==0){ // delete attr;
|
|
attr->DeleteIfAllowed();
|
|
} else {
|
|
delete attr;
|
|
}
|
|
attr = (Attribute*) resultWord.addr;
|
|
level++;
|
|
}
|
|
AggrStackEntry entry(level,attr);
|
|
theStack.push(entry);
|
|
qp->Request(args[0].addr,t1);
|
|
}
|
|
// stream ends, merge stack elements regardless of their level
|
|
assert(!theStack.empty()); // at least one element must be exist
|
|
AggrStackEntry tmpResult = theStack.top();
|
|
theStack.pop();
|
|
while(!theStack.empty()){
|
|
AggrStackEntry top = theStack.top();
|
|
theStack.pop();
|
|
((*vector)[0]).setAddr(top.value);
|
|
((*vector)[1]).setAddr(tmpResult.value);
|
|
qp->Request(args[2].addr, resultWord);
|
|
qp->ReInitResultStorage(args[2].addr);
|
|
tmpResult.destroy(); // destroy temporarly result
|
|
tmpResult.level = 1; // mark as computed
|
|
tmpResult.value = (Attribute*) resultWord.addr;
|
|
top.destroy();
|
|
}
|
|
((Attribute*)result.addr)->
|
|
CopyFrom((Attribute*)tmpResult.value);
|
|
tmpResult.destroy();
|
|
}
|
|
// close input stream
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
2.18.3 Specification of operator ~aggregate~
|
|
|
|
*/
|
|
const string AggregateSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>(stream(tuple((a1 t1) ... (an tn))) ai"
|
|
"((ti ti) -> ti) ti -> ti</text--->"
|
|
"<text>_ aggregate [ AttrName; fun; InitialValue]</text--->"
|
|
"<text>Aggregates the values from the attribute 'AttrName'"
|
|
"in the tuplestream using function 'fun' and starting "
|
|
"with 'InitialValue' as first left argument for 'fun'. "
|
|
"Returns 'InitialValue', if the stream is empty.</text--->"
|
|
"<text>query ten feed aggregate[no; "
|
|
"fun(i1: int, i2: int) i1+i2; 0]</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.18.3 Specification of operator ~aggregateB~
|
|
|
|
*/
|
|
const string AggregateBSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>(stream(tuple((a1 t1) ... (an tn))) ai"
|
|
"((ti ti) -> ti) ti -> ti</text--->"
|
|
"<text>_ aggregateB [ AttrName ; fun; ZeroValue ]</text--->"
|
|
"<text>Aggregates the values of attribute 'AttrName' "
|
|
"from the tuple stream using the function 'fun' in a balanced "
|
|
"fashion. Returns 'ZeroValue', if the stream is empty.</text--->"
|
|
"<text>query ten feed aggregateB[no; "
|
|
"fun(i1: int, i2: int) i1+i2; 0]</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.18.4 Definition of operator ~aggregate~
|
|
|
|
*/
|
|
Operator extrelaggregate (
|
|
"aggregate", // name
|
|
AggregateSpec, // specification
|
|
Aggregate, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
AggregateTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
2.18.4 Definition of operator ~aggregate\_new~
|
|
|
|
*/
|
|
Operator extrelaggregateB (
|
|
"aggregateB", // name
|
|
AggregateBSpec, // specification
|
|
AggregateB, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
AggregateTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
|
|
5.10 Operator ~symmjoin~
|
|
|
|
5.10.1 Type mapping function of operator ~symmjoin~
|
|
|
|
Result type of symmjoin operation.
|
|
|
|
---- ((stream (tuple (x1 ... xn))) (stream (tuple (y1 ... ym)))
|
|
(map tuple tuple bool)
|
|
|
|
-> (stream (tuple (x1 ... xn y1 ... ym)))
|
|
----
|
|
|
|
*/
|
|
ListExpr SymmJoinTypeMap(ListExpr args)
|
|
{
|
|
if(nl->ListLength(args)!=3){
|
|
return listutils::typeError("three arguments expected");
|
|
}
|
|
ListExpr stream1 = nl->First(args);
|
|
ListExpr stream2 = nl->Second(args);
|
|
ListExpr map = nl->Third(args);
|
|
|
|
string err = "stream(tuple1) x stream(tuple2) x "
|
|
"( tuple1 x tuple2 -> bool) expected";
|
|
if(!listutils::isTupleStream(stream1) ||
|
|
!listutils::isTupleStream(stream2) ||
|
|
!listutils::isMap<2>(map)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(!nl->Equal(nl->Second(stream1), nl->Second(map)) ||
|
|
!nl->Equal(nl->Second(stream2), nl->Third(map)) ||
|
|
!listutils::isSymbol(nl->Fourth(map),CcBool::BasicType())){
|
|
return listutils::typeError(err +"(wrong mapping)");
|
|
}
|
|
|
|
ListExpr a1List = nl->Second(nl->Second(stream1));
|
|
ListExpr a2List = nl->Second(nl->Second(stream2));
|
|
|
|
if(!listutils::disjointAttrNames(a1List,a2List)){
|
|
return listutils::typeError(err + "(name conflict in tuples");
|
|
}
|
|
ListExpr list = ConcatLists(a1List, a2List);
|
|
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()),
|
|
list));
|
|
}
|
|
/*
|
|
|
|
5.10.2 Value mapping function of operator ~symmjoin~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
|
|
// standard version
|
|
|
|
|
|
struct SymmJoinLocalInfo
|
|
{
|
|
TupleType *resultTupleType;
|
|
|
|
TupleBuffer *rightRel;
|
|
GenericRelationIterator *rightIter;
|
|
TupleBuffer *leftRel;
|
|
GenericRelationIterator *leftIter;
|
|
bool right;
|
|
Tuple *currTuple;
|
|
bool rightFinished;
|
|
bool leftFinished;
|
|
};
|
|
|
|
int
|
|
SymmJoin(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Word r, l;
|
|
SymmJoinLocalInfo* pli;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch (message)
|
|
{
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetResultTupleType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN :
|
|
{
|
|
long MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024);
|
|
cmsg.info("ERA:ShowMemInfo") << "SymmJoin.MAX_MEMORY ("
|
|
<< MAX_MEMORY/1024 << " kB): " << endl;
|
|
cmsg.send();
|
|
pli = new SymmJoinLocalInfo;
|
|
pli->rightRel = new TupleBuffer( MAX_MEMORY / 2 );
|
|
pli->rightIter = 0;
|
|
pli->leftRel = new TupleBuffer( MAX_MEMORY / 2 );
|
|
pli->leftIter = 0;
|
|
pli->right = true;
|
|
pli->currTuple = 0;
|
|
pli->rightFinished = false;
|
|
pli->leftFinished = false;
|
|
|
|
tt->IncReference();
|
|
pli->resultTupleType = tt;
|
|
|
|
qp->Open(args[0].addr);
|
|
qp->Open(args[1].addr);
|
|
|
|
local.setAddr(pli);
|
|
return 0;
|
|
}
|
|
case REQUEST :
|
|
{
|
|
pli = (SymmJoinLocalInfo*)local.addr;
|
|
|
|
while( 1 )
|
|
// This loop will end in some of the returns.
|
|
{
|
|
if( pli->right )
|
|
// Get the tuple from the right stream and match it with the
|
|
// left stored buffer
|
|
{
|
|
if( pli->currTuple == 0 )
|
|
{
|
|
qp->Request(args[0].addr, r);
|
|
if( qp->Received( args[0].addr ) )
|
|
{
|
|
pli->currTuple = (Tuple*)r.addr;
|
|
pli->leftIter = pli->leftRel->MakeScan();
|
|
}
|
|
else
|
|
{
|
|
pli->rightFinished = true;
|
|
if( pli->leftFinished )
|
|
return CANCEL;
|
|
else
|
|
{
|
|
pli->right = false;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have a tuple from the right stream in currTuple
|
|
// and an open iterator on the left stored buffer.
|
|
Tuple *leftTuple = pli->leftIter->GetNextTuple();
|
|
|
|
if( leftTuple == 0 )
|
|
// There are no more tuples in the left iterator. We then
|
|
// store the current tuple in the right buffer and close the
|
|
// left iterator.
|
|
{
|
|
if( !pli->leftFinished )
|
|
// We only need to keep track of the right tuples if the
|
|
// left stream is not finished.
|
|
{
|
|
pli->rightRel->AppendTuple( pli->currTuple );
|
|
pli->right = false;
|
|
}
|
|
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple = 0;
|
|
|
|
delete pli->leftIter;
|
|
pli->leftIter = 0;
|
|
|
|
continue; // Go back to the loop
|
|
}
|
|
else
|
|
// We match the tuples.
|
|
{
|
|
ArgVectorPointer funArgs = qp->Argument(args[2].addr);
|
|
((*funArgs)[0]).setAddr( pli->currTuple );
|
|
((*funArgs)[1]).setAddr( leftTuple );
|
|
Word funResult;
|
|
qp->Request(args[2].addr, funResult);
|
|
CcBool *boolFunResult = (CcBool*)funResult.addr;
|
|
|
|
if( boolFunResult->IsDefined() &&
|
|
boolFunResult->GetBoolval() )
|
|
{
|
|
Tuple *resultTuple = new Tuple( pli->resultTupleType );
|
|
Concat( pli->currTuple, leftTuple, resultTuple );
|
|
leftTuple->DeleteIfAllowed();
|
|
leftTuple = 0;
|
|
result.setAddr( resultTuple );
|
|
return YIELD;
|
|
}
|
|
else
|
|
{
|
|
leftTuple->DeleteIfAllowed();
|
|
leftTuple = 0;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
else
|
|
// Get the tuple from the left stream and match it with the
|
|
// right stored buffer
|
|
{
|
|
if( pli->currTuple == 0 )
|
|
{
|
|
qp->Request(args[1].addr, l);
|
|
if( qp->Received( args[1].addr ) )
|
|
{
|
|
pli->currTuple = (Tuple*)l.addr;
|
|
pli->rightIter = pli->rightRel->MakeScan();
|
|
}
|
|
else
|
|
{
|
|
pli->leftFinished = true;
|
|
if( pli->rightFinished )
|
|
return CANCEL;
|
|
else
|
|
{
|
|
pli->right = true;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have a tuple from the left stream in currTuple and
|
|
// an open iterator on the right stored buffer.
|
|
Tuple *rightTuple = pli->rightIter->GetNextTuple();
|
|
|
|
if( rightTuple == 0 )
|
|
// There are no more tuples in the right iterator. We then
|
|
// store the current tuple in the left buffer and close
|
|
// the right iterator.
|
|
{
|
|
if( !pli->rightFinished )
|
|
// We only need to keep track of the left tuples if the
|
|
// right stream is not finished.
|
|
{
|
|
pli->leftRel->AppendTuple( pli->currTuple );
|
|
pli->right = true;
|
|
}
|
|
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple = 0;
|
|
|
|
delete pli->rightIter;
|
|
pli->rightIter = 0;
|
|
|
|
continue; // Go back to the loop
|
|
}
|
|
else
|
|
// We match the tuples.
|
|
{
|
|
ArgVectorPointer funArgs = qp->Argument(args[2].addr);
|
|
((*funArgs)[0]).setAddr( rightTuple );
|
|
((*funArgs)[1]).setAddr( pli->currTuple );
|
|
Word funResult;
|
|
qp->Request(args[2].addr, funResult);
|
|
CcBool *boolFunResult = (CcBool*)funResult.addr;
|
|
|
|
if( boolFunResult->IsDefined() &&
|
|
boolFunResult->GetBoolval() )
|
|
{
|
|
Tuple *resultTuple = new Tuple( pli->resultTupleType );
|
|
Concat( rightTuple, pli->currTuple, resultTuple );
|
|
rightTuple->DeleteIfAllowed();
|
|
rightTuple = 0;
|
|
result.setAddr( resultTuple );
|
|
return YIELD;
|
|
}
|
|
else
|
|
{
|
|
|
|
rightTuple->DeleteIfAllowed();
|
|
rightTuple = 0;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case CLOSE :
|
|
{
|
|
pli = (SymmJoinLocalInfo*)local.addr;
|
|
if(pli)
|
|
{
|
|
if( pli->currTuple != 0 )
|
|
pli->currTuple->DeleteIfAllowed();
|
|
|
|
delete pli->leftIter;
|
|
delete pli->rightIter;
|
|
if( pli->resultTupleType != 0 )
|
|
pli->resultTupleType->DeleteIfAllowed();
|
|
|
|
if( pli->rightRel != 0 )
|
|
{
|
|
pli->rightRel->Clear();
|
|
delete pli->rightRel;
|
|
}
|
|
|
|
if( pli->leftRel != 0 )
|
|
{
|
|
pli->leftRel->Clear();
|
|
delete pli->leftRel;
|
|
}
|
|
|
|
delete pli;
|
|
local.setAddr(0);
|
|
}
|
|
|
|
qp->Close(args[0].addr);
|
|
qp->Close(args[1].addr);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
// with support for progress queries
|
|
|
|
class SymmJoinLocalInfo: public ProgressLocalInfo
|
|
{
|
|
public:
|
|
|
|
SymmJoinLocalInfo(): resultTupleType(0), rightRel(0),
|
|
rightIter(0),leftRel(0),leftIter(0),right(false),
|
|
currTuple(0),rightFinished(false),leftFinished(false),
|
|
costEstimation(0) {}
|
|
|
|
~SymmJoinLocalInfo() {
|
|
if(resultTupleType){
|
|
resultTupleType->DeleteIfAllowed();
|
|
resultTupleType =0;
|
|
}
|
|
if(currTuple){
|
|
currTuple->DeleteIfAllowed();
|
|
currTuple =0;
|
|
}
|
|
if(rightRel){
|
|
delete rightRel;
|
|
rightRel=0;
|
|
}
|
|
if(rightIter){
|
|
delete rightIter;
|
|
rightIter=0;
|
|
}
|
|
if(leftRel){
|
|
delete leftRel;
|
|
leftRel=0;
|
|
}
|
|
if(leftIter){
|
|
delete leftIter;
|
|
leftIter=0;
|
|
}
|
|
}
|
|
|
|
TupleType *resultTupleType;
|
|
|
|
TupleBuffer *rightRel;
|
|
GenericRelationIterator *rightIter;
|
|
TupleBuffer *leftRel;
|
|
GenericRelationIterator *leftIter;
|
|
bool right;
|
|
Tuple *currTuple;
|
|
bool rightFinished;
|
|
bool leftFinished;
|
|
SymmjoinCostEstimation *costEstimation;
|
|
};
|
|
|
|
CostEstimation* SymmjoinCostEstimationFunc() {
|
|
return new SymmjoinCostEstimation();
|
|
}
|
|
|
|
int
|
|
SymmJoin(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Word r, l;
|
|
SymmJoinLocalInfo* pli;
|
|
|
|
pli = (SymmJoinLocalInfo*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch (message)
|
|
{
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN :
|
|
{
|
|
|
|
long MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024);
|
|
cmsg.info("ERA:ShowMemInfo") << "SymmJoin.MAX_MEMORY ("
|
|
<< MAX_MEMORY/1024 << " MB): " << endl;
|
|
cmsg.send();
|
|
|
|
|
|
if ( pli ){
|
|
delete pli;
|
|
}
|
|
pli = new SymmJoinLocalInfo;
|
|
pli->rightRel = new TupleBuffer( MAX_MEMORY / 2 );
|
|
pli->rightIter = 0;
|
|
pli->leftRel = new TupleBuffer( MAX_MEMORY / 2 );
|
|
pli->leftIter = 0;
|
|
pli->right = true;
|
|
pli->currTuple = 0;
|
|
pli->rightFinished = false;
|
|
pli->leftFinished = false;
|
|
|
|
pli -> costEstimation =
|
|
(SymmjoinCostEstimation*) qp->getCostEstimation(s);
|
|
pli -> costEstimation -> init(NULL, NULL);
|
|
tt->IncReference();
|
|
pli->resultTupleType =tt;
|
|
pli->readFirst = 0;
|
|
pli->readSecond = 0;
|
|
pli->returned = 0;
|
|
local.setAddr(pli);
|
|
|
|
qp->Open(args[0].addr);
|
|
qp->Open(args[1].addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST :
|
|
{
|
|
if(!pli){
|
|
return CANCEL;
|
|
}
|
|
while( true )
|
|
// This loop will end in some of the returns.
|
|
{
|
|
if( pli->right )
|
|
// Get the tuple from the right stream and match it with the
|
|
// left stored buffer
|
|
{
|
|
if( pli->currTuple == 0 )
|
|
{
|
|
qp->Request(args[0].addr, r);
|
|
if( qp->Received( args[0].addr ) )
|
|
{
|
|
pli->currTuple = (Tuple*)r.addr;
|
|
pli->leftIter = pli->leftRel->MakeScan();
|
|
pli->readFirst++;
|
|
pli->costEstimation->processedTupleInStream2();
|
|
pli->costEstimation->setTupleBuffer2OnDisk(
|
|
! pli->rightRel->InMemory());
|
|
}
|
|
else
|
|
{
|
|
pli->rightFinished = true;
|
|
pli->costEstimation-> setStream2Exhausted(true);
|
|
if( pli->leftFinished ) {
|
|
return CANCEL;
|
|
} else {
|
|
pli->right = false;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have a tuple from the right stream in currTuple
|
|
// and an open iterator on the left stored buffer.
|
|
Tuple *leftTuple = pli->leftIter->GetNextTuple();
|
|
|
|
if( leftTuple == 0 )
|
|
// There are no more tuples in the left iterator. We then
|
|
// store the current tuple in the right buffer and close the
|
|
// left iterator.
|
|
{
|
|
if( !pli->leftFinished )
|
|
// We only need to keep track of the right tuples if the
|
|
// left stream is not finished.
|
|
{
|
|
pli->rightRel->AppendTuple( pli->currTuple );
|
|
pli->right = false;
|
|
}
|
|
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple = 0;
|
|
|
|
delete pli->leftIter;
|
|
pli->leftIter = 0;
|
|
|
|
continue; // Go back to the loop
|
|
}
|
|
else
|
|
// We match the tuples.
|
|
{
|
|
ArgVectorPointer funArgs = qp->Argument(args[2].addr);
|
|
((*funArgs)[0]).setAddr( pli->currTuple );
|
|
((*funArgs)[1]).setAddr( leftTuple );
|
|
Word funResult;
|
|
qp->Request(args[2].addr, funResult);
|
|
CcBool *boolFunResult = (CcBool*)funResult.addr;
|
|
|
|
if( boolFunResult->IsDefined() &&
|
|
boolFunResult->GetBoolval() )
|
|
{
|
|
Tuple *resultTuple = new Tuple( pli->resultTupleType );
|
|
Concat( pli->currTuple, leftTuple, resultTuple );
|
|
leftTuple->DeleteIfAllowed();
|
|
leftTuple = 0;
|
|
result.setAddr( resultTuple );
|
|
pli->returned++;
|
|
return YIELD;
|
|
}
|
|
else
|
|
{
|
|
leftTuple->DeleteIfAllowed();
|
|
leftTuple = 0;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
else
|
|
// Get the tuple from the left stream and match it with the
|
|
// right stored buffer
|
|
{
|
|
if( pli->currTuple == 0 )
|
|
{
|
|
qp->Request(args[1].addr, l);
|
|
if( qp->Received( args[1].addr ) )
|
|
{
|
|
pli->currTuple = (Tuple*)l.addr;
|
|
pli->rightIter = pli->rightRel->MakeScan();
|
|
pli->readSecond++;
|
|
pli->costEstimation->processedTupleInStream1();
|
|
pli->costEstimation->setTupleBuffer1OnDisk(
|
|
! pli->leftRel->InMemory());
|
|
}
|
|
else
|
|
{
|
|
pli->leftFinished = true;
|
|
pli->costEstimation->setStream1Exhausted(true);
|
|
if( pli->rightFinished ) {
|
|
return CANCEL;
|
|
} else {
|
|
pli->right = true;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have a tuple from the left stream in currTuple and
|
|
// an open iterator on the right stored buffer.
|
|
Tuple *rightTuple = pli->rightIter->GetNextTuple();
|
|
|
|
if( rightTuple == 0 )
|
|
// There are no more tuples in the right iterator. We then
|
|
// store the current tuple in the left buffer and close
|
|
// the right iterator.
|
|
{
|
|
if( !pli->rightFinished )
|
|
// We only need to keep track of the left tuples if the
|
|
// right stream is not finished.
|
|
{
|
|
pli->leftRel->AppendTuple( pli->currTuple );
|
|
pli->right = true;
|
|
}
|
|
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple = 0;
|
|
|
|
delete pli->rightIter;
|
|
pli->rightIter = 0;
|
|
|
|
continue; // Go back to the loop
|
|
}
|
|
else
|
|
// We match the tuples.
|
|
{
|
|
ArgVectorPointer funArgs = qp->Argument(args[2].addr);
|
|
((*funArgs)[0]).setAddr( rightTuple );
|
|
((*funArgs)[1]).setAddr( pli->currTuple );
|
|
Word funResult;
|
|
qp->Request(args[2].addr, funResult);
|
|
CcBool *boolFunResult = (CcBool*)funResult.addr;
|
|
|
|
if( boolFunResult->IsDefined() &&
|
|
boolFunResult->GetBoolval() )
|
|
{
|
|
Tuple *resultTuple = new Tuple( pli->resultTupleType );
|
|
Concat( rightTuple, pli->currTuple, resultTuple );
|
|
rightTuple->DeleteIfAllowed();
|
|
rightTuple = 0;
|
|
result.setAddr( resultTuple );
|
|
pli->returned++;
|
|
return YIELD;
|
|
}
|
|
else
|
|
{
|
|
rightTuple->DeleteIfAllowed();
|
|
rightTuple = 0;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case CLOSE :
|
|
{
|
|
if(pli)
|
|
{
|
|
if( pli->currTuple != 0 ){
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple=0;
|
|
}
|
|
|
|
if(pli->leftIter){
|
|
delete pli->leftIter;
|
|
pli->leftIter = 0;
|
|
}
|
|
if(pli->rightIter){
|
|
delete pli->rightIter;
|
|
pli->rightIter=0;
|
|
}
|
|
if( pli->resultTupleType != 0 ){
|
|
pli->resultTupleType->DeleteIfAllowed();
|
|
pli->resultTupleType=0;
|
|
}
|
|
|
|
if( pli->rightRel != 0 )
|
|
{
|
|
pli->rightRel->Clear();
|
|
delete pli->rightRel;
|
|
pli->rightRel=0;
|
|
}
|
|
|
|
if( pli->leftRel != 0 )
|
|
{
|
|
pli->leftRel->Clear();
|
|
delete pli->leftRel;
|
|
pli->leftRel=0;
|
|
}
|
|
|
|
delete pli;
|
|
local.setAddr(0);
|
|
}
|
|
|
|
qp->Close(args[0].addr);
|
|
qp->Close(args[1].addr);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
5.10.3 Specification of operator ~symmjoin~
|
|
|
|
*/
|
|
const string SymmJoinSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple (x1 ... xn))) (stream "
|
|
"(tuple (y1 ... ym)))) (map (tuple (x1 ... xn)) "
|
|
"(tuple (y1 ... ym)) -> bool) -> (stream (tuple (x1 "
|
|
"... xn y1 ... ym)))</text--->"
|
|
"<text>_ _ symmjoin[ fun ]</text--->"
|
|
"<text>Computes a Cartesian product stream from "
|
|
"its two argument streams filtering by the third "
|
|
"argument.</text--->"
|
|
"<text>query ten feed {a} twenty feed {b} "
|
|
"symmjoin[.no_a = .no_b] count</text--->"
|
|
" ) )";
|
|
|
|
/*
|
|
|
|
5.10.4 Definition of operator ~symmjoin~
|
|
|
|
*/
|
|
|
|
#ifndef USE_PROGRESS
|
|
Operator extrelsymmjoin (
|
|
"symmjoin", // name
|
|
SymmJoinSpec, // specification
|
|
SymmJoin, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SymmJoinTypeMap // type mapping
|
|
// true // needs large amounts of memory
|
|
);
|
|
#else
|
|
Operator extrelsymmjoin (
|
|
"symmjoin", // name
|
|
SymmJoinSpec, // specification
|
|
SymmJoin, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SymmJoinTypeMap, // type mapping
|
|
SymmjoinCostEstimationFunc // CostEstimation
|
|
// true // needs large amounts of memory
|
|
);
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
5.10.5 Operator ~symmproductextend~
|
|
|
|
02.06.2006 Christian Duentgen
|
|
|
|
In queries, it is advantageous to avoid the multiple evaluation of common
|
|
subexpressions (CSE). One way is to evaluate a CSE when it is needed for
|
|
the first time, and append a new attribute with the CSE's value to the tuple,
|
|
using operator ~extend~. When a CSE first appears within a join condition, and
|
|
the CSE depends on attributes from both input streams to join, this method
|
|
fails. To cope with such situations and optimize the benefit of CSE
|
|
substitution, we implement the ~symmproductextend~ operator.
|
|
|
|
The operator has three arguments, the first and second being the input tuple
|
|
streams. The third argument is a function list (i.e. a list of pairs
|
|
(attributename function), where ~attributename~ is a new identifier and
|
|
~function~ is a function calculating the new attribute's value from a pair
|
|
of tuples, one taken from each input tuple stream.
|
|
|
|
Subsequent to a ~symmproductextend~, a ~filter~ can be applied, e.g. to express
|
|
a ``join condition'' using the attributes extended (representing CSEs).
|
|
|
|
The operator works similar to
|
|
|
|
---- stream(tuple(X)) stream(tuple(Y)) symmjoin[TRUE] extend[ funlist ].
|
|
----
|
|
|
|
5.10.5.1 Typemapping for operator ~symmproductextend~
|
|
|
|
----
|
|
(stream (tuple (A)))
|
|
x (stream (tuple (B)))
|
|
x ( ( c1 (map tuple(A) tuple(B) tc1))
|
|
...
|
|
|
|
( ck (map tuple(A) tuple(B) tck)))
|
|
-> (stream (tuple(A*B*C)))
|
|
|
|
where A = (ta1 a1) ... (tan an)
|
|
B = (tb1 b1) ... (tbm bm)
|
|
C = (tc1 c1) ... (tck ck)
|
|
----
|
|
|
|
*/
|
|
|
|
/*
|
|
Typemapping operator for operator ~symmproductextend~
|
|
|
|
*/
|
|
|
|
ListExpr SymmProductExtendTypeMap(ListExpr args)
|
|
{
|
|
if(nl->ListLength(args)!=3){
|
|
return listutils::typeError("three arguments expected");
|
|
}
|
|
|
|
ListExpr stream1 = nl->First(args);
|
|
ListExpr stream2 = nl->Second(args);
|
|
ListExpr mapList = nl->Third(args);
|
|
|
|
string err = "stream(tupleA) x stream(tupleB) x "
|
|
"((name (tupleA x tupleB -> DATA))+) expected";
|
|
|
|
if(!listutils::isTupleStream(stream1) ||
|
|
!listutils::isTupleStream(stream2) ||
|
|
nl->IsAtom(mapList)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
|
|
ListExpr TType1 = nl->Second(stream1);
|
|
ListExpr TType2 = nl->Second(stream2);
|
|
|
|
ListExpr attrL1 = nl->Second(TType1);
|
|
// copy all attributes from the first list
|
|
ListExpr newAttrList = nl->OneElemList(nl->First(attrL1));
|
|
ListExpr lastAttrList = newAttrList;
|
|
attrL1 = nl->Rest(attrL1);
|
|
while(!nl->IsEmpty(attrL1)){
|
|
lastAttrList = nl->Append(lastAttrList,nl->First(attrL1));
|
|
attrL1 = nl->Rest(attrL1);
|
|
}
|
|
// copy the attributes of the second tuple stream
|
|
ListExpr attrL2 = nl->Second(TType2);
|
|
while(!nl->IsEmpty(attrL2)){
|
|
lastAttrList = nl->Append(lastAttrList, nl->First(attrL2));
|
|
attrL2 = nl->Rest(attrL2);
|
|
}
|
|
|
|
// for each mapping
|
|
|
|
while(!nl->IsEmpty(mapList)){
|
|
ListExpr namedMap = nl->First(mapList);
|
|
mapList = nl->Rest(mapList);
|
|
|
|
if(nl->ListLength(namedMap)!=2){
|
|
return listutils::typeError(err +"( invalid named map found)");
|
|
}
|
|
|
|
if(nl->AtomType(nl->First(namedMap))!=SymbolType){
|
|
return listutils::typeError(err+ "(invalid attribute name found");
|
|
}
|
|
ListExpr map = nl->Second(namedMap);
|
|
|
|
if(!listutils::isMap<2>(map)){
|
|
return listutils::typeError(err+" ( found a incorrect map in list)");
|
|
}
|
|
if(!nl->Equal(TType1, nl->Second(map)) ||
|
|
!nl->Equal(TType2, nl->Third(map))){
|
|
return listutils::typeError(err +"( ivalid argument in mapping)");
|
|
}
|
|
lastAttrList = nl->Append(lastAttrList,
|
|
nl->TwoElemList(nl->First(namedMap),
|
|
nl->Fourth(map)));
|
|
}
|
|
|
|
if(!listutils::isAttrList(newAttrList)){
|
|
return listutils::typeError(err + " ( error in result stream: "
|
|
"conflicting names or non-DATA attributes)");
|
|
}
|
|
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()),
|
|
newAttrList));
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
5.10.5.2 Value mapping function of operator ~symmproductextend~
|
|
|
|
*/
|
|
struct SymmProductExtendLocalInfo
|
|
{
|
|
TupleType *resultTupleType;
|
|
TupleBuffer *rightRel;
|
|
GenericRelationIterator *rightIter;
|
|
TupleBuffer *leftRel;
|
|
GenericRelationIterator *leftIter;
|
|
bool right;
|
|
Tuple *currTuple;
|
|
bool rightFinished;
|
|
bool leftFinished;
|
|
};
|
|
|
|
int
|
|
SymmProductExtend(Word* args, Word& result,
|
|
int message, Word& local, Supplier s)
|
|
{
|
|
Word r, l, value;
|
|
SymmProductExtendLocalInfo* pli;
|
|
int i, nooffun;
|
|
Supplier supplier, supplier2, supplier3;
|
|
ArgVectorPointer extFunArgs;
|
|
Tuple *resultTuple;
|
|
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch (message)
|
|
{
|
|
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
case FINISH : {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case OPEN :
|
|
{
|
|
long MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024);
|
|
cmsg.info("ERA:ShowMemInfo") << "SymmProductExtend.MAX_MEMORY ("
|
|
<< MAX_MEMORY/1024 << " MB): " << endl;
|
|
cmsg.send();
|
|
pli = new SymmProductExtendLocalInfo;
|
|
pli->rightRel = new TupleBuffer( MAX_MEMORY / 2 );
|
|
pli->rightIter = 0;
|
|
pli->leftRel = new TupleBuffer( MAX_MEMORY / 2 );
|
|
pli->leftIter = 0;
|
|
pli->right = true;
|
|
pli->currTuple = 0;
|
|
pli->rightFinished = false;
|
|
pli->leftFinished = false;
|
|
|
|
tt->IncReference();
|
|
pli->resultTupleType = tt;
|
|
qp->Open(args[0].addr);
|
|
qp->Open(args[1].addr);
|
|
|
|
local.setAddr(pli);
|
|
return 0;
|
|
}
|
|
case REQUEST :
|
|
{
|
|
pli = (SymmProductExtendLocalInfo*)local.addr;
|
|
|
|
while( 1 )
|
|
// This loop will end in some of the returns.
|
|
{
|
|
if( pli->right )
|
|
// Get the tuple from the right stream and match it with the
|
|
// left stored buffer
|
|
{
|
|
if( pli->currTuple == 0 )
|
|
{
|
|
qp->Request(args[1].addr, r);
|
|
if( qp->Received( args[1].addr ) )
|
|
{
|
|
pli->currTuple = (Tuple*)r.addr;
|
|
pli->leftIter = pli->leftRel->MakeScan();
|
|
}
|
|
else
|
|
{
|
|
pli->rightFinished = true;
|
|
if( pli->leftFinished )
|
|
return CANCEL;
|
|
else
|
|
{
|
|
pli->right = false;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have a tuple from the right stream in currTuple
|
|
// and an open iterator on the left stored buffer.
|
|
Tuple *leftTuple = pli->leftIter->GetNextTuple();
|
|
|
|
if( leftTuple == 0 )
|
|
// There are no more tuples in the left iterator. We then
|
|
// store the current tuple in the right buffer and close the
|
|
// left iterator.
|
|
{
|
|
if( !pli->leftFinished )
|
|
// We only need to keep track of the right tuples if the
|
|
// left stream is not finished.
|
|
{
|
|
pli->rightRel->AppendTuple( pli->currTuple );
|
|
pli->right = false;
|
|
}
|
|
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple = 0;
|
|
|
|
delete pli->leftIter;
|
|
pli->leftIter = 0;
|
|
|
|
continue; // Go back to the loop
|
|
}
|
|
else
|
|
// We match the tuples.
|
|
{
|
|
// XRIS: We must calculate the new attributes' values here
|
|
// and extend currTuple with them
|
|
|
|
resultTuple = new Tuple( pli->resultTupleType );
|
|
Concat( leftTuple, pli->currTuple, resultTuple );
|
|
|
|
supplier = args[2].addr; // get list of ext-functions
|
|
nooffun = qp->GetNoSons(supplier); // get num of ext-functions
|
|
for(i = 0; i< nooffun; i++)
|
|
{
|
|
supplier2 = qp->GetSupplier(supplier, i); // get an ext-function
|
|
//noofsons = qp->GetNoSons(supplier2);
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
extFunArgs = qp->Argument(supplier3);
|
|
((*extFunArgs)[0]).setAddr(leftTuple); // pass first argument
|
|
((*extFunArgs)[1]).setAddr(pli->currTuple);// pass second argument
|
|
qp->Request(supplier3,value); // call extattr mapping
|
|
// The original implementation tried to avoid copying the function result,
|
|
// but somehow, this results in a strongly growing tuplebuffer on disk:
|
|
// resultTuple->PutAttribute(
|
|
// leftTuple->GetNoAttributes()
|
|
// + pli->currTuple->GetNoAttributes()
|
|
// + i, (Attribute*)value.addr);
|
|
// qp->ReInitResultStorage( supplier3 );
|
|
resultTuple->PutAttribute(
|
|
leftTuple->GetNoAttributes()
|
|
+ pli->currTuple->GetNoAttributes()
|
|
+ i, ((Attribute*)value.addr)->Clone() );
|
|
}
|
|
leftTuple->DeleteIfAllowed();
|
|
leftTuple = 0;
|
|
result.setAddr( resultTuple );
|
|
return YIELD;
|
|
}
|
|
}
|
|
else
|
|
// Get the tuple from the left stream and match it with the
|
|
// right stored buffer
|
|
{
|
|
if( pli->currTuple == 0 )
|
|
{
|
|
qp->Request(args[0].addr, l);
|
|
if( qp->Received( args[0].addr ) )
|
|
{
|
|
pli->currTuple = (Tuple*)l.addr;
|
|
pli->rightIter = pli->rightRel->MakeScan();
|
|
}
|
|
else
|
|
{
|
|
pli->leftFinished = true;
|
|
if( pli->rightFinished )
|
|
return CANCEL;
|
|
else
|
|
{
|
|
pli->right = true;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have a tuple from the left stream in currTuple and
|
|
// an open iterator on the right stored buffer.
|
|
Tuple *rightTuple = pli->rightIter->GetNextTuple();
|
|
|
|
if( rightTuple == 0 )
|
|
// There are no more tuples in the right iterator. We then
|
|
// store the current tuple in the left buffer and close
|
|
// the right iterator.
|
|
{
|
|
if( !pli->rightFinished )
|
|
// We only need to keep track of the left tuples if the
|
|
// right stream is not finished.
|
|
{
|
|
pli->leftRel->AppendTuple( pli->currTuple );
|
|
pli->right = true;
|
|
}
|
|
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple = 0;
|
|
|
|
delete pli->rightIter;
|
|
pli->rightIter = 0;
|
|
|
|
continue; // Go back to the loop
|
|
}
|
|
else
|
|
// We match the tuples.
|
|
{
|
|
// XRIS: We must calculate the new attribute's values here
|
|
// and extend rightTuple
|
|
resultTuple = new Tuple( pli->resultTupleType );
|
|
Concat( pli->currTuple, rightTuple, resultTuple );
|
|
supplier = args[2].addr; // get list of ext-functions
|
|
nooffun = qp->GetNoSons(supplier); // get num of ext-functions
|
|
for(i = 0; i< nooffun; i++)
|
|
{
|
|
supplier2 = qp->GetSupplier(supplier, i); // get an ext-function
|
|
//noofsons = qp->GetNoSons(supplier2);
|
|
supplier3 = qp->GetSupplier(supplier2, 1);
|
|
extFunArgs = qp->Argument(supplier3);
|
|
((*extFunArgs)[0]).setAddr(pli->currTuple);// pass 1st argument
|
|
((*extFunArgs)[1]).setAddr(rightTuple); // pass 2nd argument
|
|
qp->Request(supplier3,value); // call extattr mapping
|
|
// The original implementation tried to avoid copying the function result,
|
|
// but somehow, this results in a strongly growing tuplebuffer on disk:
|
|
// resultTuple->PutAttribute(
|
|
// pli->currTuple->GetNoAttributes()
|
|
// + rightTuple->GetNoAttributes()
|
|
// + i, (Attribute*)value.addr);
|
|
// // extend effective left tuple
|
|
// qp->ReInitResultStorage( supplier3 );
|
|
|
|
resultTuple->PutAttribute(
|
|
pli->currTuple->GetNoAttributes()
|
|
+ rightTuple->GetNoAttributes()
|
|
+ i, ((Attribute*)value.addr)->Clone() );
|
|
// extend effective left tuple
|
|
}
|
|
rightTuple->DeleteIfAllowed();
|
|
rightTuple = 0;
|
|
result.setAddr( resultTuple );
|
|
return YIELD;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case CLOSE :
|
|
{
|
|
pli = (SymmProductExtendLocalInfo*)local.addr;
|
|
if(pli)
|
|
{
|
|
if( pli->currTuple != 0 )
|
|
pli->currTuple->DeleteIfAllowed();
|
|
|
|
delete pli->leftIter;
|
|
delete pli->rightIter;
|
|
if( pli->resultTupleType != 0 )
|
|
pli->resultTupleType->DeleteIfAllowed();
|
|
|
|
if( pli->rightRel != 0 )
|
|
{
|
|
pli->rightRel->Clear();
|
|
delete pli->rightRel;
|
|
}
|
|
|
|
if( pli->leftRel != 0 )
|
|
{
|
|
pli->leftRel->Clear();
|
|
delete pli->leftRel;
|
|
}
|
|
|
|
delete pli;
|
|
local.setAddr(0);
|
|
}
|
|
|
|
qp->Close(args[0].addr);
|
|
qp->Close(args[1].addr);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
5.10.5.3 Specification of operator ~symmproductextend~
|
|
|
|
*/
|
|
const string SymmProductExtendSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>(stream (tuple(X))) (stream (tuple(Y))) "
|
|
" [(z1, (tuple(X) tuple(Y) -> t1)) "
|
|
"... (zj, (tuple(X) tuple(Y) -> tj))] -> (stream (tuple(X*Y*Z))) "
|
|
"))</text--->"
|
|
"<text>_ _ symmproductextend[ funlist ]</text--->"
|
|
"<text>Computes a Cartesian product stream from "
|
|
"its two argument streams, extending it with "
|
|
"new attributes respecting mapping rules passed as "
|
|
"third argument.</text--->"
|
|
"<text>query ten feed {a} ten feed {b} "
|
|
"symmproductextend[ [prod: (.no_a * ..no_b)] ] "
|
|
"count</text--->"
|
|
" ) )";
|
|
|
|
/*
|
|
|
|
5.10.5.4 Definition of operator ~symmproductextend~
|
|
|
|
*/
|
|
Operator extrelsymmproductextend (
|
|
"symmproductextend", // name
|
|
SymmProductExtendSpec, // specification
|
|
SymmProductExtend, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SymmProductExtendTypeMap // type mapping
|
|
// true // needs large amounts of memory
|
|
);
|
|
|
|
|
|
/*
|
|
|
|
5.10.6 Operator ~symmproduct~
|
|
|
|
This operator calculates the cartesian product of two tuplestreams in
|
|
a symmetrical and non-blocking manner. It behaves like
|
|
|
|
---- _ _ symmjoin [ TRUE ]
|
|
----
|
|
but does not evaluate a predictate.
|
|
|
|
5.10.6.1 Typemapping for operator ~symmproduct~
|
|
|
|
----
|
|
(stream (tuple (A)))
|
|
x (stream (tuple (B)))
|
|
-> (stream (tuple(A*B)))
|
|
|
|
where A = (ta1 a1) ... (tan an)
|
|
B = (tb1 b1) ... (tbm bm)
|
|
----
|
|
|
|
*/
|
|
|
|
/*
|
|
Typemapping operator for operator ~symmproduct~
|
|
|
|
*/
|
|
|
|
ListExpr SymmProductTypeMap(ListExpr args)
|
|
{
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
ListExpr stream1 = nl->First(args);
|
|
ListExpr stream2 = nl->Second(args);
|
|
string err = "stream(tuple1) x stream(tuple2) expected";
|
|
if(!listutils::isTupleStream(stream1) ||
|
|
!listutils::isTupleStream(stream2)){
|
|
return listutils::typeError(err +
|
|
"(one of the arguments is not a tuple stream)");
|
|
}
|
|
ListExpr newAttrList = ConcatLists(nl->Second(nl->Second(stream1)),
|
|
nl->Second(nl->Second(stream2)));
|
|
if(!listutils::isAttrList(newAttrList)){
|
|
return listutils::typeError(err + "( name conflict )");
|
|
}
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()),
|
|
newAttrList));
|
|
|
|
}
|
|
|
|
/*
|
|
5.10.5.2 Value mapping function of operator ~symmproduct~
|
|
|
|
*/
|
|
|
|
struct SymmProductLocalInfo
|
|
{
|
|
TupleType *resultTupleType;
|
|
|
|
TupleBuffer *rightRel;
|
|
GenericRelationIterator *rightIter;
|
|
TupleBuffer *leftRel;
|
|
GenericRelationIterator *leftIter;
|
|
bool right;
|
|
Tuple *currTuple;
|
|
bool rightFinished;
|
|
bool leftFinished;
|
|
};
|
|
|
|
int
|
|
SymmProduct(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
Word r, l;
|
|
SymmProductLocalInfo* pli;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch (message)
|
|
{
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN :
|
|
{
|
|
long MAX_MEMORY = (qp->GetMemorySize(s) * 1024 * 1024);
|
|
cmsg.info("ERA:ShowMemInfo") << "SymmProduct.MAX_MEMORY ("
|
|
<< MAX_MEMORY/1024 << " kB): " << endl;
|
|
cmsg.send();
|
|
pli = new SymmProductLocalInfo;
|
|
pli->rightRel = new TupleBuffer( MAX_MEMORY / 2 );
|
|
pli->rightIter = 0;
|
|
pli->leftRel = new TupleBuffer( MAX_MEMORY / 2 );
|
|
pli->leftIter = 0;
|
|
pli->right = true;
|
|
pli->currTuple = 0;
|
|
pli->rightFinished = false;
|
|
pli->leftFinished = false;
|
|
|
|
tt->IncReference();
|
|
pli->resultTupleType = tt;
|
|
|
|
qp->Open(args[0].addr);
|
|
qp->Open(args[1].addr);
|
|
|
|
local.setAddr(pli);
|
|
return 0;
|
|
}
|
|
case REQUEST :
|
|
{
|
|
pli = (SymmProductLocalInfo*)local.addr;
|
|
|
|
while( 1 )
|
|
// This loop will end in some of the returns.
|
|
{
|
|
if( pli->right )
|
|
// Get the tuple from the right stream and match it with the
|
|
// left stored buffer
|
|
{
|
|
if( pli->currTuple == 0 )
|
|
{
|
|
qp->Request(args[1].addr, r);
|
|
if( qp->Received( args[1].addr ) )
|
|
{
|
|
pli->currTuple = (Tuple*)r.addr;
|
|
pli->leftIter = pli->leftRel->MakeScan();
|
|
}
|
|
else
|
|
{
|
|
pli->rightFinished = true;
|
|
if( pli->leftFinished )
|
|
return CANCEL;
|
|
else
|
|
{
|
|
pli->right = false;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have a tuple from the right stream in currTuple
|
|
// and an open iterator on the left stored buffer.
|
|
Tuple *leftTuple = pli->leftIter->GetNextTuple();
|
|
|
|
if( leftTuple == 0 )
|
|
// There are no more tuples in the left iterator. We then
|
|
// store the current tuple in the right buffer and close the
|
|
// left iterator.
|
|
{
|
|
if( !pli->leftFinished )
|
|
// We only need to keep track of the right tuples if the
|
|
// left stream is not finished.
|
|
{
|
|
pli->rightRel->AppendTuple( pli->currTuple );
|
|
pli->right = false;
|
|
}
|
|
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple = 0;
|
|
|
|
delete pli->leftIter;
|
|
pli->leftIter = 0;
|
|
|
|
continue; // Go back to the loop
|
|
}
|
|
else
|
|
// We match the tuples.
|
|
{
|
|
Tuple *resultTuple = new Tuple( pli->resultTupleType );
|
|
Concat( leftTuple, pli->currTuple, resultTuple );
|
|
leftTuple->DeleteIfAllowed();
|
|
leftTuple = 0;
|
|
result.setAddr( resultTuple );
|
|
return YIELD;
|
|
}
|
|
}
|
|
else
|
|
// Get the tuple from the left stream and match it with the
|
|
// right stored buffer
|
|
{
|
|
if( pli->currTuple == 0 )
|
|
{
|
|
qp->Request(args[0].addr, l);
|
|
if( qp->Received( args[0].addr ) )
|
|
{
|
|
pli->currTuple = (Tuple*)l.addr;
|
|
pli->rightIter = pli->rightRel->MakeScan();
|
|
}
|
|
else
|
|
{
|
|
pli->leftFinished = true;
|
|
if( pli->rightFinished )
|
|
return CANCEL;
|
|
else
|
|
{
|
|
pli->right = true;
|
|
continue; // Go back to the loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now we have a tuple from the left stream in currTuple and
|
|
// an open iterator on the right stored buffer.
|
|
Tuple *rightTuple = pli->rightIter->GetNextTuple();
|
|
|
|
if( rightTuple == 0 )
|
|
// There are no more tuples in the right iterator. We then
|
|
// store the current tuple in the left buffer and close
|
|
// the right iterator.
|
|
{
|
|
if( !pli->rightFinished )
|
|
// We only need to keep track of the left tuples if the
|
|
// right stream is not finished.
|
|
{
|
|
pli->leftRel->AppendTuple( pli->currTuple );
|
|
pli->right = true;
|
|
}
|
|
|
|
pli->currTuple->DeleteIfAllowed();
|
|
pli->currTuple = 0;
|
|
|
|
delete pli->rightIter;
|
|
pli->rightIter = 0;
|
|
|
|
continue; // Go back to the loop
|
|
}
|
|
else
|
|
// We match the tuples.
|
|
{
|
|
Tuple *resultTuple = new Tuple( pli->resultTupleType );
|
|
Concat( pli->currTuple, rightTuple, resultTuple );
|
|
rightTuple->DeleteIfAllowed();
|
|
rightTuple = 0;
|
|
result.setAddr( resultTuple );
|
|
return YIELD;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case CLOSE :
|
|
{
|
|
pli = (SymmProductLocalInfo*)local.addr;
|
|
if(pli)
|
|
{
|
|
if( pli->currTuple != 0 )
|
|
pli->currTuple->DeleteIfAllowed();
|
|
|
|
delete pli->leftIter;
|
|
delete pli->rightIter;
|
|
if( pli->resultTupleType != 0 )
|
|
pli->resultTupleType->DeleteIfAllowed();
|
|
|
|
if( pli->rightRel != 0 )
|
|
{
|
|
pli->rightRel->Clear();
|
|
delete pli->rightRel;
|
|
}
|
|
|
|
if( pli->leftRel != 0 )
|
|
{
|
|
pli->leftRel->Clear();
|
|
delete pli->leftRel;
|
|
}
|
|
|
|
delete pli;
|
|
local.setAddr(0);
|
|
}
|
|
|
|
qp->Close(args[0].addr);
|
|
qp->Close(args[1].addr);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
5.10.6.3 Specification of operator ~symmproduct~
|
|
|
|
*/
|
|
const string SymmProductSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>(stream (tuple(X))) (stream (tuple(Y))) "
|
|
" -> (stream (tuple(X*Y))) "
|
|
"))</text--->"
|
|
"<text>_ _ symmproduct</text--->"
|
|
"<text>Computes a Cartesian product stream from "
|
|
"its two argument streams, in a symmetrical and "
|
|
"non-blocking way.</text--->"
|
|
"<text>query ten feed {a} ten feed {b} "
|
|
"symmproduct count</text--->"
|
|
" ) )";
|
|
|
|
/*
|
|
|
|
5.10.6.4 Definition of operator ~symmproduct~
|
|
|
|
*/
|
|
Operator extrelsymmproduct (
|
|
"symmproduct", // name
|
|
SymmProductSpec, // specification
|
|
SymmProduct, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
SymmProductTypeMap // type mapping
|
|
// true // needs large amounts of memory
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
5.10.7 Operator ~addCounter~
|
|
|
|
5.10.7.1 Type Mapping function
|
|
|
|
This operator receives a tuple stream, an attribute name
|
|
as well as an initial value. It extends each tuple by
|
|
a counter initialized with the given value and the given name.
|
|
|
|
*/
|
|
ListExpr AddCounterTypeMap(ListExpr args){
|
|
|
|
if(nl->ListLength(args)!=3){
|
|
ErrorReporter::ReportError("three arguments required.");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr nameL = nl->Second(args);
|
|
ListExpr init = nl->Third(args);
|
|
|
|
// check init
|
|
if(!CcInt::checkType(init) && !LongInt::checkType(init) ){
|
|
return listutils::typeError("third argument must be of "
|
|
"type int or longint");
|
|
}
|
|
|
|
string errmsg;
|
|
if(!listutils::isValidAttributeName(nameL,errmsg)){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
string name = nl->SymbolValue(nameL);
|
|
|
|
// check streamlist
|
|
if(nl->ListLength(stream)!=2 ||
|
|
!nl->IsEqual(nl->First(stream),Symbol::STREAM())){
|
|
ErrorReporter::ReportError("first argument is not a stream");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr tuple = nl->Second(stream);
|
|
if(nl->ListLength(tuple)!=2 ||
|
|
!nl->IsEqual(nl->First(tuple),Tuple::BasicType())){
|
|
ErrorReporter::ReportError("first argument is not a tuple stream");
|
|
return nl->TypeError();
|
|
}
|
|
set<string> usednames;
|
|
ListExpr attributes = nl->Second(tuple);
|
|
if(nl->AtomType(attributes)!=NoAtom){
|
|
ErrorReporter::ReportError("invalid representation in tuple stream"
|
|
"(not a list)");
|
|
return nl->TypeError();
|
|
}
|
|
ListExpr last = nl->TheEmptyList();
|
|
while(!nl->IsEmpty(attributes)){
|
|
last = nl->First(attributes);
|
|
if(nl->ListLength(last)!=2){
|
|
ErrorReporter::ReportError("invalid representation in tuple stream"
|
|
" wrong listlength");
|
|
return nl->TypeError();
|
|
}
|
|
if(nl->AtomType(nl->First(last))!=SymbolType){
|
|
ErrorReporter::ReportError("invalid representation in tuple stream"
|
|
"(syntax of attribute name)");
|
|
return nl->TypeError();
|
|
}
|
|
usednames.insert(nl->SymbolValue(nl->First(last)));
|
|
attributes = nl->Rest(attributes);
|
|
}
|
|
|
|
if(usednames.find(name)!=usednames.end()){
|
|
ErrorReporter::ReportError("Name" + name +" is already used.");
|
|
return nl->TypeError();
|
|
}
|
|
|
|
// all fine, construct the result
|
|
|
|
if(nl->IsEmpty(last)){ // stream without attributes
|
|
return nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom(Tuple::BasicType()),
|
|
nl->OneElemList(
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom(name),
|
|
init))));
|
|
}
|
|
|
|
// make a copy of the attributes
|
|
attributes = nl->Second(tuple);
|
|
ListExpr reslist = nl->OneElemList(nl->First(attributes));
|
|
ListExpr lastlist = reslist;
|
|
attributes = nl->Rest(attributes);
|
|
while (!(nl->IsEmpty(attributes))) {
|
|
lastlist = nl->Append(lastlist,nl->First(attributes));
|
|
attributes = nl->Rest(attributes);
|
|
}
|
|
lastlist = nl->Append(lastlist,nl->TwoElemList(
|
|
nl->SymbolAtom(name),
|
|
init));
|
|
|
|
return nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList( nl->SymbolAtom(Tuple::BasicType()),
|
|
reslist));
|
|
|
|
}
|
|
|
|
/*
|
|
5.10.7.2 Value mapping of the ~addcounter~ operator
|
|
|
|
*/
|
|
template< class T>
|
|
class AddCounterLocalInfo{
|
|
public:
|
|
AddCounterLocalInfo(T* init, TupleType* tt){
|
|
defined = init->IsDefined();
|
|
value = *init;
|
|
tt->IncReference();
|
|
tupleType = tt;
|
|
}
|
|
~AddCounterLocalInfo(){
|
|
tupleType->DeleteIfAllowed();
|
|
tupleType = 0;
|
|
}
|
|
Tuple* createTuple(Tuple* orig){
|
|
if(!defined){
|
|
return 0;
|
|
}
|
|
Tuple* result = new Tuple(tupleType);
|
|
int size = orig->GetNoAttributes();
|
|
for(int i=0;i<size;i++){
|
|
result->CopyAttribute(i,orig,i);
|
|
}
|
|
result->PutAttribute(size, value.Clone());
|
|
value.Inc();
|
|
return result;
|
|
}
|
|
|
|
|
|
private:
|
|
bool defined;
|
|
T value;
|
|
TupleType* tupleType;
|
|
};
|
|
|
|
template<class T>
|
|
int AddCounterValueMap(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
Word orig;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch(message){
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH : {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN: {
|
|
T* Init = ((T*)args[2].addr);
|
|
qp->Open(args[0].addr);
|
|
local.addr = new AddCounterLocalInfo<T>(Init,tt);
|
|
break;
|
|
}
|
|
case REQUEST: {
|
|
qp->Request(args[0].addr,orig);
|
|
if(!qp->Received(args[0].addr)){
|
|
return CANCEL;
|
|
} else {
|
|
Tuple* tmp = ((AddCounterLocalInfo<T>*)local.addr)->
|
|
createTuple((Tuple*)orig.addr);
|
|
((Tuple*)orig.addr)->DeleteIfAllowed();
|
|
result.setAddr(tmp);
|
|
return YIELD;
|
|
}
|
|
|
|
}
|
|
case CLOSE: {
|
|
qp->Close(args[0].addr);
|
|
if(local.addr)
|
|
{
|
|
AddCounterLocalInfo<T>* acli = (AddCounterLocalInfo<T>*)local.addr;
|
|
delete acli;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: assert(false); // unknown message
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
5.10.7.3 Specification of operator ~addcounter~
|
|
|
|
*/
|
|
const string AddCounterSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>(stream (tuple(X))) x name x {int,longint} "
|
|
" -> (stream (tuple(X (name int)))) "
|
|
"))</text--->"
|
|
"<text> stream addcounter[AttrName, Initial] </text--->"
|
|
"<text>Adds a counter with the given name to the stream "
|
|
"starting at the initial value </text--->"
|
|
"<text>query ten feed addCounter[ Cnt , 1] consume</text--->"
|
|
" ) )";
|
|
|
|
/*
|
|
ValueMapping Array and SelectionFunction
|
|
|
|
*/
|
|
ValueMapping AddCounterVM[] = {
|
|
AddCounterValueMap<CcInt>,
|
|
AddCounterValueMap<LongInt>
|
|
};
|
|
|
|
int AddCounterSelect(ListExpr args){
|
|
return CcInt::checkType(nl->Third(args))?0:1;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
5.10.7.4 Definition of operator ~addcounter~
|
|
|
|
*/
|
|
Operator extreladdcounter (
|
|
"addcounter", // name
|
|
AddCounterSpec, // specification
|
|
2,
|
|
AddCounterVM, // value mapping
|
|
AddCounterSelect, // trivial selection function
|
|
AddCounterTypeMap // type mapping
|
|
);
|
|
|
|
|
|
struct printrefsInfo : OperatorInfo {
|
|
|
|
printrefsInfo()
|
|
{
|
|
name = "printrefs";
|
|
|
|
signature = "stream(tuple(...)) -> stream(tuple(...))";
|
|
syntax = "_ printrefs _";
|
|
meaning = "Prints out the values of the tuple's "
|
|
"and attribute's reference counter";
|
|
}
|
|
};
|
|
|
|
static ListExpr printrefs_tm(ListExpr args)
|
|
{
|
|
NList l(args);
|
|
|
|
static const string err1 = "printrefs expects stream(tuple(...))!";
|
|
|
|
if ( !l.hasLength(1) )
|
|
return l.typeError(err1);
|
|
|
|
NList attrs;
|
|
if ( l.checkStreamTuple( attrs ) )
|
|
return NList::typeError(err1);
|
|
|
|
return l.first().listExpr();
|
|
}
|
|
|
|
|
|
int printrefs_vm( Word* args, Word& result, int message,
|
|
Word& local, Supplier s)
|
|
{
|
|
Word w(Address(0));
|
|
|
|
switch (message)
|
|
{
|
|
case OPEN :
|
|
|
|
qp->Open(args[0].addr);
|
|
return 0;
|
|
|
|
case REQUEST :
|
|
|
|
qp->Request(args[0].addr, w);
|
|
if (qp->Received(args[0].addr))
|
|
{
|
|
Tuple* t = static_cast<Tuple*>( w.addr );
|
|
int tRefs = t->GetNumOfRefs();
|
|
cout << (void*)t << ": " << tRefs << "(";
|
|
for(int i = 0; i < t->GetNoAttributes(); i++)
|
|
{
|
|
cout << " " << t->GetAttribute(i)->NoRefs();
|
|
}
|
|
cout << " )" << endl;
|
|
|
|
result.addr = t;
|
|
return YIELD;
|
|
}
|
|
result.addr = 0;
|
|
return CANCEL;
|
|
|
|
case CLOSE :
|
|
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
2.113 Operator extend[_]aggr
|
|
|
|
This operator computes the aggregation of an attribute of a tuple stream.
|
|
Each provisonal result is appended to the original tuple.
|
|
|
|
|
|
2.113.1 Type Mapping
|
|
|
|
The Signature is:
|
|
|
|
tuplestream x attrName x newAttrName x fun
|
|
|
|
*/
|
|
|
|
ListExpr extend_aggrTM(ListExpr args){
|
|
|
|
string err = "stream(tuple(...)) x attr x newAttr x fun expected";
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr AttrNameList = nl->Second(args);
|
|
ListExpr FunList1 = nl->Third(args);
|
|
|
|
if(!nl->HasLength(FunList1,1)){ // only one function is allowed
|
|
return listutils::typeError(err + " (only one fun allowed)");
|
|
}
|
|
ListExpr FunList2 = nl->First(FunList1);
|
|
if(!nl->HasLength(FunList2,2)){
|
|
return listutils::typeError(err + " invalid function");
|
|
}
|
|
|
|
|
|
ListExpr NewAttrName = nl->First(FunList2);
|
|
ListExpr Fun = nl->Second(FunList2);
|
|
|
|
if(!Stream<Tuple>::checkType(stream)){
|
|
return listutils::typeError(err +
|
|
" (first arg is not a tuple stream) ");
|
|
}
|
|
|
|
int AttrNameListLength = nl->ListLength(AttrNameList);
|
|
if(AttrNameListLength!=1 && AttrNameListLength!=2){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr AttrName1 = nl->First(AttrNameList);
|
|
|
|
if(!listutils::isSymbol(AttrName1)){
|
|
return listutils::typeError(err +
|
|
" (second arg is not a valid attribute name ");
|
|
}
|
|
string errmsg;
|
|
if(!listutils::isValidAttributeName(NewAttrName,errmsg)){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
|
|
if(!listutils::isMap<2>(Fun)){
|
|
return listutils::typeError(err +
|
|
" (third arg is not a function");
|
|
}
|
|
ListExpr FunArgType1 = nl->Second(Fun);
|
|
ListExpr FunArgType2 = nl->Third(Fun);
|
|
ListExpr FunResType = nl->Fourth(Fun);
|
|
ListExpr AttrType;
|
|
string attrName1 = nl->SymbolValue(AttrName1);
|
|
|
|
ListExpr AttrList = nl->Second(nl->Second(stream));
|
|
|
|
int pos = listutils::findAttribute(AttrList, attrName1, AttrType);
|
|
if(pos==0){
|
|
return listutils::typeError("Attribute name " + attrName1 +
|
|
" not present in stream");
|
|
}
|
|
|
|
if(!nl->Equal(FunArgType1, AttrType)){
|
|
return listutils::typeError("Attribute type doesn't match "
|
|
"function argument type");
|
|
}
|
|
if(!nl->Equal(FunArgType2, AttrType)){
|
|
return listutils::typeError("Attribute type doesn't match "
|
|
"function argument type");
|
|
}
|
|
if(!nl->Equal(FunResType, AttrType)){
|
|
return listutils::typeError("Attribute type doesn't match "
|
|
"function result type");
|
|
}
|
|
|
|
ListExpr NewAttr = nl->OneElemList(nl->TwoElemList(NewAttrName, AttrType));
|
|
ListExpr ResAttrList = listutils::concat(AttrList, NewAttr);
|
|
|
|
if(!listutils::isAttrList(ResAttrList)){
|
|
return listutils::typeError("Attribute " + nl->ToString(NewAttr) +
|
|
" already present in stream");
|
|
}
|
|
|
|
ListExpr Res = nl->TwoElemList(
|
|
nl->SymbolAtom(Stream<Tuple>::BasicType()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom(Tuple::BasicType()),
|
|
ResAttrList)) ;
|
|
|
|
int resetPos = -1;
|
|
if(AttrNameListLength==2){
|
|
ListExpr AttrName2 = nl->Second(AttrNameList);
|
|
if(!listutils::isSymbol(AttrName2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
string attrName2=nl->SymbolValue(AttrName2);
|
|
ListExpr at;
|
|
int index = listutils::findAttribute(AttrList,attrName2, at);
|
|
if(index==0){
|
|
return listutils::typeError("Attributre " + attrName2 + " not found");
|
|
}
|
|
if(!CcBool::checkType(at)){
|
|
return listutils::typeError("Attrbute " + attrName2 + " not of type bool");
|
|
}
|
|
resetPos = index -1;
|
|
}
|
|
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->TwoElemList(nl->IntAtom(pos-1),
|
|
nl->IntAtom(resetPos)
|
|
),
|
|
Res);
|
|
}
|
|
|
|
|
|
|
|
class ExtendAggrInfo{
|
|
|
|
public:
|
|
ExtendAggrInfo(Word& _stream, Supplier _fun, int _attrPos,
|
|
int _resetPos, TupleType* type):
|
|
stream(_stream),first(true), fun(_fun), attrPos(_attrPos),
|
|
resetPos(_resetPos), value(0) {
|
|
tupleType = type;
|
|
tupleType->IncReference();
|
|
stream.open();
|
|
}
|
|
|
|
~ExtendAggrInfo(){
|
|
stream.close();
|
|
tupleType->DeleteIfAllowed();
|
|
if(value){
|
|
value->DeleteIfAllowed();
|
|
}
|
|
}
|
|
|
|
Tuple* getNextTuple(){
|
|
|
|
Tuple* srcTuple = stream.request();
|
|
if(srcTuple==0){
|
|
return 0;
|
|
}
|
|
|
|
Tuple* resTuple = new Tuple(tupleType);
|
|
// copy old attributes
|
|
for(int i=0;i<srcTuple->GetNoAttributes(); i++){
|
|
resTuple->CopyAttribute(i,srcTuple,i);
|
|
}
|
|
bool reset=false;
|
|
if(resetPos>=0){
|
|
CcBool* b = (CcBool*) srcTuple->GetAttribute(resetPos);
|
|
reset = b->IsDefined() && b->GetValue();
|
|
}
|
|
if(first){
|
|
value = srcTuple->GetAttribute(attrPos)->Clone();
|
|
first = false;
|
|
} else if(reset){
|
|
value->DeleteIfAllowed();
|
|
value = srcTuple->GetAttribute(attrPos)->Clone();
|
|
} else {
|
|
// compute value from current value and next attribute
|
|
Attribute* attr = srcTuple->GetAttribute(attrPos);
|
|
ArgVectorPointer funargs = qp->Argument(fun);
|
|
(*funargs)[0] = value;
|
|
(*funargs)[1] = attr;
|
|
Word funres;
|
|
qp->Request(fun,funres);
|
|
Attribute* victim = value;
|
|
value = ((Attribute*)funres.addr)->Copy();
|
|
victim->DeleteIfAllowed();
|
|
}
|
|
resTuple->PutAttribute(srcTuple->GetNoAttributes(), value->Clone());
|
|
srcTuple->DeleteIfAllowed();
|
|
return resTuple;
|
|
}
|
|
|
|
private:
|
|
Stream<Tuple> stream;
|
|
bool first;
|
|
Supplier fun;
|
|
int attrPos;
|
|
int resetPos;
|
|
Attribute* value;
|
|
TupleType* tupleType;
|
|
};
|
|
|
|
|
|
int
|
|
extend_aggrVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s1){
|
|
|
|
ExtendAggrInfo* li = (ExtendAggrInfo*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s1).addr;
|
|
|
|
switch(message){
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s1)));
|
|
qp->GetLocal2(s1).addr=tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s1).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN : {
|
|
if(li){
|
|
delete li;
|
|
}
|
|
int attrPos = ((CcInt*)args[3].addr)->GetValue();
|
|
int resetPos = ((CcInt*)args[4].addr)->GetValue();
|
|
Supplier s = args[2].addr;
|
|
Supplier s2 = qp->GetSupplier(s,0);
|
|
Supplier s3 = qp->GetSupplier(s2,1);
|
|
local.addr = new ExtendAggrInfo(args[0], s3, attrPos,
|
|
resetPos,
|
|
tt);
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if(!li){
|
|
return CANCEL;
|
|
}
|
|
result.addr = li->getNextTuple();
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const string extend_aggrSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(tuple(x)) x attrName x newAttrName x fun -> "
|
|
"stream(tuple(x @ newAttrName))</text--->"
|
|
"<text>stream extend_aggr [ attrName ; newAttrName : fun ] )</text--->"
|
|
"<text>Compute an aggrgation over an attribute of a tuple and stored all "
|
|
"provisional results into the"
|
|
" result tuples"
|
|
"</text--->"
|
|
"<text>query ten feed extend_aggr[no; aNo : "
|
|
"fun(i1: int, i2: int) i1 + i2 ] </text--->"
|
|
") )";
|
|
|
|
|
|
|
|
/*
|
|
2.113.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator extend_aggr(
|
|
"extend_aggr",
|
|
extend_aggrSpec,
|
|
extend_aggrVM,
|
|
Operator::SimpleSelect,
|
|
extend_aggrTM);
|
|
|
|
|
|
|
|
/*
|
|
2.114 Operator extend[_]last
|
|
|
|
This operator extends a tuple using values from that tuple and from
|
|
values of the last tuple from the stream. For the first tuple, the
|
|
values are given in a separate list.
|
|
|
|
|
|
2.114.1 Type Mapping
|
|
|
|
signature is stream(tuple) x funlist x list
|
|
|
|
|
|
*/
|
|
ListExpr extend_lastTM(ListExpr args){
|
|
string err = "stream(tuple) x funlist with defaults expected";
|
|
if(!nl->HasLength(args, 2)){
|
|
return listutils::typeError(err + " (wrong number of arguments)");
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr fundeflist = nl->Second(args);
|
|
|
|
if(!Stream<Tuple>::checkType(stream)){
|
|
return listutils::typeError(err + " (first arg is not a tuple stream");
|
|
}
|
|
if(nl->AtomType(fundeflist)!=NoAtom){
|
|
return listutils::typeError("secondo argument is not a function list");
|
|
}
|
|
if(nl->IsEmpty(fundeflist)){
|
|
return listutils::typeError(err + " (funlist is empty)");
|
|
}
|
|
|
|
ListExpr TupleList = nl->Second(stream);
|
|
ListExpr attrList = nl->Second(TupleList);
|
|
ListExpr ExtList = nl->TheEmptyList();
|
|
ListExpr last = nl->TheEmptyList();
|
|
bool first = true;
|
|
|
|
|
|
while(!nl->IsEmpty(fundeflist)){
|
|
ListExpr firstFun = nl->First(fundeflist);
|
|
fundeflist = nl->Rest(fundeflist);
|
|
if(!nl->HasLength(firstFun,3)){ // (name map default)
|
|
return listutils::typeError(err + " (Invalid named function)");
|
|
}
|
|
|
|
ListExpr newName = nl->First(firstFun);
|
|
|
|
string error;
|
|
if(!listutils::isValidAttributeName(newName, error)){
|
|
return listutils::typeError(error);
|
|
}
|
|
|
|
ListExpr mapList = nl->Second(firstFun);
|
|
if(!listutils::isMap<2>(mapList)){
|
|
return listutils::typeError(err + " (invalid map found)");
|
|
}
|
|
if(!nl->Equal(nl->Second(mapList),TupleList) ||
|
|
!nl->Equal(nl->Third(mapList),TupleList)){
|
|
return listutils::typeError(err + " ( invalid function arguments )");
|
|
}
|
|
ListExpr resType = nl->Fourth(mapList);
|
|
ListExpr defType = nl->Third(firstFun);
|
|
if(!nl->Equal(resType,defType)){
|
|
return listutils::typeError(err + " (type mismatch between fun "
|
|
"result and default)");
|
|
}
|
|
ListExpr dummy;
|
|
string name = nl->SymbolValue(newName);
|
|
if(listutils::findAttribute(attrList,name,dummy)>0){
|
|
return listutils::typeError(err + " ( attribute " + name +
|
|
" already exists");
|
|
}
|
|
ListExpr ext = nl->TwoElemList( newName, resType);
|
|
if(first){
|
|
ExtList = nl->OneElemList(ext);
|
|
last = ExtList;
|
|
first = false;
|
|
} else {
|
|
last = nl->Append(last, ext);
|
|
}
|
|
}
|
|
|
|
ListExpr resAttrList = listutils::concat(attrList, ExtList);
|
|
return nl->TwoElemList(
|
|
nl->SymbolAtom(Stream<Tuple>::BasicType()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom(Tuple::BasicType()),
|
|
resAttrList));
|
|
}
|
|
|
|
|
|
/*
|
|
2.114.2 Value Mapping
|
|
|
|
*/
|
|
class ExtendLastInfo{
|
|
public:
|
|
ExtendLastInfo(Word& _stream, Supplier _s1,
|
|
TupleType* resType):
|
|
stream(_stream), s1(_s1), first(true), lastTuple(0){
|
|
tupleType = resType;
|
|
tupleType->IncReference();
|
|
stream.open();
|
|
}
|
|
~ExtendLastInfo(){
|
|
stream.close();
|
|
tupleType->DeleteIfAllowed();
|
|
if(lastTuple){
|
|
lastTuple->DeleteIfAllowed();
|
|
}
|
|
}
|
|
|
|
Tuple* getNextTuple(){
|
|
|
|
Tuple* srcTuple = stream.request();
|
|
if(srcTuple==0){
|
|
return 0;
|
|
}
|
|
Tuple* resTuple = new Tuple(tupleType);
|
|
// copy the original attributesA
|
|
int no_attr = srcTuple->GetNoAttributes();
|
|
for(int i=0;i<no_attr; i++){
|
|
resTuple->CopyAttribute(i,srcTuple,i);
|
|
}
|
|
|
|
if(first){
|
|
// copy default values
|
|
int noDefaults = qp->GetNoSons(s1);
|
|
for(int i=0;i<noDefaults;i++){
|
|
Supplier s = qp->GetSon(qp->GetSon(s1,i),2);
|
|
Word res;
|
|
qp->Request(s,res);
|
|
Attribute* resAttr = (Attribute*) res.addr;
|
|
resTuple->PutAttribute(i + no_attr, resAttr->Clone());
|
|
}
|
|
first = false;
|
|
lastTuple = srcTuple;
|
|
} else { // lastTuple is present, use functions
|
|
int noFuns = qp->GetNoSons(s1);
|
|
for(int i=0; i < noFuns; i++){
|
|
Supplier fun1 = qp->GetSon(s1,i); // (Name Function default)
|
|
|
|
Supplier fun = qp->GetSon(fun1,1); // get function
|
|
ArgVectorPointer funargs = qp->Argument(fun);
|
|
(*funargs)[0] = srcTuple;
|
|
(*funargs)[1] = lastTuple;
|
|
Word funres;
|
|
qp->Request(fun, funres);
|
|
resTuple->PutAttribute(i+no_attr,
|
|
((Attribute*) funres.addr)->Clone());
|
|
}
|
|
lastTuple->DeleteIfAllowed();
|
|
lastTuple = srcTuple;
|
|
}
|
|
return resTuple;
|
|
}
|
|
|
|
private:
|
|
Stream<Tuple> stream;
|
|
Supplier s1;
|
|
bool first;
|
|
TupleType* tupleType;
|
|
Tuple* lastTuple;
|
|
};
|
|
|
|
|
|
|
|
template<class T>
|
|
int extend_lastVMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s1){
|
|
|
|
T* li = (T*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s1).addr;
|
|
|
|
switch(message){
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s1)));
|
|
qp->GetLocal2(s1).addr = tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s1).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN : {
|
|
if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new T(args[0], args[1].addr, tt);
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if(!li){
|
|
return CANCEL;
|
|
}
|
|
result.addr = li->getNextTuple();
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
|
|
const string extend_lastSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(tuple(x)) x funlist x list -> "
|
|
"stream(tuple(x @ newAttributes))</text--->"
|
|
"<text>stream extend_last [ funlist ; list ] )</text--->"
|
|
"<text>Computes new attributes from the current tuple and the"
|
|
" previous one in the stream. The current tuple can be accessed using"
|
|
" the dot(.). Attribute values from the previous tuple can be accessed "
|
|
" by ..\n"
|
|
" For the first tuple, no previous tuple is available. For this reason,"
|
|
" the default value from the list is usied instead of the funtion."
|
|
"</text--->"
|
|
"<text>query ten feed extend_last[ ANum : .no - ..no ; 3] tconsume "
|
|
" </text--->"
|
|
") )";
|
|
|
|
|
|
|
|
/*
|
|
2.114.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator extend_last(
|
|
"extend_last",
|
|
extend_lastSpec,
|
|
extend_lastVMT<ExtendLastInfo>,
|
|
Operator::SimpleSelect,
|
|
extend_lastTM);
|
|
|
|
|
|
/*
|
|
2.116 Operator extend\_next
|
|
|
|
|
|
2.116.1 Type Mapping
|
|
|
|
The Type mapping is the same as for the extend[_]last operator.
|
|
|
|
*/
|
|
|
|
|
|
class ExtendNextInfo{
|
|
public:
|
|
ExtendNextInfo(Word& _stream, Supplier _s1,
|
|
TupleType* resType):
|
|
stream(_stream), s1(_s1), lastTuple(0){
|
|
tupleType = resType;
|
|
tupleType->IncReference();
|
|
stream.open();
|
|
}
|
|
~ExtendNextInfo(){
|
|
stream.close();
|
|
tupleType->DeleteIfAllowed();
|
|
if(lastTuple){
|
|
lastTuple->DeleteIfAllowed();
|
|
}
|
|
}
|
|
|
|
Tuple* getNextTuple(){
|
|
|
|
Tuple* srcTuple = stream.request();
|
|
if(srcTuple==0){
|
|
if(lastTuple){ // copy default values
|
|
int no_attr = lastTuple->GetNoAttributes();
|
|
Tuple* resTuple = getResTuple(lastTuple);
|
|
lastTuple->DeleteIfAllowed();
|
|
lastTuple = 0;
|
|
int noDefaults = qp->GetNoSons(s1);
|
|
for(int i=0;i<noDefaults;i++){
|
|
Supplier s = qp->GetSon(qp->GetSon(s1,i),2);
|
|
Word res;
|
|
qp->Request(s,res);
|
|
Attribute* resAttr = (Attribute*) res.addr;
|
|
resTuple->PutAttribute(i + no_attr, resAttr->Clone());
|
|
}
|
|
return resTuple;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if(!lastTuple){ // it's the first tuple in stream
|
|
lastTuple = srcTuple;
|
|
return getNextTuple();
|
|
}
|
|
|
|
|
|
int no_attr = lastTuple->GetNoAttributes();
|
|
Tuple* resTuple= getResTuple(lastTuple);
|
|
int noFuns = qp->GetNoSons(s1);
|
|
for(int i=0; i < noFuns; i++){
|
|
Supplier fun1 = qp->GetSon(s1,i); // (Name Function default)
|
|
Supplier fun = qp->GetSon(fun1,1); // get function
|
|
ArgVectorPointer funargs = qp->Argument(fun);
|
|
(*funargs)[0] = lastTuple;
|
|
(*funargs)[1] = srcTuple;
|
|
Word funres;
|
|
qp->Request(fun, funres);
|
|
resTuple->PutAttribute(i+no_attr,
|
|
((Attribute*) funres.addr)->Clone());
|
|
}
|
|
lastTuple->DeleteIfAllowed();
|
|
lastTuple = srcTuple;
|
|
return resTuple;
|
|
}
|
|
|
|
|
|
private:
|
|
Stream<Tuple> stream;
|
|
Supplier s1;
|
|
TupleType* tupleType;
|
|
Tuple* lastTuple;
|
|
|
|
// creates a new tuple and copies the content of srctuple
|
|
// to the first attributes of the result
|
|
Tuple* getResTuple(Tuple* srcTuple){
|
|
Tuple* resTuple = new Tuple(tupleType);
|
|
// copy the original attributesA
|
|
int no_attr = srcTuple->GetNoAttributes();
|
|
for(int i=0;i<no_attr; i++){
|
|
resTuple->CopyAttribute(i,srcTuple,i);
|
|
}
|
|
return resTuple;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const string extend_nextSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(tuple(x)) x funlist x list -> "
|
|
"stream(tuple(x @ newAttributes))</text--->"
|
|
"<text>stream extend_next [ funlist ; list ] )</text--->"
|
|
"<text>Computes new attributes from the current tuple and the"
|
|
" nexxt one in the stream. The current tuple can be accessed using"
|
|
" the dot(.). Attribute values from the next tuple can be accessed "
|
|
" by ..\n"
|
|
" For the last tuple, no next tuple is available. For this reason,"
|
|
" the default value from the list is used instead of the funtion."
|
|
"</text--->"
|
|
"<text>query ten feed extend_next[ NNum : .no - ..no ; 3] tconsume "
|
|
" </text--->"
|
|
") )";
|
|
|
|
|
|
|
|
/*
|
|
2.114.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator extend_next(
|
|
"extend_next",
|
|
extend_nextSpec,
|
|
extend_lastVMT<ExtendNextInfo>,
|
|
Operator::SimpleSelect,
|
|
extend_lastTM);
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
2.115 Operator ~toFields~
|
|
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
2.115.1 Type mapping for ~toFields~ is
|
|
(stream(tuple(a1:d1, ..., an:dn))) x (aj)
|
|
-> (stream(tuple(aj:dj, Field: string, Type: text, Value:text)))
|
|
|
|
*/
|
|
ListExpr toFieldsType( ListExpr args ) {
|
|
if ( !nl->HasLength( args, 2 ) ) {
|
|
return listutils::typeError( "two arguments expected" );
|
|
}
|
|
if ( !Stream<Tuple>::checkType( nl->First( args ) ) ) {
|
|
return listutils::typeError( "first argument must be a stream of tuples" );
|
|
}
|
|
if ( !listutils::isSymbol( nl->Second( args )) ) {
|
|
return listutils::typeError( "second argument must be an attribute name" );
|
|
}
|
|
ListExpr attrlist = nl->Second(nl->Second(nl->First( args )));
|
|
string attrname = nl->SymbolValue(nl->Second( args ));
|
|
ListExpr type;
|
|
int attrpos = listutils::findAttribute(attrlist, attrname, type);
|
|
if ( !attrpos ) {
|
|
return listutils::typeError( "attribute " + attrname + " does not exist" );
|
|
}
|
|
ListExpr resultattrlist = nl->FourElemList(
|
|
nl->TwoElemList( nl->SymbolAtom( attrname ),
|
|
type ),
|
|
nl->TwoElemList( nl->SymbolAtom( "Field" ),
|
|
nl->SymbolAtom( CcString::BasicType() ) ),
|
|
nl->TwoElemList( nl->SymbolAtom( "Type" ),
|
|
nl->SymbolAtom( FText::BasicType() ) ),
|
|
nl->TwoElemList( nl->SymbolAtom( "Value" ),
|
|
nl->SymbolAtom( FText::BasicType() ) ) );
|
|
ListExpr resultlist = nl->TwoElemList(
|
|
nl->SymbolAtom( Stream<Tuple>::BasicType() ),
|
|
nl->TwoElemList( nl->SymbolAtom( Tuple::BasicType() ),
|
|
resultattrlist ) );
|
|
|
|
ListExpr attrs = nl->OneElemList( nl->StringAtom( nl->SymbolValue
|
|
( nl->First( nl->First( attrlist ) ) ) ) );
|
|
ListExpr lastAttr = attrs;
|
|
ListExpr types = nl->OneElemList( nl->TextAtom( nl->SymbolValue
|
|
( nl->Second( nl->First( attrlist ) ) ) ) );
|
|
ListExpr lastType = types;
|
|
attrlist = nl->Rest( attrlist );
|
|
while ( !nl->IsEmpty( attrlist ) ) {
|
|
ListExpr first = nl->First( attrlist );
|
|
ListExpr firstfirst = nl->First( first );
|
|
ListExpr firstsecond = nl->Second( first );
|
|
lastAttr = nl->Append( lastAttr, nl->StringAtom
|
|
( nl->SymbolValue( firstfirst ) ) );
|
|
lastType = nl->Append( lastType, nl->TextAtom
|
|
( nl->ToString( firstsecond ) ) );
|
|
attrlist = nl->Rest( attrlist ); // Iteration
|
|
}
|
|
|
|
ListExpr infolist1 = nl->OneElemList( nl->IntAtom( attrpos ) );
|
|
ListExpr infolist2 = listutils::concat(infolist1,attrs);
|
|
ListExpr infolist = listutils::concat( infolist2, types );
|
|
|
|
ListExpr result = nl->ThreeElemList( nl->SymbolAtom( "APPEND" ),
|
|
infolist,
|
|
resultlist );
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
|
|
2.155.2 Class ~ToFieldsInfo~
|
|
|
|
*/
|
|
|
|
class ToFieldsInfo {
|
|
public:
|
|
ToFieldsInfo( Word& is, vector<string> &fields1, vector<string> &types1,
|
|
int position, TupleType* _tt );
|
|
~ToFieldsInfo();
|
|
Tuple* nextTuple();
|
|
private:
|
|
Stream<Tuple> stream;
|
|
Tuple* tuple;
|
|
unsigned int pos;
|
|
int keypos;
|
|
vector<string> fields, types;
|
|
TupleType* tt;
|
|
int algid, typid;
|
|
bool istype;
|
|
vector<OutObject> outfuns;
|
|
vector<ListExpr> typelists;
|
|
ListExpr typelist;
|
|
};
|
|
|
|
ToFieldsInfo::ToFieldsInfo( Word& is, vector<string> &fields1,
|
|
vector<string> &types1, int position,
|
|
TupleType* _tt):
|
|
stream( is ), tuple( 0 ), pos(0), keypos( position ), fields( fields1 ),
|
|
types(types1) {
|
|
stream.open();
|
|
tuple = stream.request();
|
|
tt = _tt;
|
|
tt->IncReference();
|
|
SecondoCatalog* sc = SecondoSystem::GetCatalog();
|
|
AlgebraManager* am = SecondoSystem::GetAlgebraManager();
|
|
for ( int i = 0; i < tuple->GetNoAttributes(); i++ ) {
|
|
// check whether types[ i ] is an atomic type. Otherwise cut the nested list
|
|
bool listok = nl->ReadFromString( types[ i ], typelist );
|
|
while ( !nl->IsAtom( typelist ) ) {
|
|
typelist = nl->First( typelist );
|
|
}
|
|
istype = sc->GetTypeId( nl->ToString( typelist ), algid, typid );
|
|
// get and save the Out functions
|
|
// depending on algebra id and type id of the attributes
|
|
outfuns.push_back( am->OutObj( algid, typid ) );
|
|
listok = nl->ReadFromString( types[ i ], typelist );
|
|
if ( !listok ) {
|
|
cout << "unknown type" << endl;
|
|
}
|
|
typelists.push_back( typelist );
|
|
}
|
|
}
|
|
|
|
ToFieldsInfo::~ToFieldsInfo() {
|
|
if ( tuple ) {
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
stream.close();
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple* ToFieldsInfo::nextTuple() {
|
|
if ( !tuple ) {
|
|
return 0;
|
|
}
|
|
if ( pos == fields.size() ) { // tuple finished
|
|
tuple->DeleteIfAllowed();
|
|
tuple = stream.request();
|
|
pos = 0;
|
|
if ( !tuple ) { // last tuple finished
|
|
return 0;
|
|
}
|
|
}
|
|
Tuple* result = new Tuple( tt );
|
|
// set 1st entry to key attribute
|
|
result->PutAttribute( 0, tuple->GetAttribute(keypos-1)->Copy() );
|
|
// set 2nd entry to field name
|
|
result->PutAttribute( 1, new CcString( true, fields[ pos ] ) );
|
|
// set 3rd entry to attribute type
|
|
result->PutAttribute( 2, new FText( true, types[ pos ] ) );
|
|
string value;
|
|
Attribute* attrvalue = tuple->GetAttribute( pos );
|
|
if ( attrvalue->hasTextRepresentation() ) {
|
|
value = attrvalue->toText();
|
|
}
|
|
else {
|
|
// call the Out function if necessary
|
|
ListExpr output = outfuns[ pos ]( typelists[ pos ], attrvalue );
|
|
value = nl->ToString( output );
|
|
}
|
|
// set 4th entry to attribute value
|
|
result->PutAttribute( 3, new FText( true, value ) );
|
|
pos++; // proceed through input tuple
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
|
|
2.155.3 Value mapping for ~toFields~
|
|
|
|
*/
|
|
|
|
int toFieldsFun ( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
ToFieldsInfo* tfi = (ToFieldsInfo*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch( message ) {
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN: {
|
|
if ( tfi ) {
|
|
delete tfi;
|
|
}
|
|
int pos = ( (CcInt*)args[ 2 ].addr )->GetIntval();
|
|
vector<string> fields, types;
|
|
int n = qp->GetNoSons( s );
|
|
// there are 2*n+3 arguments
|
|
int noattrs = ( n-3 ) / 2;
|
|
for ( int i = 0; i < noattrs; i++ ) {
|
|
fields.push_back( ( (CcString*)args[ i + 3 ].addr )->GetValue() );
|
|
types.push_back( ( (FText*)args[ i + noattrs + 3 ].addr )->GetValue() );
|
|
}
|
|
local.addr = new ToFieldsInfo( args[ 0 ], fields, types, pos, tt);
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if ( !tfi ) {
|
|
return CANCEL;
|
|
}
|
|
result.addr = tfi->nextTuple();
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if ( tfi ) {
|
|
delete tfi;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
2.155.4 Description for operator ~toFields~
|
|
|
|
*/
|
|
|
|
struct toFieldsInfo : OperatorInfo {
|
|
|
|
toFieldsInfo() : OperatorInfo() {
|
|
|
|
name = "toFields";
|
|
signature = "((stream (tuple([a1:d1, ...,an:dn]))) x ai) -> "
|
|
"stream(tuple([ai, attrname, dj, aj]))";
|
|
syntax = "_ feed toFields [ _ ]";
|
|
meaning = "Decomposes a stream of tuples into its items.";
|
|
}
|
|
|
|
};
|
|
|
|
/*
|
|
|
|
|
|
2.116 Operator ~fromFields~
|
|
inverts the operator toFields
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
2.116.1 Type mapping for ~fromFields~ is
|
|
(stream(tuple(keyvalue: keytype, Field: string, Type: text, Value: text)))
|
|
x (relation(X)) -> (stream(X))
|
|
|
|
*/
|
|
|
|
ListExpr fromFieldsType( ListExpr args ) {
|
|
if ( !nl->HasLength( args, 2 ) ) {
|
|
return listutils::typeError( "two arguments expected" );
|
|
}
|
|
if ( !Stream<Tuple>::checkType( nl->First( args ) ) ) {
|
|
return listutils::typeError( "first argument must be a stream of tuples" );
|
|
}
|
|
if ( !listutils::isRelDescription( nl->Second( args ) ) ) {
|
|
return listutils::typeError( "second argument must be a relation" );
|
|
}
|
|
ListExpr attrlist = nl->Second( nl->Second( nl->First( args ) ) );
|
|
string error = "stream must contain tuples of type"
|
|
"((x y) (Field string) (Value text)) [in arbitrary order]";
|
|
// retrieve the input tuple positions of key, field and value, ignore type
|
|
int posV = -1;
|
|
int posK = -1;
|
|
int posF = -1;
|
|
int pos = 0;
|
|
while ( !nl->IsEmpty( attrlist ) ) {
|
|
ListExpr attr = nl->First( attrlist );
|
|
attrlist = nl->Rest( attrlist );
|
|
if ( CcString::checkType( nl->Second( attr ) )
|
|
&& ( listutils::isSymbol( nl->First( attr ), "Field" ) ) ) {
|
|
posF = pos;
|
|
}
|
|
else if ( FText::checkType( nl->Second( attr ) )
|
|
&& ( listutils::isSymbol( nl->First( attr ), "Value" ) ) ) {
|
|
posV = pos;
|
|
}
|
|
else if ( !listutils::isSymbol( nl->First( attr ), "Type" )
|
|
&& ( posK < 0 ) ) {
|
|
posK = pos;
|
|
}
|
|
pos++;
|
|
}
|
|
// value, field or key were not found
|
|
if ( posV == -1 || posK == -1 || posF == -1 ) {
|
|
return listutils::typeError( error );
|
|
}
|
|
ListExpr tuple = nl->Second( nl->Second( args ) );
|
|
ListExpr result = nl->TwoElemList( nl->SymbolAtom
|
|
( Stream<Tuple>::BasicType() ) , tuple );
|
|
return nl->ThreeElemList( nl->SymbolAtom( "APPEND" ),
|
|
nl->ThreeElemList( nl->IntAtom( posF ),
|
|
nl->IntAtom( posK ),
|
|
nl->IntAtom( posV ) ),
|
|
result );
|
|
}
|
|
/*
|
|
|
|
2.116.2 Class ~fromFieldsInfo~
|
|
|
|
*/
|
|
|
|
class FromFieldsInfo {
|
|
private:
|
|
Stream<Tuple> stream;
|
|
Tuple* tuple;
|
|
Tuple* inTuple;
|
|
TupleType* tt;
|
|
vector<InObject> infuns;
|
|
vector<ObjectCreation> createfuns;
|
|
map<string,int> field2pos;
|
|
map<string,int>::iterator attrPos;
|
|
int attrMax; // number of attributes in every complete output tuple
|
|
Attribute* key;
|
|
vector<ListExpr> typelists;
|
|
int posF, posK, posV; // input tuple positions of Field, Key and Value
|
|
|
|
pair<int, int> getMainType( ListExpr type ){
|
|
while( !( nl->IsAtom( nl->First( type ) ) &&
|
|
( nl->AtomType( nl->First( type ) == IntType ) ) ) ) {
|
|
type = nl->First( type );
|
|
}
|
|
pair<int, int> result( nl->IntValue( nl->First( type ) ),
|
|
nl->IntValue( nl->Second( type ) ) );
|
|
return result;
|
|
}
|
|
|
|
public:
|
|
FromFieldsInfo( Word& is, TupleType* _tt , int pF, int pK, int pV,
|
|
Supplier s ):
|
|
stream( is ), tuple( 0 ), posF( pF ), posK( pK ), posV( pV ) {
|
|
inTuple = 0;
|
|
key = 0;
|
|
stream.open();
|
|
tt = _tt;
|
|
tt->IncReference();
|
|
AlgebraManager* am = SecondoSystem::GetAlgebraManager();
|
|
ListExpr tl = nl->Second(GetTupleResultType(s));
|
|
ListExpr attrList = nl->Second( tl );
|
|
int pos = -1;
|
|
while( !nl->IsEmpty( attrList ) ) {
|
|
pos++;
|
|
ListExpr attr = nl->First( attrList );
|
|
attrList = nl->Rest( attrList );
|
|
string field = nl->SymbolValue( nl->First( attr ) );
|
|
field2pos[field] = pos; // assign each attribute name to its position
|
|
pair<int, int> t = getMainType( nl->Second( attr ) );
|
|
// collect In functions, typelists and Create functions
|
|
infuns.push_back( am->InObj( t.first, t.second ) );
|
|
typelists.push_back( nl->Second( attr ) );
|
|
createfuns.push_back( am->CreateObj( t.first, t.second ) );
|
|
}
|
|
pos++;
|
|
attrMax = pos;
|
|
}
|
|
|
|
~FromFieldsInfo(){
|
|
if ( tuple ) {
|
|
tuple->DeleteIfAllowed();
|
|
}
|
|
stream.close();
|
|
tt->DeleteIfAllowed();
|
|
if ( key ) {
|
|
key->DeleteIfAllowed();
|
|
}
|
|
}
|
|
|
|
// this function converts an inTuple into a tuple attribute
|
|
bool processTuple( Tuple* inTuple, Tuple* tuple ){
|
|
CcString* fieldCc = ( CcString* ) inTuple->GetAttribute( posF );
|
|
if ( fieldCc->IsDefined() ) {
|
|
attrPos = field2pos.find( fieldCc->GetValue() );
|
|
if( attrPos != field2pos.end() ) {
|
|
FText* attrvalue = (FText*) inTuple->GetAttribute( posV );
|
|
string valuestr;
|
|
if ( attrvalue->IsDefined() ) {
|
|
valuestr = attrvalue->GetValue();
|
|
}
|
|
// call Create function depending on attribute
|
|
Word creation = createfuns[ attrPos->second ]
|
|
( typelists[ attrPos->second ] );
|
|
Attribute* newAttr = (Attribute*)creation.addr;
|
|
if ( newAttr->hasTextRepresentation() ) {
|
|
bool fromtextok = newAttr->fromText( valuestr );
|
|
if ( !fromtextok ) {
|
|
cout << "string could not be read" << endl;
|
|
newAttr->SetDefined(false);
|
|
}
|
|
}
|
|
else { // In function necessary
|
|
ListExpr instance; // needed to call the In function
|
|
bool instanceok = nl->ReadFromString( valuestr, instance );
|
|
if( !instanceok ) {
|
|
cout << "string could not be read" << endl;
|
|
newAttr->SetDefined(false);
|
|
}
|
|
else {
|
|
int errorPos = 0;
|
|
ListExpr errorInfo;
|
|
bool correct;
|
|
Word in = infuns[ attrPos->second ] // call In function
|
|
( typelists[ attrPos->second ],
|
|
instance, errorPos, errorInfo, correct );
|
|
if ( correct ) {
|
|
newAttr->DeleteIfAllowed();
|
|
newAttr = ( Attribute* )in.addr;
|
|
}
|
|
else {
|
|
cout << "In function failed" << endl;
|
|
newAttr->SetDefined(false);
|
|
}
|
|
}
|
|
}
|
|
// attribute already set -> overwrite
|
|
if ( tuple->GetAttribute( attrPos->second ) != 0 ) {
|
|
cout << "attribute " ;
|
|
inTuple->GetAttribute( posF )->Print(cout) <<
|
|
" was already defined for key " << key <<
|
|
" and is now overwritten" << endl;
|
|
// delete old attribute value
|
|
tuple->GetAttribute( attrPos->second )->DeleteIfAllowed();
|
|
}
|
|
tuple->PutAttribute( attrPos->second, newAttr );
|
|
}
|
|
else { // attribute does not exist -> input tuple is ignored
|
|
cout << "attribute ";
|
|
inTuple->GetAttribute( posF )->Print(cout) <<
|
|
" does not exist" << endl;
|
|
return false;
|
|
}
|
|
}
|
|
else { // undefined attribute -> input tuple is ignored
|
|
cout << "undefined attribute " << endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// fills the output tuple with undefined values if necessary
|
|
bool fillTuple( Tuple* tuple ){
|
|
for ( int pos = 0; pos < attrMax; pos++ ) {
|
|
if ( !tuple->GetAttribute( pos ) ) {
|
|
Word creation = createfuns[ pos ]( typelists[ pos ] );
|
|
Attribute* emptyAttr = ( Attribute* )creation.addr;
|
|
emptyAttr->SetDefined( false );
|
|
tuple->PutAttribute( pos, emptyAttr );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Tuple* nextTuple() {
|
|
Tuple* tuple = new Tuple( tt );
|
|
int attrsStored = 0; // #attributes currently stored in the output tuple
|
|
while ( attrsStored < attrMax ) {
|
|
if ( !inTuple ) {
|
|
inTuple = stream.request();
|
|
}
|
|
// not enough attributes for output tuple -> fill
|
|
if ( ( !inTuple && attrsStored < attrMax - 1 && attrsStored > 0 ) ) {
|
|
bool filled = fillTuple( tuple );
|
|
if ( filled ) {
|
|
cout << "not enough attributes for key " << key->toText()
|
|
<< ", tuple filled with undefined values" << endl;
|
|
return tuple;
|
|
}
|
|
else {
|
|
tuple->DeleteIfAllowed();
|
|
return 0;
|
|
}
|
|
}
|
|
if ( !inTuple ) {
|
|
tuple->DeleteIfAllowed();
|
|
return 0;
|
|
}
|
|
// new key, output tuple not completed -> fill
|
|
if ( key && key->Compare( inTuple->GetAttribute( posK ) )
|
|
&& attrsStored < attrMax - 1 && attrsStored > 0 ) {
|
|
bool filled = fillTuple( tuple );
|
|
cout << "unexpected new key " << key
|
|
<< ", tuple filled with undefined values" << endl;
|
|
if ( filled ) {
|
|
return tuple;
|
|
}
|
|
else {
|
|
tuple->DeleteIfAllowed();
|
|
return 0;
|
|
}
|
|
}
|
|
if ( key ) {
|
|
key->DeleteIfAllowed();
|
|
}
|
|
key = inTuple->GetAttribute( posK )->Copy();
|
|
bool ok = processTuple( inTuple, tuple ); // the operator's main task
|
|
if ( ok ) {
|
|
attrsStored++;
|
|
}
|
|
inTuple->DeleteIfAllowed();
|
|
inTuple = 0;
|
|
}
|
|
return tuple;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
2.116.3 Value mapping for ~fromFields~
|
|
|
|
*/
|
|
|
|
int fromFieldsFun( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
FromFieldsInfo* ffi = (FromFieldsInfo*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch( message ) {
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN: {
|
|
if ( ffi ) {
|
|
delete ffi;
|
|
}
|
|
int posF = ( ( CcInt* )args[ 2 ].addr )->GetValue();
|
|
int posK = ( ( CcInt* )args[ 3 ].addr )->GetValue();
|
|
int posV = ( ( CcInt* )args[ 4 ].addr )->GetValue();
|
|
local.addr = new FromFieldsInfo( args[ 0 ], tt, posF, posK, posV,s );
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if ( !ffi ) {
|
|
return CANCEL;
|
|
}
|
|
result.addr = ffi->nextTuple();
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if ( ffi ) {
|
|
delete ffi;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
2.116.4 Description for Operator ~fromFields~
|
|
|
|
*/
|
|
|
|
struct fromFieldsInfo : OperatorInfo {
|
|
fromFieldsInfo() : OperatorInfo() {
|
|
name = "fromFields";
|
|
signature = "(stream(tuple(keyvalue: keytype, Field: string, "
|
|
"Type: text, Value: text))) x (rel(tuple(X))) "
|
|
"-> (stream(tuple(X)))";
|
|
syntax = "_ feed fromFields [ _ ]";
|
|
meaning = "Composes a stream of tuple items into a certain relation.";
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
2.117 Operator ~applyToAll~
|
|
|
|
This operator receives a tuple stream as well as a function t -> x, x,t in DATA
|
|
and applies this function to all attributes of type t within each tuple. The
|
|
attribute is replaced by the new value.
|
|
|
|
2.117.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr applyToAllTM(ListExpr args){
|
|
string err = "stream(Tuple) x (DATA -> DATA) expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err + " (wrong number of arguments)");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " (first arg is not a tuple stream)");
|
|
}
|
|
ListExpr fun = nl->Second(args);
|
|
if(!listutils::isMap<1>(fun)){
|
|
return listutils::typeError(err +" (second arg is not a function)");
|
|
}
|
|
ListExpr funArg = nl->Second(fun);
|
|
ListExpr funRes = nl->Third(fun);
|
|
if(!listutils::isDATA(funArg) || !listutils::isDATA(funRes)){
|
|
return listutils::typeError(err + " ( function not of type DATA -> DATA");
|
|
}
|
|
|
|
// tests are ok, build result lists
|
|
ListExpr resList = nl->TheEmptyList();
|
|
ListExpr resLast = nl->TheEmptyList();
|
|
ListExpr replaceList = nl->TheEmptyList();
|
|
ListExpr replaceLast = nl->TheEmptyList();
|
|
|
|
ListExpr attrList = nl->Second(nl->Second(nl->First(args)));
|
|
bool firstRes = true;
|
|
bool firstReplace = true;
|
|
int attrNo = 0;
|
|
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr attr = nl->First(attrList);
|
|
if(nl->Equal(funArg, nl->Second(attr))){
|
|
if(firstReplace) {
|
|
replaceList = nl->OneElemList(nl->IntAtom(attrNo));
|
|
replaceLast = replaceList;
|
|
firstReplace = false;
|
|
} else {
|
|
replaceLast = nl->Append(replaceLast, nl->IntAtom(attrNo));
|
|
}
|
|
attr = nl->TwoElemList( nl->First(attr), funRes);
|
|
}
|
|
if(firstRes){
|
|
resList = nl->OneElemList(attr);
|
|
resLast = resList;
|
|
firstRes = false;
|
|
} else {
|
|
resLast = nl->Append(resLast, attr);
|
|
}
|
|
attrNo++;
|
|
attrList = nl->Rest(attrList);
|
|
}
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
replaceList,
|
|
nl->TwoElemList(nl->SymbolAtom(Stream<Tuple>::BasicType()),
|
|
nl->TwoElemList( nl->SymbolAtom(Tuple::BasicType()),
|
|
resList)));
|
|
}
|
|
|
|
/*
|
|
2.117.2 LocalInfo for applyToAll operator
|
|
|
|
*/
|
|
|
|
class applyToAllLocalInfo{
|
|
|
|
public:
|
|
|
|
applyToAllLocalInfo(Word& s, Word& f, vector<int>& pos, TupleType* _tt):
|
|
stream(s), fun(f), positions(pos) {
|
|
stream.open();
|
|
tt = _tt;
|
|
tt->IncReference();
|
|
positions.push_back(-1);
|
|
}
|
|
~applyToAllLocalInfo(){
|
|
tt->DeleteIfAllowed();
|
|
stream.close();
|
|
}
|
|
|
|
Tuple* nextTuple(){
|
|
Tuple* inTuple = stream.request();
|
|
if(!inTuple){
|
|
return 0;
|
|
}
|
|
if(positions.empty()){
|
|
return inTuple;
|
|
}
|
|
int p_index=0;
|
|
int p = positions[p_index];
|
|
Tuple* resTuple = new Tuple(tt);
|
|
for(int i=0;i<inTuple->GetNoAttributes();i++){
|
|
if(i!=p){ // do not apply the function
|
|
resTuple->CopyAttribute(i,inTuple, i);
|
|
} else {
|
|
Attribute* inAttr = inTuple->GetAttribute(i);
|
|
ArgVectorPointer funArgs = qp->Argument(fun.addr);
|
|
(*funArgs)[0] = inAttr;
|
|
Word funRes;
|
|
qp->Request(fun.addr, funRes);
|
|
resTuple->PutAttribute(i, ((Attribute*)funRes.addr)->Clone());
|
|
p_index++;
|
|
p = positions[p_index];
|
|
}
|
|
}
|
|
inTuple->DeleteIfAllowed();
|
|
return resTuple;
|
|
}
|
|
|
|
|
|
private:
|
|
Stream<Tuple> stream;
|
|
Word fun;
|
|
vector<int> positions;
|
|
TupleType* tt;
|
|
};
|
|
|
|
|
|
/*
|
|
2.117.3 ValueMapping for applyToAll
|
|
|
|
*/
|
|
|
|
int applyToAllVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
|
|
|
|
applyToAllLocalInfo* li = (applyToAllLocalInfo*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch(message){
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
}
|
|
vector<int> attrPos;
|
|
for(int i=2;i<qp->GetNoSons(s); i++){
|
|
attrPos.push_back(((CcInt*)args[i].addr)->GetValue());
|
|
}
|
|
local.addr = new applyToAllLocalInfo(args[0], args[1],
|
|
attrPos, tt);
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if(!li){
|
|
return CANCEL;
|
|
}
|
|
result.addr = li->nextTuple();
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
const string applyToAllSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(tuple(x)) x fun -> stream(tuple(y))"
|
|
"</text--->"
|
|
"<text>stream applyToAll [ fun ] </text--->"
|
|
"<text>Replaces all attributes of argument type of fun by the "
|
|
" result of the function."
|
|
"</text--->"
|
|
"<text>query ten feed applyToAll[ fun(int i) i * i] tconsume "
|
|
" </text--->"
|
|
") )";
|
|
|
|
|
|
Operator applyToAll(
|
|
"applyToAll",
|
|
applyToAllSpec,
|
|
applyToAllVM,
|
|
Operator::SimpleSelect,
|
|
applyToAllTM);
|
|
|
|
|
|
|
|
/*
|
|
2.117 Operator =
|
|
|
|
This operator compares two stream of the same type for equality.
|
|
The stream type can be either a tuple stream or an attribute stream.
|
|
|
|
2.117.1 Type Mapping
|
|
|
|
*/
|
|
|
|
ListExpr equalStreamsTM(ListExpr args){
|
|
string err = "stream(X) x stream(X) expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err + " (wrong number of arguments");
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!nl->Equal(arg1,arg2)){
|
|
return listutils::typeError(err + " (arguments have different types)");
|
|
}
|
|
if(!Stream<Attribute>::checkType(arg1) &&
|
|
!Stream<Tuple>::checkType(arg1)){
|
|
return listutils::typeError(err + " ( arguments are not streams)");
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
|
|
/*
|
|
2.117.2 ValueMapping
|
|
|
|
*/
|
|
template<class StreamType, class Cmp>
|
|
int equalStreamsVM1( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
|
|
Stream<StreamType> s1(args[0]);
|
|
Stream<StreamType> s2(args[1]);
|
|
|
|
s1.open();
|
|
s2.open();
|
|
|
|
StreamType* e1 = s1.request();
|
|
StreamType* e2 = s2.request();
|
|
Cmp compare;
|
|
|
|
while( (e1!=0) && (e2!=0)){
|
|
int cmp = compare(e1,e2);
|
|
e1->DeleteIfAllowed();
|
|
e2->DeleteIfAllowed();
|
|
if(cmp!=0){ // different elements found
|
|
s1.close();
|
|
s2.close();
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
e1 = s1.request();
|
|
e2 = s2.request();
|
|
}
|
|
s1.close();
|
|
s2.close();
|
|
|
|
if(e1){ // s1 has more elements
|
|
e1->DeleteIfAllowed();
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
if(e2) {
|
|
e2->DeleteIfAllowed();
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
res->Set(true,true);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
2.117.4 Auxiliary classes for Value Mapping
|
|
|
|
*/
|
|
class AttrCompareAlmost{
|
|
public:
|
|
int operator()(Attribute* a1, Attribute* a2){
|
|
return a1->CompareAlmost(a2);
|
|
}
|
|
};
|
|
|
|
/*
|
|
2.117.4 ValueMapping Array and SelectionFunction
|
|
|
|
*/
|
|
|
|
ValueMapping equalStreamsVM[] = {
|
|
equalStreamsVM1<Tuple,LexicographicalTupleCmpAlmost>,
|
|
equalStreamsVM1<Attribute,AttrCompareAlmost>
|
|
};
|
|
|
|
int equalStreamsSelect(ListExpr args){
|
|
|
|
ListExpr arg1 = nl->First(args);
|
|
if(Stream<Attribute>::checkType(arg1)){ // attribute stream
|
|
return 1;
|
|
} else { // tuple stream
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
2.117.5 Specification
|
|
|
|
*/
|
|
OperatorSpec equalStreamsSpec(
|
|
"stream(X) x stream(X) -> bool; with X in {attribute, tuple(Y)}",
|
|
" _ = _ ",
|
|
" Check streams for equality ",
|
|
" query (plz feed) = (plz feed)");
|
|
|
|
/*
|
|
2.117.6 Operator instance
|
|
|
|
*/
|
|
Operator equalStreams(
|
|
"=",
|
|
equalStreamsSpec.getStr(),
|
|
2,
|
|
equalStreamsVM,
|
|
equalStreamsSelect,
|
|
equalStreamsTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
2.118 Operator ~replaceAttr~
|
|
|
|
This operator replaces attributes by function values
|
|
|
|
*/
|
|
ListExpr replaceAttrTM(ListExpr args){
|
|
string err = "stream(tuple(x)) x funlist expected";
|
|
if(!nl->HasLength(args,2)) {
|
|
return listutils::typeError(err + " (wrong number of arguments)");
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
if(!Stream<Tuple>::checkType(stream)){
|
|
return listutils::typeError(err + " ( first arg is not a tuple stream)");
|
|
}
|
|
ListExpr funList = nl->Second(args);
|
|
if(nl->AtomType(funList) != NoAtom){
|
|
return listutils::typeError(err + " ( second argument is atomar)");
|
|
}
|
|
if(nl->IsEmpty(funList)){
|
|
return listutils::typeError(err + " (funlist is empty)");
|
|
}
|
|
|
|
// first, we store all functions within a map<string, ListExpr>
|
|
// the listexpr is the return type of the map
|
|
ListExpr tupleT = nl->Second(stream);
|
|
ListExpr attrList = nl->Second(tupleT);
|
|
|
|
string error;
|
|
ListExpr attrType;
|
|
map<string,ListExpr> newTypes;
|
|
ListExpr indexList = nl->TheEmptyList();
|
|
bool first = true;
|
|
ListExpr last = nl->TheEmptyList();
|
|
|
|
while(!nl->IsEmpty(funList)){
|
|
ListExpr fun = nl->First(funList);
|
|
funList = nl->Rest(funList);
|
|
if(!nl->HasLength(fun,2)){ // (name, map)
|
|
return listutils::typeError(err + " ( invalid function found)");
|
|
}
|
|
ListExpr name = nl->First(fun);
|
|
if(!listutils::isValidAttributeName(name, error)){
|
|
return listutils::typeError(nl->ToString(name) +
|
|
" is not a valid attribute name ("
|
|
+ error +")");
|
|
}
|
|
string nameS = nl->SymbolValue(name);
|
|
int index = listutils::findAttribute(attrList,nameS,attrType);
|
|
if(index==0){
|
|
return listutils::typeError("AttrName + " + nameS +
|
|
" not present in Tuple");
|
|
}
|
|
if(newTypes.find(nameS)!=newTypes.end()){
|
|
return listutils::typeError("Attribute " + nameS
|
|
+ " is mentioned twice");
|
|
}
|
|
|
|
ListExpr maplist = nl->Second(fun);
|
|
if(!listutils::isMap<1>(maplist)){
|
|
return listutils::typeError(nl->ToString(maplist)
|
|
+ " is not a map with 1 argument");
|
|
}
|
|
if(!nl->Equal(tupleT, nl->Second(maplist))){
|
|
return listutils::typeError("function argument for " + nameS
|
|
+ "does not correspond to the tuple in stream");
|
|
}
|
|
ListExpr resType = nl->Third(maplist);
|
|
if(!Attribute::checkType(resType)){
|
|
return listutils::typeError("function result for " + nameS
|
|
+ " is not an attribute");
|
|
}
|
|
// store index for appending
|
|
if(first){
|
|
indexList = nl->OneElemList(nl->IntAtom(index-1));
|
|
last = indexList;
|
|
first = false;
|
|
} else {
|
|
last = nl->Append(last, nl->IntAtom(index-1));
|
|
}
|
|
// store result type in map
|
|
newTypes[nameS] = resType;
|
|
}
|
|
|
|
// now, we create the result attr type
|
|
ListExpr resAttrList=nl->TheEmptyList();
|
|
first = true;
|
|
while(!nl->IsEmpty(attrList)){
|
|
ListExpr attr = nl->First(attrList);
|
|
attrList = nl->Rest(attrList);
|
|
string name = nl->SymbolValue(nl->First(attr));
|
|
ListExpr newAttr;
|
|
if(newTypes.find(name)==newTypes.end()){ // no change
|
|
newAttr = attr;
|
|
} else {
|
|
newAttr = nl->TwoElemList(nl->First(attr),
|
|
newTypes[name]);
|
|
}
|
|
if(first){
|
|
resAttrList = nl->OneElemList(newAttr);
|
|
last = resAttrList;
|
|
first = false;
|
|
} else {
|
|
last = nl->Append(last, newAttr);
|
|
}
|
|
}
|
|
ListExpr resList = nl->TwoElemList(listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(listutils::basicSymbol<Tuple>(),
|
|
resAttrList));
|
|
|
|
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()),
|
|
indexList,
|
|
resList);
|
|
}
|
|
|
|
/*
|
|
2.118.2 LocalInfo
|
|
|
|
*/
|
|
|
|
class replaceAttrLocalInfo{
|
|
|
|
public:
|
|
replaceAttrLocalInfo(Word& _stream,
|
|
Supplier _funs,
|
|
map<int,int> _mapping,
|
|
TupleType* _tupleType):
|
|
stream(_stream), funs(_funs),mapping(_mapping){
|
|
stream.open();
|
|
tt = _tupleType;
|
|
tt->IncReference();
|
|
}
|
|
|
|
~replaceAttrLocalInfo(){
|
|
tt->DeleteIfAllowed();
|
|
stream.close();
|
|
}
|
|
|
|
Tuple* next(){
|
|
Tuple* orig = stream.request();
|
|
if(!orig){
|
|
return 0;
|
|
}
|
|
Tuple* res = new Tuple(tt);
|
|
for(int i=0;i<res->GetNoAttributes();i++){
|
|
if(mapping.find(i)==mapping.end()){
|
|
// no function known
|
|
res->CopyAttribute(i,orig,i);
|
|
} else {
|
|
// evaluate function
|
|
Supplier sf = qp->GetSon(funs,mapping[i]);
|
|
Supplier sfm = qp->GetSon(sf,1); // access mapping
|
|
((*qp->Argument(sfm))[0]).setAddr(orig);
|
|
qp->Request(sfm,value);
|
|
Attribute* a = (Attribute*) value.addr;
|
|
res->PutAttribute(i,a->Clone());
|
|
}
|
|
}
|
|
orig->DeleteIfAllowed();
|
|
return res;
|
|
|
|
}
|
|
|
|
private:
|
|
Stream<Tuple> stream;
|
|
Supplier funs;
|
|
map<int,int> mapping;
|
|
TupleType* tt;
|
|
Word value; // class member to avoid constructor calls of word
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
2.118.3 ValueMapping
|
|
|
|
*/
|
|
|
|
int replaceAttrVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
|
|
replaceAttrLocalInfo* li = (replaceAttrLocalInfo*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
switch(message){
|
|
case INIT: {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
map<int,int> pos;
|
|
for(int i=2;i<qp->GetNoSons(s);i++){
|
|
int index = ( ((CcInt*) args[i].addr)->GetValue());
|
|
pos[index] = i-2;
|
|
}
|
|
local.addr = new replaceAttrLocalInfo(args[0], qp->GetSon(s,1), pos,
|
|
tt);
|
|
return 0;
|
|
}
|
|
case REQUEST : {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE : {
|
|
if(li){
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
1.118.4 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec replaceAttrSpec(
|
|
"stream(X) x funlist -> stream(Y)",
|
|
" _ replaceAttr [ funlist ] ",
|
|
" Replaces attributes within a tuple stream ",
|
|
" query plz feed replaceAttr[ Ort : .PLZ , PLZ : .PLZ + 23] consume" );
|
|
|
|
/*
|
|
2.117.6 Operator instance
|
|
|
|
*/
|
|
Operator replaceAttr(
|
|
"replaceAttr",
|
|
replaceAttrSpec.getStr(),
|
|
replaceAttrVM,
|
|
Operator::SimpleSelect,
|
|
replaceAttrTM
|
|
);
|
|
|
|
|
|
/*
|
|
2.118 Operator ~pfilter~
|
|
|
|
|
|
2.118.1 Type Mapping
|
|
|
|
|
|
*/
|
|
|
|
ListExpr pfilterTM(ListExpr args){
|
|
|
|
string err = "stream(tuple(X)) x ( (tuple(X) , tuple(X) -> bool) expected";
|
|
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr map = nl->Second(args);
|
|
if(!Stream<Tuple>::checkType(stream) ||
|
|
!listutils::isMap<2>(map)){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr m1 = nl->Second(map);
|
|
ListExpr m2 = nl->Third(map);
|
|
ListExpr mr = nl->Fourth(map);
|
|
ListExpr tuple = nl->Second(stream);
|
|
if(!nl->Equal(tuple,m1) || !nl->Equal(tuple,m2)){
|
|
return listutils::typeError("function arguments does not fi"
|
|
"t the tuple type");
|
|
}
|
|
if(!CcBool::checkType(mr)){
|
|
return listutils::typeError("function result not bool");
|
|
}
|
|
return stream;
|
|
|
|
}
|
|
|
|
int pfilterVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
|
|
Tuple* lastTuple = (Tuple*) local.addr;
|
|
switch(message){
|
|
case OPEN: {
|
|
if(lastTuple){
|
|
lastTuple->DeleteIfAllowed();
|
|
local.addr = 0;
|
|
}
|
|
qp->Open(args[0].addr);
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if(!lastTuple){ // first tuple
|
|
Word tupleW;
|
|
qp->Request(args[0].addr, tupleW);
|
|
if(! qp->Received(args[0].addr)){
|
|
return CANCEL;
|
|
}
|
|
local.addr = tupleW.addr;
|
|
lastTuple = (Tuple*) local.addr;
|
|
lastTuple->IncReference();
|
|
result.addr = lastTuple;
|
|
return YIELD;
|
|
}
|
|
ArgVectorPointer funargs = qp->Argument(args[1].addr);
|
|
while(1){
|
|
// get next tuple
|
|
Word tupleW;
|
|
qp->Request(args[0].addr, tupleW);
|
|
if(! qp->Received(args[0].addr)){
|
|
return CANCEL;
|
|
}
|
|
// evaluate function
|
|
(*funargs)[0] = tupleW;
|
|
(*funargs)[1].addr = lastTuple;
|
|
Word funres;
|
|
qp->Request(args[1].addr, funres);
|
|
CcBool * res = (CcBool*) funres.addr;
|
|
if(res->IsDefined() && res->GetBoolval()){
|
|
lastTuple->DeleteIfAllowed();
|
|
lastTuple = (Tuple*) tupleW.addr;
|
|
lastTuple->IncReference();
|
|
result.addr = lastTuple;
|
|
local.addr = lastTuple;
|
|
return YIELD;
|
|
} else {
|
|
( (Tuple*) tupleW.addr) -> DeleteIfAllowed();
|
|
}
|
|
}
|
|
}
|
|
|
|
case CLOSE :{
|
|
if(lastTuple){
|
|
lastTuple->DeleteIfAllowed();
|
|
local.addr = 0;
|
|
}
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
OperatorSpec pfilterSpec(
|
|
"stream(tuple(X)) x (tuple(X) x tuple(X) -> bool) -> stream(tuple(X))",
|
|
" _ pfilter [ fun ] ",
|
|
" Returns the first tuple in the stream and all tuples fulfilling the"
|
|
" filter consition which uses the current and the last tuple",
|
|
" query ten feed pfilter[ (. - ..) > 3] consume" );
|
|
|
|
/*
|
|
2.117.6 Operator instance
|
|
|
|
*/
|
|
Operator pfilter(
|
|
"pfilter",
|
|
pfilterSpec.getStr(),
|
|
pfilterVM,
|
|
Operator::SimpleSelect,
|
|
pfilterTM
|
|
);
|
|
|
|
|
|
/*
|
|
2.118 Operator extendX
|
|
|
|
*/
|
|
|
|
ListExpr extendXTM(ListExpr args){
|
|
|
|
int len = nl->ListLength(args);
|
|
|
|
if(len!=4){
|
|
return listutils::typeError("3 arguments expected");
|
|
}
|
|
|
|
string err = "stream(tuple) x (AttrName+) x "
|
|
"(tuple -> stream(DATA)) x DATA expected";
|
|
|
|
// check for tuple stream
|
|
ListExpr ts = nl->First(args);
|
|
if(!Stream<Tuple>::checkType(ts)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
// check for list of attribute names
|
|
ListExpr anames = nl->Second(args);
|
|
if(nl->AtomType(anames)!=NoAtom || nl->IsEmpty(anames)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
while(!nl->IsEmpty(anames)){
|
|
ListExpr fa = nl->First(anames);
|
|
string msg;
|
|
if(!listutils::isValidAttributeName(fa,msg)){
|
|
return listutils::typeError("invalid Attribute name " + msg);
|
|
}
|
|
anames = nl->Rest(anames);
|
|
}
|
|
anames = nl->Second(args);
|
|
|
|
ListExpr fun = nl->Third(args);
|
|
|
|
if(!listutils::isMap<1>(fun)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!nl->Equal(nl->Second(ts), nl->Second(fun))){
|
|
return listutils::typeError("Fun argument does not fit the tuple type");
|
|
}
|
|
|
|
ListExpr res = nl->Third(fun);
|
|
|
|
if(!Stream<Attribute>::checkType(res)){
|
|
return listutils::typeError("result of function is not a stream(DATA)");
|
|
}
|
|
|
|
res = nl->Second(res); // remove stream
|
|
|
|
// build result attributes
|
|
ListExpr extAttrList = nl->OneElemList( nl->TwoElemList(
|
|
nl->First(anames),
|
|
res));
|
|
ListExpr last = extAttrList;
|
|
anames = nl->Rest(anames);
|
|
while(!nl->IsEmpty(anames)){
|
|
last = nl->Append(last, nl->TwoElemList(nl->First(anames), res));
|
|
anames = nl->Rest(anames);
|
|
}
|
|
|
|
ListExpr resAttrList = listutils::concat( nl->Second(nl->Second(ts)),
|
|
extAttrList);
|
|
|
|
ListExpr resultList = nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(listutils::basicSymbol<Tuple>(),
|
|
resAttrList));
|
|
|
|
|
|
|
|
if(!Stream<Tuple>::checkType(resultList)){
|
|
return listutils::typeError("Name conflicts in result tuple");
|
|
}
|
|
ListExpr defaultValue = nl->Fourth(args);
|
|
if(!nl->Equal(res,defaultValue)){
|
|
return listutils::typeError("Default value" + nl->ToString(defaultValue) +
|
|
" != stream element from fun " + nl->ToString(res) );
|
|
}
|
|
return resultList;
|
|
}
|
|
|
|
|
|
class extendXLocal{
|
|
public:
|
|
|
|
extendXLocal(Word _istream,
|
|
Supplier _fun, int _no,
|
|
Attribute* _defaultValue,
|
|
TupleType* _tupleType):
|
|
istream(_istream), fun(_fun), no(_no), defaultValue(_defaultValue){
|
|
tt = _tupleType;
|
|
tt->IncReference();
|
|
istream.open();
|
|
}
|
|
|
|
~extendXLocal(){
|
|
tt->DeleteIfAllowed();
|
|
istream.close();
|
|
}
|
|
|
|
Tuple* nextTuple(){
|
|
Tuple* ituple = istream.request();
|
|
if(ituple==0){
|
|
return 0;
|
|
}
|
|
Tuple* rtuple = new Tuple(tt);
|
|
for(int i=0;i<ituple->GetNoAttributes();i++){
|
|
rtuple->CopyAttribute(i,ituple,i);
|
|
}
|
|
ArgVectorPointer funargs = qp->Argument(fun);
|
|
((*funargs)[0]).setAddr(ituple);
|
|
|
|
|
|
int pos = 0;
|
|
bool ok = true;
|
|
Word result;
|
|
qp->Open(fun);
|
|
while(pos < no && ok) {
|
|
qp->Request(fun,result);
|
|
ok = qp->Received(fun);
|
|
if(ok){
|
|
rtuple->PutAttribute(ituple->GetNoAttributes()+pos,
|
|
(Attribute*) result.addr);
|
|
} else {
|
|
rtuple->PutAttribute(ituple->GetNoAttributes()+pos,
|
|
defaultValue->Copy());
|
|
}
|
|
pos++;
|
|
}
|
|
qp->Close(fun);
|
|
while(pos < no){
|
|
rtuple->PutAttribute(ituple->GetNoAttributes()+pos,
|
|
defaultValue->Copy());
|
|
pos++;
|
|
}
|
|
ituple->DeleteIfAllowed();
|
|
return rtuple;
|
|
}
|
|
|
|
private:
|
|
Stream<Tuple> istream;
|
|
Supplier fun;
|
|
int no;
|
|
Attribute* defaultValue;
|
|
TupleType* tt;
|
|
};
|
|
|
|
int extendXVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
|
|
extendXLocal* li = (extendXLocal*)local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
|
|
switch(message){
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr = tt;
|
|
return 0;
|
|
}
|
|
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case OPEN : {
|
|
if(li) delete li;
|
|
// stream attrlist fun default
|
|
// 0 1 2 3
|
|
int ns = qp->GetNoSons(qp->GetSon(s,1));
|
|
Attribute* dv = (Attribute*) args[3].addr;
|
|
local.addr = new extendXLocal( args[0], args[2].addr,
|
|
ns, dv, tt);
|
|
}
|
|
return 0;
|
|
case REQUEST: result.addr = li?li->nextTuple():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
case CLOSE :{
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
OperatorSpec extendXSpec(
|
|
"stream(tuple(X)) x AttrName + x fun(tuple) -> stream(DATA) "
|
|
"x DATA -> stream(tuple())",
|
|
" _ extendX [ attrnames ; fun ; default ] ",
|
|
" Appends so much attributes from the stream created by the function"
|
|
" to the original tuple as new attributes names are given. "
|
|
" If less attributes comes out from the function stream, the default"
|
|
" value is used to fill up the missing attributes",
|
|
" query Kinos feed extendX[ S1, S2, S3 ; intstream(4,5), 0); \"\"" );
|
|
|
|
Operator extendXOP(
|
|
"extendX",
|
|
extendXSpec.getStr(),
|
|
extendXVM,
|
|
Operator::SimpleSelect,
|
|
extendXTM
|
|
);
|
|
|
|
|
|
/*
|
|
2.118 Operator countMt
|
|
|
|
2.118.1 Type Mapping
|
|
|
|
*/
|
|
|
|
ListExpr countMtTM(ListExpr args){
|
|
string err = "stream(Tuple) x int expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args)) ||
|
|
!CcInt::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
/*
|
|
2.118.2 Value Mapping
|
|
|
|
*/
|
|
int countMtVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
int count = 0;
|
|
CcInt* interval = (CcInt*) args[1].addr;
|
|
int iv = interval->IsDefined()?interval->GetValue():0;
|
|
if(iv<0){
|
|
iv = 0;
|
|
}
|
|
Stream<Tuple> stream(args[0]);
|
|
stream.open();
|
|
Tuple* tuple=0;
|
|
clock_t time = clock();
|
|
iv = (iv * CLOCKS_PER_SEC ) / 1000;
|
|
while( (tuple = stream.request())!=0){
|
|
count++;
|
|
tuple->DeleteIfAllowed();
|
|
clock_t ct = clock();
|
|
if((ct-time)>=iv){
|
|
cout << "sw ta" << endl;
|
|
qp->switchTransaction(s);
|
|
time = ct;
|
|
}
|
|
}
|
|
res->Set(true,count);
|
|
return 0;
|
|
}
|
|
|
|
|
|
OperatorSpec countMtSpec(
|
|
" stream(tuple) x int -> int",
|
|
" _ contMt[_]",
|
|
"Count the number of tuples in the result."
|
|
"If used as the root of the operator tree,"
|
|
" after a certain amount of time (milliseconds "
|
|
"given in the 2th parameter), the current state"
|
|
" of this query is committed to the database.",
|
|
" query strassen feed countMt[100]\"\"" );
|
|
|
|
Operator countMtOP(
|
|
"countMt",
|
|
countMtSpec.getStr(),
|
|
countMtVM,
|
|
Operator::SimpleSelect,
|
|
countMtTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
Operator bringToMemory
|
|
|
|
*/
|
|
|
|
ListExpr bringToMemoryTM(ListExpr args){
|
|
|
|
string err = "tuple expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Tuple::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
|
|
int bringToMemoryVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
Tuple* tuple = (Tuple*) args[0].addr;
|
|
tuple->bringToMemory();
|
|
result=qp->ResultStorage(s);
|
|
((CcBool*) result.addr)->Set(true,true);
|
|
return 0;
|
|
}
|
|
|
|
|
|
OperatorSpec bringToMemorySpec(
|
|
" tuple->tuple ",
|
|
" _ bringToMemory",
|
|
"Brings all parts of a tuple into main memory.",
|
|
" query strassen feed extend[Dummy : . bringToMemory] consume ");
|
|
|
|
|
|
Operator bringToMemoryOP(
|
|
"bringToMemory",
|
|
bringToMemorySpec.getStr(),
|
|
bringToMemoryVM,
|
|
Operator::SimpleSelect,
|
|
bringToMemoryTM
|
|
);
|
|
|
|
|
|
|
|
ListExpr feedSTM(ListExpr args){
|
|
string err = "stream({text,string}) x rel(X) expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err + " (wrong number of args)");
|
|
}
|
|
if( !Stream<CcString>::checkType(nl->First(args))
|
|
&& !Stream<FText>::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " (invalid type for arg 1)");
|
|
}
|
|
if(!Relation::checkType(nl->Second(args))){
|
|
return listutils::typeError(err + " (second arg is not a relation)");
|
|
}
|
|
return nl->TwoElemList( listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->Second(nl->Second(args)));
|
|
}
|
|
|
|
template<class T>
|
|
class feedSLI{
|
|
|
|
public:
|
|
feedSLI(Word _stream, ListExpr _relType):stream(_stream),
|
|
relType(_relType), relation(0),iterator(0){
|
|
stream.open();
|
|
ctlg = SecondoSystem::GetCatalog();
|
|
}
|
|
|
|
~feedSLI(){
|
|
destroyRelAndIt();
|
|
stream.close();
|
|
}
|
|
|
|
Tuple* next(){
|
|
|
|
while(true){
|
|
if(iterator){
|
|
Tuple* t = iterator->GetNextTuple();
|
|
if(t){
|
|
return t;
|
|
} else {
|
|
destroyRelAndIt();
|
|
}
|
|
}
|
|
retrieveNextIterator();
|
|
if(iterator==0){
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
Stream<T> stream;
|
|
ListExpr relType;
|
|
Relation* relation;
|
|
GenericRelationIterator* iterator;
|
|
SecondoCatalog* ctlg;
|
|
|
|
|
|
void destroyRelAndIt(){
|
|
if(iterator){delete iterator;}
|
|
if(relation){delete relation;}
|
|
iterator=0;
|
|
relation=0;
|
|
}
|
|
|
|
void retrieveNextIterator(){
|
|
T* objName;
|
|
while( (objName = stream.request())) {
|
|
if(!objName->IsDefined()){
|
|
objName->DeleteIfAllowed();
|
|
} else {
|
|
string name = objName->GetValue();
|
|
objName->DeleteIfAllowed();
|
|
if(retrieveNextIterator(name)){
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool retrieveNextIterator(const string& name){
|
|
assert(iterator==0);
|
|
assert(relation==0);
|
|
if(!ctlg->IsObjectName(name)){
|
|
return false;
|
|
}
|
|
ListExpr ot = ctlg->GetObjectTypeExpr(name);
|
|
if(!nl->Equal(ot,relType)){
|
|
return false;
|
|
}
|
|
Word r;
|
|
bool def;
|
|
if(!ctlg->GetObject(name,r,def)){
|
|
return false;
|
|
}
|
|
if(!def){
|
|
return false;
|
|
}
|
|
relation = (Relation*) r.addr;
|
|
iterator = relation->MakeScan();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template<class T>
|
|
int feedSVMT( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ) {
|
|
feedSLI<T>* li = (feedSLI<T>*) local.addr;
|
|
switch(message){
|
|
case OPEN:
|
|
if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new feedSLI<T>(args[0],
|
|
qp->GetType(qp->GetSon(s,1)));
|
|
return 0;
|
|
case REQUEST:
|
|
result.addr=li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
case CLOSE:
|
|
if(li){
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
}
|
|
return -51765; // should never happen
|
|
}
|
|
|
|
ValueMapping feedSVM[] = {
|
|
feedSVMT<CcString>,
|
|
feedSVMT<FText>
|
|
};
|
|
|
|
int feedSSelect(ListExpr args){
|
|
return Stream<CcString>::checkType(nl->First(args))?0:1;
|
|
}
|
|
OperatorSpec feedSSpec(
|
|
"stream({text,string}) x rel(tuple(X)) -> stream(tuple(X))",
|
|
"_ feedS[_]",
|
|
"Feeds the tuples of several relation objects having "
|
|
"the type given in the second argument into a single stream of tuples."
|
|
"the object names come from the input stream. Names not representing "
|
|
"a relation with the expected type, are ignored. The value of the "
|
|
"relation argument is also ignored." ,
|
|
"query [const rel(tupl([N : string])) value ( (\"ten\")(\"ten\") )] "
|
|
" feed namedtransformstream[N] feedS[ten] count"
|
|
);
|
|
|
|
Operator feedSOp(
|
|
"feedS",
|
|
feedSSpec.getStr(),
|
|
2,
|
|
feedSVM,
|
|
feedSSelect,
|
|
feedSTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
2.119 Operator nth
|
|
|
|
|
|
|
|
|
|
2.118.1 Type Mapping
|
|
|
|
*/
|
|
|
|
ListExpr nthType( ListExpr args)
|
|
{
|
|
if(!nl->HasLength(args,3))
|
|
{
|
|
return listutils::typeError("three arguments expected");
|
|
}
|
|
|
|
ListExpr arg1 = nl->First(args);
|
|
|
|
|
|
if((!listutils::isTupleStream(arg1)))
|
|
{
|
|
return listutils::typeError("first argument must be a stream of tuples");
|
|
}
|
|
|
|
|
|
ListExpr arg2 = nl->Second(args);
|
|
|
|
if(!CcInt::checkType(arg2))
|
|
{
|
|
return listutils::typeError("second argument musst be a int value");
|
|
}
|
|
|
|
|
|
|
|
ListExpr arg3 = nl->Third(args);
|
|
|
|
if(!CcBool::checkType(arg3))
|
|
{
|
|
return listutils::typeError("third argument musst be a bool value");
|
|
}
|
|
|
|
|
|
|
|
return arg1;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
2.119.2 Value Mapping
|
|
|
|
*/
|
|
|
|
class nthInfo{
|
|
|
|
public:
|
|
nthInfo(int iv, bool bv):
|
|
intvalue(iv), boolvalue(bv) {}
|
|
|
|
int intvalue;
|
|
bool boolvalue;
|
|
};
|
|
|
|
|
|
int nthValueMapping(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
nthInfo* li = (nthInfo*) local.addr;
|
|
|
|
|
|
switch(message) {
|
|
|
|
case OPEN: {
|
|
int intvalue = 0;
|
|
bool boolvalue = false;
|
|
CcInt* currentval = static_cast<CcInt*>(args[1].addr);
|
|
CcBool* currentbool = static_cast<CcBool*>(args[2].addr);
|
|
|
|
if(currentval->IsDefined() ) {
|
|
intvalue = currentval->GetIntval();
|
|
}
|
|
|
|
if (intvalue <= 0) {
|
|
intvalue = 1;
|
|
}
|
|
|
|
|
|
if(currentbool->IsDefined() ) {
|
|
boolvalue = currentbool->GetBoolval();
|
|
}
|
|
|
|
srand(time(0));
|
|
qp->Open(args[0].addr);
|
|
if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new nthInfo(intvalue,boolvalue);
|
|
return 0;
|
|
}
|
|
|
|
|
|
case REQUEST: {
|
|
if(!li) {
|
|
return CANCEL;
|
|
}
|
|
Word tuple(Address(0));
|
|
Tuple* current = 0;
|
|
int randvalue;
|
|
randvalue = rand()%li->intvalue + 1;
|
|
if(li->boolvalue) { // exact case
|
|
for (int i=1; i< li->intvalue; i++){
|
|
qp->Request(args[0].addr, tuple);
|
|
if(!qp->Received(args[0].addr)) {
|
|
result.addr = 0;
|
|
return CANCEL;
|
|
} else {
|
|
current = static_cast<Tuple*>(tuple.addr);
|
|
current -> DeleteIfAllowed();
|
|
}
|
|
}
|
|
qp->Request(args[0].addr, tuple);
|
|
if(qp->Received(args[0].addr)) {
|
|
result= tuple;
|
|
return YIELD;
|
|
} else {
|
|
result.addr = 0;
|
|
return CANCEL;
|
|
}
|
|
} else { // random case
|
|
for (int i=1; i< randvalue; i++) {
|
|
qp->Request(args[0].addr, tuple);
|
|
if(!qp->Received(args[0].addr)) {
|
|
result.addr = 0;
|
|
return CANCEL;
|
|
} else {
|
|
current = static_cast<Tuple*>(tuple.addr);
|
|
current -> DeleteIfAllowed();
|
|
}
|
|
}
|
|
qp->Request(args[0].addr, tuple);
|
|
if(qp->Received(args[0].addr)) {
|
|
result= tuple;
|
|
for (int i=randvalue; i<li->intvalue; i++) {
|
|
qp->Request(args[0].addr, tuple);
|
|
if(!qp->Received(args[0].addr)) {
|
|
current = static_cast<Tuple*>(result.addr);
|
|
current -> DeleteIfAllowed();
|
|
result.addr = 0;
|
|
return CANCEL;
|
|
} else {
|
|
current = static_cast<Tuple*>(tuple.addr);
|
|
current -> DeleteIfAllowed();
|
|
}
|
|
}
|
|
return YIELD;
|
|
} else {
|
|
result.addr = 0;
|
|
return CANCEL;
|
|
}
|
|
} //random case
|
|
// end request case
|
|
}
|
|
|
|
case CLOSE: {
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
case CLOSEPROGRESS:
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
|
|
|
|
case REQUESTPROGRESS: {
|
|
ProgressInfo p1;
|
|
ProgressInfo* pRes;
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
if (qp-> RequestProgress(args[0].addr, &p1) ) {
|
|
pRes->Copy(p1);
|
|
pRes->Card = p1.Card/li->intvalue;
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
}
|
|
} // end of switch
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const string nthSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple([a1:d1, ... ,an:dn]))))"
|
|
"x int x bool"
|
|
" -> (stream (tuple([ai:di,...... ,am:dm])))"
|
|
"</text--->"
|
|
"<text>_ nth[_,_]</text--->"
|
|
"<text>normal (TRUE): get every nth tuple"
|
|
" specified by the intvalue."
|
|
" random (FALSE): get"
|
|
" one random tuple"
|
|
" in the intervals specified by intvalue "
|
|
"stream.</text--->"
|
|
"<text>query Orte feed nth[2, TRUE] count"
|
|
"</text--->"
|
|
") )";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Operator extrelnth (
|
|
"nth", // name
|
|
nthSpec, // specification
|
|
nthValueMapping, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
nthType // type mapping
|
|
);
|
|
|
|
/*
|
|
2.120 Operator ~exist~
|
|
|
|
The operator checks whether at least one element is present in a tuple stream.
|
|
|
|
2.120.1 Type mapping function of operator ~exist~
|
|
|
|
Type mapping for ~exist~ is
|
|
|
|
---- stream(tuple) -> bool
|
|
|
|
*/
|
|
ListExpr ExistTypeMap( ListExpr args )
|
|
{
|
|
|
|
if(nl->ListLength(args)!=1){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
|
|
string err = "stream(tuple) or stream(DATA) expected";
|
|
|
|
ListExpr stream = nl->First(args);
|
|
|
|
if( !Stream<Tuple>::checkType(stream)
|
|
&& !Stream<Attribute>::checkType(stream) ) {
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
/*
|
|
2.120.3 Value mapping function of operator ~exist~
|
|
|
|
*/
|
|
|
|
template<class Elem>
|
|
int ExistVMT(Word* args, Word& result, int message, Word& local, Supplier s) {
|
|
Word sWord;
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* b = static_cast<CcBool*>( result.addr );
|
|
|
|
qp->Open(args[0].addr);
|
|
qp->Request(args[0].addr, sWord);
|
|
if(qp->Received(args[0].addr)) {
|
|
b ->Set(true,true);
|
|
((Elem*)sWord.addr)->DeleteIfAllowed();
|
|
} else {
|
|
b ->Set(true,false);
|
|
}
|
|
|
|
qp->Close(args[0].addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping ExistVM[] = {
|
|
ExistVMT<Tuple>, ExistVMT<Attribute>
|
|
};
|
|
|
|
int ExistSelect(ListExpr args){
|
|
return Stream<Attribute>::checkType(nl->First(args))?1:0;
|
|
}
|
|
|
|
/*
|
|
2.120.4 Specification of operator ~exist~
|
|
|
|
*/
|
|
const string ExistSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( "
|
|
"<text>stream(tuple) -> bool || stream(DATA) -> bool"
|
|
"</text--->"
|
|
"<text>_ exist</text--->"
|
|
"<text>Returns true, if the stream "
|
|
"contains at least one tuple"
|
|
"</text--->"
|
|
"<text>query Kinos feed exist"
|
|
"</text--->"
|
|
") )";
|
|
|
|
|
|
|
|
/*
|
|
2.120.5 Definition of operator ~exist~
|
|
|
|
*/
|
|
Operator extrelexist (
|
|
"exist", // name
|
|
ExistSpec, // specification
|
|
2,
|
|
ExistVM, // value mapping
|
|
ExistSelect, // trivial selection function
|
|
ExistTypeMap // type mapping
|
|
);
|
|
|
|
/*
|
|
Operator ~obojoin~
|
|
|
|
Connects two tuple streams one by one
|
|
|
|
*/
|
|
ListExpr obojoinTM(ListExpr args){
|
|
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError("two tuple streams expected");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError("first arg is not a tuple stream");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->Second(args))){
|
|
return listutils::typeError("second arg is not a tuple stream");
|
|
}
|
|
|
|
ListExpr al1 = nl->Second(nl->Second(nl->First(args)));
|
|
ListExpr al2 = nl->Second(nl->Second(nl->Second(args)));
|
|
if(!listutils::disjointAttrNames(al1,al2)){
|
|
return listutils::typeError("name conflict in attribute names found");
|
|
}
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(listutils::basicSymbol<Tuple>(),
|
|
listutils::concat(al1,al2)));
|
|
}
|
|
|
|
template<class E1, class E2>
|
|
class obojoinInfo{
|
|
|
|
public:
|
|
obojoinInfo(Word& s1, Word& s2, TupleType* _tt): stream1(s1), stream2(s2){
|
|
stream1.open();
|
|
stream2.open();
|
|
tt = _tt;
|
|
tt->IncReference();
|
|
}
|
|
~obojoinInfo(){
|
|
stream1.close();
|
|
stream2.close();
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple* next(){
|
|
E1* e1 = stream1.request();
|
|
E2* e2 = stream2.request();
|
|
if(!e1 && !e2){
|
|
return 0;
|
|
}
|
|
if(!e1){
|
|
e2->DeleteIfAllowed();
|
|
return 0;
|
|
}
|
|
if(!e2){
|
|
e1->DeleteIfAllowed();
|
|
return 0;
|
|
}
|
|
Tuple* res = new Tuple(tt);
|
|
concat(e1,e2,res);
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
Stream<E1> stream1;
|
|
Stream<E2> stream2;
|
|
TupleType* tt;
|
|
|
|
void concat(Attribute* e1, Attribute* e2, Tuple* res){
|
|
res->PutAttribute(0,e1);
|
|
res->PutAttribute(1,e2);
|
|
}
|
|
|
|
void concat(Attribute* e1, Tuple* e2, Tuple* res){
|
|
res->PutAttribute(0,e1);
|
|
for(int i=0;i<e2->GetNoAttributes(); i++){
|
|
res->CopyAttribute(i, e2, i+1);
|
|
}
|
|
e2->DeleteIfAllowed();
|
|
}
|
|
|
|
void concat(Tuple* e1, Attribute* e2, Tuple* res){
|
|
for(int i=0;i<e1->GetNoAttributes(); i++){
|
|
res->CopyAttribute(i, e1, i);
|
|
}
|
|
res->PutAttribute(e1->GetNoAttributes(),e2);
|
|
e1->DeleteIfAllowed();
|
|
}
|
|
|
|
void concat(Tuple* e1, Tuple* e2, Tuple* res){
|
|
int e1num = e1->GetNoAttributes();
|
|
for(int i=0;i<e1num; i++){
|
|
res->CopyAttribute(i, e1, i);
|
|
}
|
|
for(int i=0; i<e2->GetNoAttributes();i++){
|
|
res->CopyAttribute(i, e2, i + e1num);
|
|
}
|
|
e1->DeleteIfAllowed();
|
|
e2->DeleteIfAllowed();
|
|
}
|
|
};
|
|
|
|
template<class e1, class e2>
|
|
int obojoinVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
obojoinInfo<e1,e2>* li = (obojoinInfo<e1,e2>*) local.addr;
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
switch(message){
|
|
case INIT : {
|
|
tt = new TupleType(nl->Second(GetTupleResultType(s)));
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new obojoinInfo<e1,e2>(args[0],args[1],tt);
|
|
return 0;
|
|
}
|
|
case REQUEST :{
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE :{
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
OperatorSpec obojoinSpec(
|
|
"stream(tuple(X)) x stream(tuple(Y)) -> stream(tuple(XY))",
|
|
"_ _ obojoin",
|
|
"Joins two tuple streams one by one, meaning the first tuple"
|
|
" of the first stream is joined with the first tuple of the "
|
|
" second stream, both second tuples are joined and so on.",
|
|
" query ten feed ten feed {a} obojoind consume"
|
|
);
|
|
|
|
Operator obojoinOP(
|
|
"obojoin",
|
|
obojoinSpec.getStr(),
|
|
obojoinVM<Tuple,Tuple>,
|
|
Operator::SimpleSelect,
|
|
obojoinTM
|
|
);
|
|
|
|
|
|
ListExpr obojoinDTM(ListExpr args){
|
|
if(!nl->HasMinLength(args,3)){
|
|
return listutils::typeError("three args expected");
|
|
}
|
|
int noDATA = 0; // number of data streams
|
|
bool fd = false; // first stream is a data stream
|
|
|
|
if(Stream<Attribute>::checkType(nl->First(args))){
|
|
noDATA++;
|
|
fd = true;
|
|
} else if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError("first argument must be a stream "
|
|
"of DATA or a stream of tuples");
|
|
}
|
|
if(Stream<Attribute>::checkType(nl->Second(args))){
|
|
noDATA++;
|
|
} else if(!Stream<Tuple>::checkType(nl->Second(args))){
|
|
return listutils::typeError("second argument must be a stream "
|
|
"of DATA or a stream of tuples");
|
|
}
|
|
|
|
if(nl->AtomType(nl->Third(args))!=NoAtom){
|
|
return listutils::typeError("third arg is not a list of attr names");
|
|
}
|
|
|
|
if(!nl->HasLength(nl->Third(args),noDATA)){
|
|
return listutils::typeError("invalid number of attribute names");
|
|
}
|
|
|
|
|
|
if(noDATA == 0){ // two tuple streams
|
|
ListExpr al1 = nl->Second(nl->Second(nl->First(args)));
|
|
ListExpr al2 = nl->Second(nl->Second(nl->Second(args)));
|
|
if(!listutils::disjointAttrNames(al1,al2)){
|
|
return listutils::typeError("name conflict in attribute names found");
|
|
}
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(listutils::basicSymbol<Tuple>(),
|
|
listutils::concat(al1,al2)));
|
|
}
|
|
// noDATA > 0
|
|
ListExpr third = nl->Third(args);
|
|
|
|
if(nl->AtomType(nl->First(third))!=SymbolType){
|
|
return listutils::typeError("third arg is not an attribute name");
|
|
}
|
|
string n1 = nl->SymbolValue(nl->First(third));
|
|
if(!stringutils::isIdent(n1)){
|
|
return listutils::typeError(n1 + " is not a valid attribute name");
|
|
}
|
|
if(noDATA==1){
|
|
ListExpr al1;
|
|
ListExpr al2;
|
|
if(fd){
|
|
al1 = nl->OneElemList(nl->TwoElemList(nl->First(third),
|
|
nl->Second(nl->First(args))));
|
|
al2 = nl->Second(nl->Second(nl->Second(args)));
|
|
} else {
|
|
al1 = nl->Second(nl->Second(nl->First(args)));
|
|
al2 = nl->OneElemList(nl->TwoElemList(nl->First(third),
|
|
nl->Second(nl->Second(args))));
|
|
}
|
|
if(!listutils::disjointAttrNames(al1,al2)){
|
|
return listutils::typeError("name conflict in attribute names found");
|
|
}
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(listutils::basicSymbol<Tuple>(),
|
|
listutils::concat(al1,al2)));
|
|
}
|
|
// noDATA is 2
|
|
if(nl->AtomType(nl->Second(third))!=SymbolType){
|
|
return listutils::typeError("fourth arg is not an attribute name");
|
|
}
|
|
string n2 = nl->SymbolValue(nl->Second(third));
|
|
if(!stringutils::isIdent(n2)){
|
|
return listutils::typeError(n1 + " is not a valid attribute name");
|
|
}
|
|
|
|
if(n1==n2){
|
|
return listutils::typeError("cannot use the same attribute name twice");
|
|
}
|
|
|
|
ListExpr attrList = nl->TwoElemList(
|
|
nl->TwoElemList(
|
|
nl->First(third),
|
|
nl->Second(nl->First(args))),
|
|
nl->TwoElemList(
|
|
nl->Second(third),
|
|
nl->Second(nl->Second(args))));
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(listutils::basicSymbol<Tuple>(),
|
|
attrList));
|
|
}
|
|
|
|
ValueMapping obojoinDVM[] = {
|
|
obojoinVM<Attribute,Attribute>,
|
|
obojoinVM<Attribute,Tuple>,
|
|
obojoinVM<Tuple,Attribute>,
|
|
obojoinVM<Tuple,Tuple>
|
|
};
|
|
|
|
int obojoinDSelect(ListExpr args){
|
|
int n1 = Stream<Attribute>::checkType(nl->First(args))?0:2;
|
|
int n2 = Stream<Attribute>::checkType(nl->Second(args))?0:1;
|
|
return n1 + n2;
|
|
}
|
|
|
|
|
|
|
|
OperatorSpec obojoinDSpec(
|
|
"stream({tuple(X),DATA}) x stream({tuple(Y),DATA}) "
|
|
"[x ID [ x ID]]-> stream(tuple(XY))",
|
|
"_ _ obojoinD[_,_]",
|
|
"Joins two streams one by one, meaning the first element"
|
|
" of the first stream is joined with the first element of the "
|
|
" second stream, both second elements are joined and so on."
|
|
"For each data stream, an attribute name must be given within the "
|
|
"parameter list.",
|
|
"query ten feed intstream(0,100) obojoinD[K] consume"
|
|
);
|
|
|
|
Operator obojoinDOP(
|
|
"obojoinD",
|
|
obojoinDSpec.getStr(),
|
|
4,
|
|
obojoinDVM,
|
|
obojoinDSelect,
|
|
obojoinDTM
|
|
);
|
|
|
|
|
|
/*
|
|
2.29 ~gettuples~
|
|
|
|
This operator retrieves tuples from arelation given by a stream of
|
|
tuple ids.
|
|
|
|
*/
|
|
ListExpr gettuplesTM(ListExpr args){
|
|
|
|
string err = " stream(tid) x rel expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err + " (wrong number of args)");
|
|
}
|
|
if(!Stream<TupleIdentifier>::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " (first arg is not a stream of tids)");
|
|
}
|
|
if(!Relation::checkType(nl->Second(args))){
|
|
return listutils::typeError(err + "(second arg is not a relation)");
|
|
}
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->Second(nl->Second(args)));
|
|
}
|
|
|
|
|
|
class gettuplesInfo{
|
|
|
|
public:
|
|
gettuplesInfo(Word w, GenericRelation* _rel): stream(w), rel(_rel){
|
|
stream.open();
|
|
}
|
|
|
|
~gettuplesInfo(){
|
|
stream.close();
|
|
}
|
|
|
|
Tuple* next(){
|
|
TupleIdentifier* res;
|
|
while( (res = stream.request())){
|
|
if(!res->IsDefined()){
|
|
res->DeleteIfAllowed();
|
|
} else {
|
|
TupleId id = res->GetTid();
|
|
res->DeleteIfAllowed();
|
|
if(id!=0){
|
|
Tuple* tuple = rel->GetTuple(id,true);
|
|
if(tuple){
|
|
return tuple;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
Stream<TupleIdentifier> stream;
|
|
GenericRelation* rel;
|
|
|
|
};
|
|
|
|
|
|
int gettuplesVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
gettuplesInfo* li = (gettuplesInfo*) local.addr;
|
|
switch(message){
|
|
case OPEN : if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
local.addr = new gettuplesInfo(args[0],
|
|
(GenericRelation*) args[1].addr);
|
|
return 0;
|
|
case REQUEST:
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
case CLOSE:
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
OperatorSpec gettuplesSpec(
|
|
"stream(tid) x rel",
|
|
" _ _ gettuples",
|
|
"Retrieves tuples form a relation based on a tuple id stream",
|
|
"query intstream(1,0) use( int2tid(.) ) Kinos gettuples consume"
|
|
);
|
|
|
|
Operator gettuplesOP(
|
|
"gettuples",
|
|
gettuplesSpec.getStr(),
|
|
gettuplesVM,
|
|
Operator::SimpleSelect,
|
|
gettuplesTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
2.30 operator isOrdered
|
|
|
|
checks whether a tuple stream is ascending ordered
|
|
|
|
2.30.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr isOrderedTM(ListExpr args){
|
|
string err = "stream(tuple) expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err + "wrong number of args");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
|
|
/*
|
|
2.30.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int isOrderedVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
|
|
Stream<Tuple> stream(args[0]);
|
|
stream.open();
|
|
Tuple* last = stream.request();
|
|
res->Set(true,true);
|
|
LexicographicalTupleCmp cmp;
|
|
|
|
if(last){
|
|
Tuple* current;
|
|
while( (current = stream.request()) ){
|
|
int c = cmp(last,current);
|
|
if(c>0){ // invalid order found
|
|
last->DeleteIfAllowed();
|
|
current->DeleteIfAllowed();
|
|
stream.close();
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
last->DeleteIfAllowed();
|
|
last = current;
|
|
}
|
|
last->DeleteIfAllowed();
|
|
}
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
2.30.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec isOrderedSpec(
|
|
" stream(tuple) -> bool ",
|
|
" _ isOrdered",
|
|
"checks whether a tuple stream is in ascending order",
|
|
"query Kinos feed sort isOrdered"
|
|
);
|
|
|
|
/*
|
|
2.30.4 Instance
|
|
|
|
*/
|
|
|
|
Operator isOrderedOP(
|
|
"isOrdered",
|
|
isOrderedSpec.getStr(),
|
|
isOrderedVM,
|
|
Operator::SimpleSelect,
|
|
isOrderedTM
|
|
);
|
|
|
|
|
|
/*
|
|
2.34 ~isOrderedBy~
|
|
|
|
2.34.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr isOrderedByTM(ListExpr args){
|
|
string err="stream(tuple) x attrlist expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err + " (wrong number of args)");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError(err + " first arg is not a tuple stream)");
|
|
}
|
|
ListExpr alist = nl->Second(args);
|
|
if(nl->AtomType(alist) != NoAtom){
|
|
return listutils::typeError("Expected list as second arg");
|
|
}
|
|
vector<pair<int,bool> > orderspec;
|
|
ListExpr attrList = nl->Second(nl->Second(nl->First(args)));
|
|
|
|
|
|
while(!nl->IsEmpty(alist)){
|
|
ListExpr first = nl->First(alist);
|
|
alist = nl->Rest(alist);
|
|
string aname;
|
|
bool asc;
|
|
if(nl->AtomType(first)==SymbolType) {
|
|
aname = nl->SymbolValue(first);
|
|
asc = true;
|
|
} else if(!nl->HasLength(args,2)){
|
|
return listutils::typeError("invalid by part");
|
|
} else if( (nl->AtomType(nl->First(first)) != SymbolType)
|
|
||(nl->AtomType(nl->Second(first)) != SymbolType)) {
|
|
return listutils::typeError("invalid by part");
|
|
} else {
|
|
aname = nl->SymbolValue(nl->First(first));
|
|
ListExpr o = nl->Second(first);
|
|
if(!listutils::isSymbol(o,"asc") && !listutils::isSymbol(o,"desc")){
|
|
return listutils::typeError("sorting criterion must be asc or desc");
|
|
}
|
|
asc = listutils::isSymbol(o,"asc");
|
|
}
|
|
ListExpr type;
|
|
int index = listutils::findAttribute(attrList, aname, type);
|
|
if(!index){
|
|
return listutils::typeError("Attribute " + aname
|
|
+ " not present in tuple");
|
|
}
|
|
orderspec.push_back(make_pair(index,asc));
|
|
}
|
|
if(orderspec.empty()){
|
|
return listutils::typeError("missing order specification");
|
|
}
|
|
ListExpr appendList = nl->OneElemList(
|
|
nl->IntAtom(orderspec[0].first));
|
|
ListExpr last = appendList;
|
|
last = nl->Append(last, nl->BoolAtom(orderspec[0].second));
|
|
for(size_t i=1;i<orderspec.size();i++){
|
|
last = nl->Append(last, nl->IntAtom(orderspec[i].first));
|
|
last = nl->Append(last, nl->BoolAtom(orderspec[i].second));
|
|
}
|
|
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
appendList,
|
|
listutils::basicSymbol<CcBool>());
|
|
}
|
|
|
|
/*
|
|
2.34.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int isOrderedByVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
result= qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
res->Set(true,true);
|
|
Stream<Tuple> stream(args[0]);
|
|
stream.open();
|
|
|
|
SortOrderSpecification orderspec;
|
|
int arg = 2;
|
|
while(arg < qp->GetNoSons(s)){
|
|
int pos = ((CcInt*) args[arg].addr)->GetValue();
|
|
arg++;
|
|
bool asc = ((CcBool*) args[arg].addr)->GetValue();
|
|
arg++;
|
|
orderspec.push_back(make_pair(pos,asc));
|
|
}
|
|
|
|
TupleCmpBy cmp(orderspec);
|
|
Tuple* last = stream.request();
|
|
if(last){
|
|
Tuple* current;
|
|
while( (current = stream.request()) ){
|
|
int c = cmp(last,current);
|
|
if(c>0){ // invalid order found
|
|
last->DeleteIfAllowed();
|
|
current->DeleteIfAllowed();
|
|
res->Set(true,false);
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
last->DeleteIfAllowed();
|
|
last = current;
|
|
}
|
|
last->DeleteIfAllowed();
|
|
}
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
2.34.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec isOrderedBySpec(
|
|
" stream(tuple) x orderspec -> bool ",
|
|
" _ isOrderedBy[<orderspec> ",
|
|
"Checks whether a tuple stream is ordered in the way that "
|
|
"given by orderspec. where orderspec is a list of pairs constisting "
|
|
"of an attribute name and {asc,desc}. The latter can be omitted.",
|
|
"query Kinos feed sortby[Name desc, GeoData] isOrderedBy[Name desc, GeoData]"
|
|
);
|
|
|
|
/*
|
|
2.35.4 Instance
|
|
|
|
*/
|
|
|
|
Operator isOrderedByOP(
|
|
"isOrderedBy",
|
|
isOrderedBySpec.getStr(),
|
|
isOrderedByVM,
|
|
Operator::SimpleSelect,
|
|
isOrderedByTM
|
|
);
|
|
|
|
|
|
/*
|
|
2.36 Operator ~tids~
|
|
|
|
Converts a tuple stream into a stream of tids.
|
|
|
|
2.26.1 Type Mapping
|
|
|
|
*/
|
|
|
|
ListExpr tidsTM(ListExpr args){
|
|
string err = "stream(tuple) expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err+ " ( wrong number of args)");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return nl->TwoElemList( listutils::basicSymbol<Stream<TupleIdentifier> >(),
|
|
listutils::basicSymbol<TupleIdentifier>());
|
|
|
|
}
|
|
|
|
/*
|
|
2.36.2 Value Mapping
|
|
|
|
*/
|
|
int tidsVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
Stream<Tuple>* li = (Stream<Tuple>*) local.addr;
|
|
switch(message){
|
|
case OPEN:
|
|
if(li){
|
|
li->close();
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
li = new Stream<Tuple>(args[0]);
|
|
li->open();
|
|
local.addr = li;
|
|
return 0;
|
|
case REQUEST:{
|
|
if(!li) return CANCEL;
|
|
Tuple* t = li->request();
|
|
if(t){
|
|
result.addr = new TupleIdentifier(true, t->GetTupleId());
|
|
t->DeleteIfAllowed();
|
|
} else {
|
|
result.addr = 0;
|
|
}
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE:
|
|
if(li){
|
|
li->close();
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
2.36.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec tidsSpec(
|
|
"stream(tuple) -> stream(tid) ",
|
|
" _ tids ",
|
|
"Replaces tuples in a stream by their tids.",
|
|
"query ten feed tids count"
|
|
);
|
|
|
|
/*
|
|
2.36.3 Operator
|
|
|
|
*/
|
|
|
|
Operator tidsOp(
|
|
"tids",
|
|
tidsSpec.getStr(),
|
|
tidsVM,
|
|
Operator::SimpleSelect,
|
|
tidsTM
|
|
);
|
|
|
|
|
|
/*
|
|
2.37 Operator ~noRefs~
|
|
|
|
Retrieves the number of references of a tuple.
|
|
|
|
*/
|
|
ListExpr noRefsTM(ListExpr args){
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
ListExpr a = nl->First(args);
|
|
if(!Tuple::checkType(a) && !Attribute::checkType(a)){
|
|
return listutils::typeError("tuple expected");
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
|
|
template<class T>
|
|
int noRefsVMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
T* a = (T*) args[0].addr;
|
|
int refs = a->GetNumOfRefs();
|
|
result=qp->ResultStorage(s);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
res->Set(true,refs);
|
|
return 0;
|
|
}
|
|
|
|
|
|
ValueMapping noRefsVM[] = {
|
|
noRefsVMT<Attribute>,
|
|
noRefsVMT<Tuple>
|
|
};
|
|
|
|
int noRefsSelect(ListExpr args){
|
|
return Attribute::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec noRefsSpec(
|
|
"{tuple, DATA} -> int",
|
|
"noRefs(_)",
|
|
"Retrieves the number of refeences for the attribute. "
|
|
"This operator is useful for debugging purposes.",
|
|
"query ten feed extend[RefT : noRefs(.), RefNo : noRefs(.No)] consume"
|
|
);
|
|
|
|
Operator noRefsOp(
|
|
"noRefs",
|
|
noRefsSpec.getStr(),
|
|
2,
|
|
noRefsVM,
|
|
noRefsSelect,
|
|
noRefsTM
|
|
|
|
);
|
|
|
|
/*
|
|
Operator ~addModCounter~
|
|
|
|
*/
|
|
ListExpr addModCounterTM(ListExpr args){
|
|
// stream(t) x IDENT x int x (t x int -> bool) x (t x int -> int)
|
|
if(!nl->HasLength(args,5)){
|
|
return listutils::typeError("5 args expected");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError("first arg is not a tuple stream");
|
|
}
|
|
string err;
|
|
if(!listutils::isValidAttributeName(nl->Second(args),err)){
|
|
return listutils::typeError("second arg is not a valid attribute name");
|
|
}
|
|
if(!CcInt::checkType(nl->Third(args))){
|
|
return listutils::typeError("third arg is not an int");
|
|
}
|
|
if(!listutils::isMap<2>(nl->Fourth(args))){
|
|
return listutils::typeError("Fourth arg is not a binary funtion");
|
|
}
|
|
if(!listutils::isMap<2>(nl->Fifth(args))){
|
|
return listutils::typeError("Fifth arg is not a binary funtion");
|
|
}
|
|
|
|
string n = nl->SymbolValue(nl->Second(args));
|
|
ListExpr attrList = nl->Second(nl->Second(nl->First(args)));
|
|
ListExpr type;
|
|
if(listutils::findAttribute(attrList,n,type)){
|
|
return listutils::typeError("Attribute " + n
|
|
+ " already part of the tuple");
|
|
}
|
|
ListExpr tt = nl->Second(nl->First(args));
|
|
ListExpr fun1 = nl->Fourth(args);
|
|
if(!nl->Equal(tt,nl->Second(fun1))){
|
|
return listutils::typeError("first arg of fun1 and tuple type "
|
|
"in stream differ");
|
|
}
|
|
if(!CcInt::checkType(nl->Third(fun1))){
|
|
return listutils::typeError("second arg of fun 1 is not an int");
|
|
}
|
|
if(!CcBool::checkType(nl->Fourth(fun1))){
|
|
return listutils::typeError("fun 1 does not create a bool");
|
|
}
|
|
ListExpr fun2 = nl->Fifth(args);
|
|
if(!nl->Equal(tt,nl->Second(fun2))){
|
|
return listutils::typeError("first arg of fun2 and tuple type "
|
|
"in stream differ");
|
|
}
|
|
if(!CcInt::checkType(nl->Third(fun2))){
|
|
return listutils::typeError("second arg of fun 2 is not an int");
|
|
}
|
|
if(!CcInt::checkType(nl->Fourth(fun2))){
|
|
return listutils::typeError("fun 2 does not create an int");
|
|
}
|
|
|
|
ListExpr al = nl->OneElemList(nl->TwoElemList( nl->SymbolAtom(n),
|
|
listutils::basicSymbol<CcInt>()));
|
|
attrList = listutils::concat(attrList,al);
|
|
if(!listutils::isAttrList(attrList)){
|
|
return listutils::typeError("result is not a valid attrlist");
|
|
}
|
|
tt = nl->TwoElemList(listutils::basicSymbol<Tuple>(), attrList);
|
|
return nl->TwoElemList(listutils::basicSymbol<Stream<Tuple> >(), tt);
|
|
}
|
|
|
|
class addModCounterInfo{
|
|
public:
|
|
addModCounterInfo( Word& _stream, CcInt* initial,
|
|
Word& _fun1, Word& _fun2, TupleType* _tt):
|
|
stream(_stream), fun1(_fun1.addr), fun2(_fun2.addr), tt(_tt)
|
|
{
|
|
tt->IncReference();
|
|
f1args = qp->Argument(fun1);
|
|
f2args = qp->Argument(fun2);
|
|
currentValue = 0;
|
|
if(initial->IsDefined()){
|
|
currentValue = initial->GetValue();
|
|
}
|
|
initValue = currentValue;
|
|
stream.open();
|
|
}
|
|
|
|
~addModCounterInfo(){
|
|
stream.close();
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
|
|
Tuple* next(){
|
|
Tuple* in = stream.request();
|
|
if(!in){
|
|
return 0;
|
|
}
|
|
CcInt* c = new CcInt(true,currentValue);
|
|
(*f1args)[0] = in;
|
|
(*f1args)[1] = c;
|
|
qp->Request(fun1,funres);
|
|
CcBool* b = (CcBool*) funres.addr;
|
|
if(b->IsDefined() && b->GetValue()){
|
|
// modify counter
|
|
(*f2args)[0] = in;
|
|
(*f2args)[1] = c;
|
|
qp->Request(fun2,funres);
|
|
CcInt* nv = (CcInt*) funres.addr;
|
|
currentValue = nv->IsDefined()?nv->GetValue():initValue;
|
|
c->Set(true,currentValue);
|
|
}
|
|
Tuple* res = new Tuple(tt);
|
|
for(int i=0;i<in->GetNoAttributes();i++){
|
|
res->CopyAttribute(i,in,i);
|
|
}
|
|
res->PutAttribute(in->GetNoAttributes(),c);
|
|
in->DeleteIfAllowed();
|
|
currentValue++;
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
Stream<Tuple> stream;
|
|
Supplier fun1;
|
|
Supplier fun2;
|
|
TupleType* tt;
|
|
ArgVectorPointer f1args;
|
|
ArgVectorPointer f2args;
|
|
int currentValue;
|
|
int initValue;
|
|
Word funres;
|
|
};
|
|
|
|
|
|
int addModCounterVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
addModCounterInfo* li = (addModCounterInfo*)local.addr;
|
|
switch(message){
|
|
case INIT: {
|
|
qp->GetLocal2(s).addr = new TupleType(
|
|
nl->Second(GetTupleResultType(s)));
|
|
return 0;
|
|
}
|
|
case FINISH: {
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
case OPEN: {
|
|
TupleType* tt = (TupleType*) qp->GetLocal2(s).addr;
|
|
if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new addModCounterInfo(args[0], (CcInt*) args[2].addr,
|
|
args[3],args[4], tt);
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
OperatorSpec addModCounterSpec(
|
|
"stream(t) x IDENT x int x ( t x int -> bool) x (t x int -> int) "
|
|
"-> stream(T@(I int)",
|
|
" _ addModCounter[Name, initial, fun1, fun2] ",
|
|
"Adds a counter to a tuple stream. The counter starts with the value "
|
|
"initial and is inremeneted for each tuple. "
|
|
"The first function takes the tuple and the current value of the "
|
|
"counter as arguments. If this function evaluates to true, "
|
|
"the current counter value is replaced by the result of the second "
|
|
"function. If this result is undefined, the counter uses it's initial "
|
|
" value.",
|
|
"query plz feed addModCounter[C ,1, .PLZ * .. > 80000, 0] consume"
|
|
);
|
|
|
|
Operator addModCounterOp(
|
|
"addModCounter",
|
|
addModCounterSpec.getStr(),
|
|
addModCounterVM,
|
|
Operator::SimpleSelect,
|
|
addModCounterTM
|
|
);
|
|
|
|
/*
|
|
Operator ~rduph~
|
|
|
|
*/
|
|
ListExpr Rdup2TypeMap( ListExpr args ) {
|
|
|
|
NList type(args);
|
|
if ( ! (type.hasLength(1) || type.hasLength(2)) )
|
|
{
|
|
return NList::typeError(
|
|
"Expecting one stream argument and a optional int (buckets).");
|
|
}
|
|
|
|
NList first = type.first();
|
|
if (
|
|
!first.hasLength(2) ||
|
|
!first.first().isSymbol(Symbol::STREAM()) ||
|
|
!first.second().hasLength(2) ||
|
|
!first.second().first().isSymbol(Tuple::BasicType()) ||
|
|
!IsTupleDescription( first.second().second().listExpr() ) )
|
|
{
|
|
return NList::typeError("Error in first argument!");
|
|
}
|
|
|
|
bool bucketsGiven = false;
|
|
|
|
// Check bucket parameter
|
|
if(type.hasLength(2)) {
|
|
if(!CcInt::checkType(nl->Second(args))) {
|
|
return NList::typeError("Error in second (buckets) argument!");
|
|
}
|
|
|
|
bucketsGiven = true;
|
|
}
|
|
|
|
ListExpr stream = first.listExpr();
|
|
ListExpr attrList = nl->Second(nl->Second(stream));
|
|
ListExpr appendList;
|
|
ListExpr last;
|
|
|
|
// TODO:
|
|
|
|
// Append all attribute positions for krdup tm
|
|
for(int i = 0; i < nl->ListLength(attrList); i++) {
|
|
if(i == 0){
|
|
appendList = nl->OneElemList(nl->IntAtom(i));
|
|
last = appendList;
|
|
} else {
|
|
last = nl->Append(last,nl->IntAtom(i));
|
|
}
|
|
}
|
|
|
|
if(bucketsGiven) {
|
|
last = nl->Append(last,nl->IntAtom(0));
|
|
} else {
|
|
last = nl->Append(last,nl->IntAtom(-1));
|
|
}
|
|
|
|
// Append all attribute positions for krdup tm
|
|
for(int i = 0; i < nl->ListLength(attrList); i++) {
|
|
last = nl->Append(last,nl->IntAtom(i));
|
|
}
|
|
|
|
ListExpr resultList = nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
appendList,
|
|
stream);
|
|
|
|
cout << nl->ToString(resultList) << endl;
|
|
|
|
return resultList;
|
|
}
|
|
|
|
const string rduphSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( <text>((stream (tuple(...))) x int)"
|
|
" -> (stream (tuple(...)))"
|
|
"</text--->"
|
|
"<text>_ rduph[buckets]</text--->"
|
|
"<text>This operator removes duplicate tuples "
|
|
" from a stream using a hashmap.</text--->"
|
|
"<text>query Orte feed project[BevT] "
|
|
"rduph[999997] count</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.9.4 Definition of operator ~rduph~
|
|
|
|
*/
|
|
Operator extrelrduph (
|
|
"rduph", // name
|
|
rduphSpec, // specification
|
|
krduphVM, // value mapping (reuse krduph operator)
|
|
Operator::SimpleSelect, // trivial selection function
|
|
Rdup2TypeMap // type mapping
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
Operator ~swap~
|
|
|
|
*/
|
|
ListExpr swapTM(ListExpr args){
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError("three arguments expected");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError("first arg is not an tuple stream");
|
|
}
|
|
if(nl->AtomType(nl->Second(args))!=SymbolType){
|
|
return listutils::typeError("second arg is not a valid attribute name");
|
|
}
|
|
if(nl->AtomType(nl->Third(args))!=SymbolType){
|
|
return listutils::typeError("third arg is not a valid attribute name");
|
|
}
|
|
ListExpr attrList = nl->Second(nl->Second(nl->First(args)));
|
|
ListExpr t1;
|
|
string n1 = nl->SymbolValue(nl->Second(args));
|
|
int index1 = listutils::findAttribute(attrList,n1, t1);
|
|
if(!index1){
|
|
return listutils::typeError("attribute " + n1 + " not part of the tuple");
|
|
}
|
|
ListExpr t2;
|
|
string n2 = nl->SymbolValue(nl->Third(args));
|
|
int index2 = listutils::findAttribute(attrList,n2, t2);
|
|
if(!index2){
|
|
return listutils::typeError("attribute " + n2 + " not part of the tuple");
|
|
}
|
|
if(!nl->Equal(t1,t2)){
|
|
return listutils::typeError("attributes " + n1 + " and " + n1
|
|
+ " have different types");
|
|
}
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->TwoElemList(
|
|
nl->IntAtom(index1-1),
|
|
nl->IntAtom(index2-1)
|
|
),
|
|
nl->First(args));
|
|
|
|
|
|
}
|
|
|
|
class swapInfo{
|
|
|
|
public:
|
|
swapInfo(Word& _stream, int _index1, int _index2):
|
|
stream(_stream), index1(_index1), index2(_index2){
|
|
stream.open();
|
|
}
|
|
~swapInfo(){
|
|
stream.close();
|
|
}
|
|
|
|
Tuple* next(){
|
|
Tuple* tuple = stream.request();
|
|
if(tuple){
|
|
Attribute* a1 = tuple->GetAttribute(index1)->Copy();
|
|
Attribute* a2 = tuple->GetAttribute(index2)->Copy();
|
|
tuple->PutAttribute(index1,a2);
|
|
tuple->PutAttribute(index2,a1);
|
|
}
|
|
return tuple;
|
|
}
|
|
private:
|
|
Stream<Tuple> stream;
|
|
int index1;
|
|
int index2;
|
|
};
|
|
|
|
|
|
int swapVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
swapInfo* li = (swapInfo*) local.addr;
|
|
switch(message){
|
|
case OPEN:
|
|
if(li) delete li;
|
|
local.addr = new swapInfo(args[0],
|
|
((CcInt*) args[3].addr)->GetValue(),
|
|
((CcInt*) args[4].addr)->GetValue());
|
|
return 0;
|
|
case REQUEST:
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
case CLOSE:
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
OperatorSpec swapSpec(
|
|
"stream(tuple) x IDENT x INDENT -> stream(tuple)",
|
|
"_ swap [_,_]",
|
|
"Swaps two attribute values within each tuple of "
|
|
"a stream. The attributes must be of the same type.",
|
|
"query plz feed addcounter[c,3] swap[PLZ,C] count"
|
|
);
|
|
|
|
Operator swapOp(
|
|
"swap",
|
|
swapSpec.getStr(),
|
|
swapVM,
|
|
Operator::SimpleSelect,
|
|
swapTM
|
|
);
|
|
|
|
|
|
ListExpr hashvalueTM(ListExpr args){
|
|
if(!nl->HasLength(args,1) && !nl->HasLength(args,2)){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
if(!Tuple::checkType(nl->First(args))){
|
|
return listutils::typeError("tuple [x int] expected");
|
|
}
|
|
if(nl->HasLength(args,2)){
|
|
if(!CcInt::checkType(nl->Second(args))){
|
|
return listutils::typeError("second argument must be an int");
|
|
}
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->IntAtom(0)),
|
|
listutils::basicSymbol<CcInt>());
|
|
|
|
}
|
|
|
|
int hashvalueVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
|
|
Tuple* arg = (Tuple*) args[0].addr;
|
|
size_t hash = arg->HashValue();
|
|
|
|
int mv = -1;
|
|
CcInt* Mv = (CcInt*) args[1].addr;
|
|
if(Mv->IsDefined()){
|
|
mv = Mv->GetValue();
|
|
}
|
|
if(mv!=0){
|
|
hash = hash % mv;
|
|
}
|
|
|
|
result = qp->ResultStorage(s);
|
|
((CcInt*) result.addr)->Set(true,hash);
|
|
return 0;
|
|
}
|
|
|
|
OperatorSpec hashvalueSpec(
|
|
"tuple -> int",
|
|
"hashvalue(_)",
|
|
"Returns a hashvalue for a complete tuple. ",
|
|
"query plz feed extend[H : hashvalue(.)] consume"
|
|
);
|
|
|
|
Operator hashvalueOp(
|
|
"hashvalue",
|
|
hashvalueSpec.getStr(),
|
|
hashvalueVM,
|
|
Operator::SimpleSelect,
|
|
hashvalueTM
|
|
);
|
|
|
|
|
|
/*
|
|
Type Map Operator ~AGGRTYPE~
|
|
|
|
This opertaor takes a tuple stream, an attribute name, and
|
|
may be further elements. It extracts the type of the attribute
|
|
with given name from the tuple stream. Other attributes are
|
|
ignored.
|
|
|
|
*/
|
|
ListExpr AGGRTYPETM(ListExpr args){
|
|
if(!nl->HasMinLength(args,2)){
|
|
return listutils::typeError("at least 2 arguments expected");
|
|
}
|
|
if(!Stream<Tuple>::checkType(nl->First(args))){
|
|
return listutils::typeError("First argument is not a tuple stream");
|
|
}
|
|
if(nl->AtomType(nl->Second(args)) != SymbolType){
|
|
return listutils::typeError("Second argument ist not "
|
|
"a valid attribute name");
|
|
}
|
|
string aname = nl->SymbolValue(nl->Second(args));
|
|
ListExpr attrList = nl->Second(nl->Second(nl->First(args)));
|
|
ListExpr attrType = nl->TheEmptyList();
|
|
int index = listutils::findAttribute(attrList, aname, attrType);
|
|
if(index == 0){
|
|
return listutils::typeError("Attribute " + aname +
|
|
" not part of the tuple");
|
|
}
|
|
return attrType;
|
|
}
|
|
|
|
OperatorSpec AGGRTYPESpec(
|
|
"stream(tuple) x IDENT [ x ... ] -> DATA",
|
|
"AGGRTYPE(_,_,...)",
|
|
"Extracts the type of an attribute from a tuple "
|
|
"stream description. Type Map Operator. ",
|
|
"query ten feed aggregteB[No; . + ..; 0] "
|
|
);
|
|
|
|
Operator AGGRTYPEOp(
|
|
"AGGRTYPE",
|
|
AGGRTYPESpec.getStr(),
|
|
0,
|
|
Operator::SimpleSelect,
|
|
AGGRTYPETM
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
2.10 Operator ~buffer~
|
|
|
|
Decuples two streams by buffering up to n
|
|
elements. After the buffer is filled, the buffered
|
|
is emitted and re-filled.
|
|
|
|
2.10.1 Type mapping function of operator ~buffer~
|
|
|
|
Type mapping for ~buffer~ is
|
|
|
|
---- ((stream (tuple ((x1 t1)...(xn tn))) int) ->
|
|
((stream (tuple ((x1 t1)...(xn tn))))
|
|
|
|
or ((stream T) int) -> (stream T) for T in kind DATA
|
|
----
|
|
|
|
*/
|
|
ListExpr BufferTypeMap( ListExpr args )
|
|
{
|
|
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
|
|
string err = " stream(tuple(...) x int or stream(DATA) x int expected";
|
|
|
|
ListExpr stream = nl->First(args);
|
|
ListExpr count = nl->Second(args);
|
|
|
|
if(( !Stream<Tuple>::checkType(stream) &&
|
|
!Stream<Attribute>::checkType(stream) ) ||
|
|
!CcInt::checkType(count) ){
|
|
return listutils::typeError(err);
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
/*
|
|
2.10.2 Value mapping function of operator ~buffer~
|
|
|
|
*/
|
|
|
|
|
|
struct BufferLocalInfo
|
|
{
|
|
BufferLocalInfo( const size_t maxTuples = 0 ):
|
|
maxTuples( maxTuples ), streamExhausted(false)
|
|
{}
|
|
|
|
std::queue<Word> buffer;
|
|
size_t maxTuples;
|
|
bool streamExhausted;
|
|
};
|
|
|
|
int Buffer(Word* args, Word& result, int message, Word& local, Supplier s)
|
|
{
|
|
BufferLocalInfo *bli;
|
|
Word tupleWord;
|
|
|
|
bli = (BufferLocalInfo*) local.addr;
|
|
|
|
CcInt* maxTupleCCInt = ((CcInt*) args[1].addr);
|
|
|
|
switch(message)
|
|
{
|
|
case OPEN:
|
|
|
|
if ( bli ) delete bli;
|
|
|
|
|
|
if(! maxTupleCCInt->IsDefined()) {
|
|
cerr << "Error: Max buffer value is not defined" << endl;
|
|
return CANCEL;
|
|
}
|
|
|
|
bli = new BufferLocalInfo( maxTupleCCInt->GetIntval() );
|
|
local.setAddr( bli );
|
|
|
|
qp->Open(args[0].addr);
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
|
|
// Case 1: We have buffered tuples
|
|
if(! bli -> buffer.empty()) {
|
|
//cout << "Debug: Serving request from queue" << endl;
|
|
result = bli -> buffer.front();
|
|
bli -> buffer.pop();
|
|
return YIELD;
|
|
}
|
|
|
|
// Case 2: Buffer is empty but stream is ready
|
|
if(! bli->streamExhausted) {
|
|
//cout << "Debug: Re-fill queue" << endl;
|
|
|
|
while(bli -> buffer.size() < bli -> maxTuples) {
|
|
qp->Request(args[0].addr, tupleWord);
|
|
|
|
if(! qp->Received(args[0].addr)) {
|
|
bli->streamExhausted = true;
|
|
break;
|
|
}
|
|
|
|
bli -> buffer.push(tupleWord);
|
|
}
|
|
}
|
|
|
|
if(! bli -> buffer.empty()) {
|
|
result = bli -> buffer.front();
|
|
bli -> buffer.pop();
|
|
return YIELD;
|
|
}
|
|
|
|
// Case 3: Buffer is empty and stream is exhauted
|
|
return CANCEL;
|
|
|
|
case CLOSE:
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
|
|
case CLOSEPROGRESS:
|
|
if ( bli )
|
|
{
|
|
delete bli;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
|
|
case REQUESTPROGRESS:
|
|
|
|
ProgressInfo p1;
|
|
ProgressInfo* pRes;
|
|
|
|
pRes = (ProgressInfo*) result.addr;
|
|
|
|
if ( !bli ) {
|
|
return CANCEL;
|
|
}
|
|
|
|
if ( qp->RequestProgress(args[0].addr, &p1) )
|
|
{
|
|
pRes->Card = p1.Card;
|
|
pRes->CopySizes(p1);
|
|
pRes->Time = p1.Time;
|
|
pRes->Progress = p1.Progress;
|
|
pRes->BTime = p1.BTime;
|
|
pRes->BProgress = p1.BProgress;
|
|
return YIELD;
|
|
} else {
|
|
return CANCEL;
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
2.10.3 Specification of operator ~buffer~
|
|
|
|
*/
|
|
const string BufferSpec = "( ( \"Signature\" \"Syntax\" \"Meaning\" "
|
|
"\"Example\" ) "
|
|
"( "
|
|
"<text>((stream (tuple([a1:d1, ... ,an:dn]"
|
|
"))) x int) -> (stream (tuple([a1:d1, ... ,"
|
|
"an:dn]))) or \n"
|
|
"((stream T) int) -> (stream T), "
|
|
"for T in kind DATA.</text--->"
|
|
"<text>_ buffer [ _ ]</text--->"
|
|
"<text>Decuples two streams by buffering up to n "
|
|
"elements. After the buffer is filled, the buffered "
|
|
"is emitted and re-filled.</text--->"
|
|
"<text>query cities feed buffer[10] consume\n"
|
|
"query intstream(1,1000) buffer[10] printstream count"
|
|
"</text--->"
|
|
") )";
|
|
|
|
/*
|
|
2.10.4 Definition of operator ~buffer~
|
|
|
|
*/
|
|
Operator extrelbuffer (
|
|
"buffer", // name
|
|
BufferSpec, // specification
|
|
Buffer, // value mapping
|
|
Operator::SimpleSelect, // trivial selection function
|
|
BufferTypeMap // type mapping
|
|
);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
3 Class ~ExtRelationAlgebra~
|
|
|
|
A new subclass ~ExtRelationAlgebra~ 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 ~extendedRelationAlgebra~
|
|
is defined.
|
|
|
|
*/
|
|
|
|
|
|
class ExtRelationAlgebra : public Algebra
|
|
{
|
|
public:
|
|
ExtRelationAlgebra() : Algebra()
|
|
{
|
|
AddOperator(&extrelsample);
|
|
AddOperator(&extrelgroup); // type map operator
|
|
AddOperator(&extrelcancel);
|
|
AddOperator(&extrelextract);
|
|
AddOperator(&extrelextend);
|
|
extrelextend.enableInitFinishSupport();
|
|
AddOperator(&extrelconcat);
|
|
AddOperator(&extrelmin);
|
|
AddOperator(&extrelmax);
|
|
|
|
ValueMapping avgFuns[] = { AvgValueMapping<int,CcInt>,
|
|
AvgValueMapping<SEC_STD_REAL,CcReal>, 0 };
|
|
ValueMapping sumFuns[] = { SumValueMapping<int,CcInt>,
|
|
SumValueMapping<SEC_STD_REAL,CcReal>, 0 };
|
|
ValueMapping varFuns[] = { VarValueMapping<int,CcInt>,
|
|
VarValueMapping<SEC_STD_REAL,CcReal>, 0 };
|
|
|
|
// Operator* extrelavg =
|
|
AddOperator(avgInfo(), avgFuns, AvgSumSelect, AvgSumTypeMap<true>);
|
|
// extrelavg->SetUsesMemory();
|
|
|
|
|
|
AddOperator(sumInfo(), sumFuns, AvgSumSelect, AvgSumTypeMap<false>);
|
|
|
|
AddOperator(printrefsInfo(), printrefs_vm, printrefs_tm);
|
|
|
|
Operator* vfo = AddOperator(varInfo(), varFuns,
|
|
AvgSumSelect, AvgSumTypeMap<true>);
|
|
vfo->SetUsesMemory();
|
|
|
|
ValueMapping statsFuns[] =
|
|
{
|
|
StatsValueMapping<int,CcInt,int,CcInt>,
|
|
StatsValueMapping<int,CcInt,SEC_STD_REAL,CcReal>,
|
|
StatsValueMapping<SEC_STD_REAL,CcReal,int,CcInt>,
|
|
StatsValueMapping<SEC_STD_REAL,CcReal,SEC_STD_REAL,CcReal>, 0
|
|
};
|
|
Operator* sio = AddOperator(statsInfo(), statsFuns,
|
|
StatsSelect, StatsTypeMap);
|
|
sio->SetUsesMemory();
|
|
|
|
AddOperator(&extrelhead);
|
|
AddOperator(&extrelbuffer);
|
|
AddOperator(&extrelsortby);
|
|
extrelsortby.SetUsesMemory();
|
|
AddOperator(&extrelsort);
|
|
extrelsort.SetUsesMemory();
|
|
|
|
AddOperator(&extrelrdup);
|
|
AddOperator(&extrelmergesec);
|
|
AddOperator(&extrelmergediff);
|
|
AddOperator(&extrelmergeunion);
|
|
AddOperator(&extrelmergejoin);
|
|
extrelmergejoin.SetUsesMemory();
|
|
|
|
AddOperator(&extrelsortmergejoin);
|
|
extrelsortmergejoin.SetUsesMemory();
|
|
AddOperator(&extrelsmouterjoin);
|
|
extrelsmouterjoin.SetUsesMemory();
|
|
AddOperator(&extrelhashjoin);
|
|
extrelhashjoin.SetUsesMemory();
|
|
AddOperator(&extrelloopjoin);
|
|
extrelloopjoin.enableInitFinishSupport();
|
|
AddOperator(&extrelextendstream);
|
|
extrelextendstream.enableInitFinishSupport();
|
|
AddOperator(&extrelprojectextendstream);
|
|
extrelprojectextendstream.enableInitFinishSupport();
|
|
AddOperator(&extrelloopsel);
|
|
extrelloopsel.enableInitFinishSupport();
|
|
AddOperator(&extrelgroupby);
|
|
extrelgroupby.SetUsesMemory();
|
|
extrelgroupby.enableInitFinishSupport();
|
|
AddOperator(&extrelaggregate);
|
|
AddOperator(&extrelaggregateB);
|
|
AddOperator(&extrelsymmjoin);
|
|
extrelsymmjoin.SetUsesMemory();
|
|
extrelsymmjoin.enableInitFinishSupport();
|
|
AddOperator(&extrelsymmouterjoin);
|
|
extrelsymmouterjoin.SetUsesMemory();
|
|
AddOperator(&extrelsymmproductextend);
|
|
extrelsymmproductextend.SetUsesMemory();
|
|
extrelsymmproductextend.enableInitFinishSupport();
|
|
AddOperator(&extrelsymmproduct);
|
|
extrelsymmproduct.enableInitFinishSupport();
|
|
extrelsymmproduct.SetUsesMemory();
|
|
AddOperator(&extrelprojectextend);
|
|
extrelprojectextend.enableInitFinishSupport();
|
|
AddOperator(&krdup);
|
|
krdup.enableInitFinishSupport();
|
|
AddOperator(&krduph);
|
|
krduph.enableInitFinishSupport();
|
|
krduph.SetUsesMemory();
|
|
AddOperator(&extrelrduph);
|
|
extrelrduph.SetUsesMemory();
|
|
extrelrduph.enableInitFinishSupport();
|
|
AddOperator(&extreladdcounter);
|
|
extreladdcounter.enableInitFinishSupport();
|
|
AddOperator(&ksmallest);
|
|
AddOperator(&kbiggest);
|
|
AddOperator(&extrelslidingwindow);
|
|
extrelslidingwindow.enableInitFinishSupport();
|
|
AddOperator(&extend_aggr);
|
|
extend_aggr.enableInitFinishSupport();
|
|
AddOperator(&extend_last);
|
|
extend_last.enableInitFinishSupport();
|
|
AddOperator(&extend_next);
|
|
extend_next.enableInitFinishSupport();
|
|
AddOperator(&aggregateC);
|
|
Operator* tfo = AddOperator(toFieldsInfo(), toFieldsFun, toFieldsType );
|
|
tfo->enableInitFinishSupport();
|
|
Operator* ffo = AddOperator(fromFieldsInfo(), fromFieldsFun,
|
|
fromFieldsType );
|
|
ffo->enableInitFinishSupport();
|
|
AddOperator(&applyToAll);
|
|
applyToAll.enableInitFinishSupport();
|
|
AddOperator(&equalStreams);
|
|
AddOperator(&replaceAttr);
|
|
replaceAttr.enableInitFinishSupport();
|
|
AddOperator(&pfilter);
|
|
AddOperator(&extendXOP);
|
|
extendXOP.enableInitFinishSupport();
|
|
AddOperator(&countMtOP);
|
|
AddOperator(&bringToMemoryOP);
|
|
AddOperator(&feedSOp);
|
|
AddOperator(&extrelnth);
|
|
AddOperator(&obojoinOP);
|
|
obojoinOP.enableInitFinishSupport();
|
|
AddOperator(&obojoinDOP);
|
|
obojoinDOP.enableInitFinishSupport();
|
|
AddOperator(&gettuplesOP);
|
|
AddOperator(&isOrderedOP);
|
|
AddOperator(&isOrderedByOP);
|
|
AddOperator(&tidsOp);
|
|
AddOperator(&noRefsOp);
|
|
AddOperator(&extractDefOp);
|
|
AddOperator(&addModCounterOp);
|
|
addModCounterOp.enableInitFinishSupport();
|
|
AddOperator(&swapOp);
|
|
AddOperator(&hashvalueOp);
|
|
AddOperator(&AGGRTYPEOp);
|
|
AddOperator(&extrelexist);
|
|
|
|
#ifdef USE_PROGRESS
|
|
// support for progress queries
|
|
extrelextend.EnableProgress();
|
|
extrelhead.EnableProgress();
|
|
extrelbuffer.EnableProgress();
|
|
extrelsortby.EnableProgress();
|
|
extrelsort.EnableProgress();
|
|
extrelrdup.EnableProgress();
|
|
extrelmergejoin.EnableProgress();
|
|
extrelsortmergejoin.EnableProgress();
|
|
extrelsmouterjoin.EnableProgress();
|
|
extrelsymmouterjoin.EnableProgress();
|
|
extrelhashjoin.EnableProgress();
|
|
extrelloopjoin.EnableProgress();
|
|
extrelextendstream.EnableProgress();
|
|
extrelprojectextendstream.EnableProgress();
|
|
extrelgroupby.EnableProgress();
|
|
extrelsymmjoin.EnableProgress();
|
|
extrelprojectextend.EnableProgress();
|
|
ksmallest.EnableProgress();
|
|
kbiggest.EnableProgress();
|
|
extrelloopsel.EnableProgress();
|
|
extrelaggregate.EnableProgress();
|
|
extrelnth.EnableProgress();
|
|
#endif
|
|
}
|
|
|
|
~ExtRelationAlgebra() {};
|
|
};
|
|
|
|
} // end of namespace extrelationalg
|
|
|
|
/*
|
|
|
|
4 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*
|
|
InitializeExtRelationAlgebra( NestedList* nlRef,
|
|
QueryProcessor* qpRef,
|
|
AlgebraManager* amRef )
|
|
{
|
|
nl = nlRef;
|
|
qp = qpRef;
|
|
am = amRef;
|
|
return (new extrelationalg::ExtRelationAlgebra());
|
|
}
|
|
|