/* ---- This file is part of SECONDO. Copyright (C) 2004, University in Hagen, Department of Computer Science, Database Systems for New Applications. SECONDO is free software{} you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation{} either version 2 of the License, or (at your option) any later version. SECONDO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY{} without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO{} if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- */ #include #include "Algebra.h" #include "Attribute.h" #include "NestedList.h" #include "NList.h" #include "ListUtils.h" #include "LogMsg.h" #include "QueryProcessor.h" #include "ConstructorTemplates.h" #include "StandardTypes.h" #include "TypeMapUtils.h" #include "Symbols.h" #include "Algebras/Relation-C++/RelationAlgebra.h" #include "Record.h" #include "ListIterator.h" #include "Stream.h" //#define RECORD_DEBUG #undef RECORD_DEBUG extern NestedList* nl; extern QueryProcessor *qp; using namespace mappings; using namespace std; namespace record { /* 1 Typkonstruktor for record */ TypeConstructor recordTC( Record::BasicType(), // name of the type in SECONDO Record::Property, // property function describing signature Record::Out, Record::In, // Out and In functions 0, 0, // SaveToList, RestoreFromList functions Record::Create, Record::Delete, // object creation and deletion OpenAttribute, SaveAttribute, // object open, save Record::Close, Record::Clone, // close, and clone Record::Cast, // cast function Record::SizeOfObj, // sizeof function Record::KindCheck ); // kind checking function /* 2 Operator ~createRecord~ Creates a record object with the listed elements. Elements have to be kind DATA. 2.1 Type mapping of operator ~createRecord~ */ ListExpr createRecordTM(ListExpr args) { ListExpr elem, resultList, errorInfo, rest; string elemname, type; vector elemnamelist; int unique; #ifdef RECORD_DEBUG cerr<ToString(args)<<")"<ListLength(args) == 0 ) { ErrorReporter::ReportError("Operator needs at least one element."); return nl->TypeError(); } rest = nl->First(args); unique = 0; while ( !nl->IsEmpty(rest) ) { if ( nl->IsAtom(rest) ) { ErrorReporter::ReportError("Each Element must consist of id and type"); return nl->TypeError(); } elem = nl->First(rest); rest = nl->Rest(rest); if (nl->ListLength(elem) == 2) { if ((nl->IsAtom(nl->First(elem))) && (nl->AtomType(nl->First(elem)) == SymbolType)) { elemname = nl->SymbolValue(nl->First(elem)); // check name convention for element names char * copy = new char[elemname.size() + 1]; strcpy(copy, elemname.c_str()); if ( isupper(*copy) == 0 ) { ErrorReporter::ReportError("element name has to start with a capital " "letter: " + elemname); delete[] copy; return nl->TypeError(); } delete[] copy; // check if name is a typename. if (SecondoSystem::GetCatalog()->IsTypeName(elemname)) { ErrorReporter::ReportError(elemname + " is not allowed as element " "name. Maybe there exists an object" "with this name in the database. "); return nl->TypeError(); } unique = std::count(elemnamelist.begin(), elemnamelist.end(), elemname); if (unique > 0) { ErrorReporter::ReportError("Doubly defined element name " + elemname); return nl->TypeError(); } elemnamelist.push_back(elemname); } else { //nl->First(elem) not an atom and Type is not SymbolType ErrorReporter::ReportError("Invalid element name " + nl->ToString(nl->First(elem))); return nl->TypeError(); } } else { //ListLength(elem) != 2 ErrorReporter::ReportError("Invalid element definition " + nl->ToString(elem)); return nl->TypeError(); } //pruefen ob es sich um einen gueltigen Typen handelt if (nl->IsAtom(nl->Second(elem))) { type = nl->SymbolValue(nl->Second(elem)); } else { type = nl->SymbolValue(nl->First(nl->Second(elem))); } if (!SecondoSystem::GetCatalog()->IsTypeName(type)) { ErrorReporter::ReportError(type + " is not a type."); return nl->TypeError(); } //check type to be kind DATA errorInfo = nl->OneElemList(nl->SymbolAtom("ERROR")); if( !am->CheckKind(Kind::DATA(), nl->Second(elem), errorInfo) ) { ErrorReporter::ReportError("Elements have to be kind DATA"); return nl->TypeError(); } } resultList = nl->Cons(nl->SymbolAtom(Record::BasicType()), nl->First(args)); #ifdef RECORD_DEBUG cerr<<"RecordAlgebra::createRecordTM end."; cerr<<"returns: "<ToString(resultList)<ResultStorage(s); record = static_cast(result.addr); record->Clear(); sons = qp->GetNoSons(args[0].addr); for (int i = 0; i< sons; i++) { son = qp->GetSupplier(args[0].addr, i); value = qp->Request(qp->GetSon(son,1)); elem = (Attribute*)value.addr; name = qp->GetType(qp->GetSon(son,0)); type = qp->GetType(qp->GetSon(son,1)); if (!nl->IsAtom(type)) { type = nl->First(type); } #ifdef RECORD_DEBUG cerr<<"RecordAlgebra::createRecordVM Type = "<ToString(type)<AppendElement(elem, nl->ToString(type), nl->ToString(name)); } result.addr = record; #ifdef RECORD_DEBUG cerr<<"RecordAlgebra::createRecordVM end."<ToString(args)<<")"<ListLength(args)!=2){ ErrorReporter::ReportError("Two arguments expected."); return nl->TypeError(); } first = nl->First(args); //z.B. (record (name string) (age int)) if(nl->ListLength(first)<=1) { ErrorReporter::ReportError("First argument must be a record."); return nl->TypeError(); } if(!nl->IsEqual(nl->First(first), Record::BasicType())) { ErrorReporter::ReportError("First argument must be a record."); return nl->TypeError(); } second = nl->Second(args); if(nl->AtomType(second)!=SymbolType) { ErrorReporter::ReportError("Second argument must be an attribute name."); return nl->TypeError(); } elemList = nl->Rest(first); elemName = nl->SymbolValue(second); elemIndex = listutils::findAttribute(elemList, elemName, elemType); if (elemIndex == 0) { ErrorReporter::ReportError("Element " + elemName + " not found in element list."); return nl->TypeError(); } ListExpr ret = nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()) , nl->OneElemList(nl->IntAtom(elemIndex)) , elemType); #ifdef RECORD_DEBUG cerr<<"RecordAlgebra::attrTM end. returns "<ToString(ret)<GetIntval(); assert(1<=index); assert(index <= recordptr->GetNoElements()); element = recordptr->GetElement(index - 1); result = qp->ResultStorage(s); (static_cast(result.addr))->CopyFrom(element); element->DeleteIfAllowed(); return 0; } /* 3.3 Operator Descriptions */ struct createRecordInfo : OperatorInfo { createRecordInfo() { name = "createRecord"; signature = "( (id1 T1) (id2 T2) ... (idn Tn) ) -> " "record( (id1 T1) (id2 T2) ... (idn Tn) )"; syntax = "createRecord([id1: T1, id2: T2, ..., idn: Tn])"; meaning = "Creates a record with the elements of the given list."; } }; struct attrInfo : OperatorInfo { attrInfo() { name = "attr"; signature = "record( (id1 T1) (id2 T2) ... (idn Tn) ) x idi -> Ti"; syntax = "attr(_, _)"; meaning = "Returns the value of the element identified with idi"; } }; /* 4 Operator ~transformT2Rstream~ Creates a stream of records from a stream of tuples, each record contains attributes of tuple 4.1 Type mapping function of operator ~transformT2Rstream~ */ ListExpr transformT2RstreamTypeMap(ListExpr args) { if(nl->ListLength(args)!=1){ ErrorReporter::ReportError("one argument expected"); return nl->TypeError(); } ListExpr arg = nl->First(args); if(!Stream::checkType(arg)){ ErrorReporter::ReportError("stream(tuple) expected"); return nl->TypeError(); } ListExpr attrList = nl->Second(nl->Second(arg)); return nl->TwoElemList(nl->SymbolAtom(Stream::BasicType()), nl->Cons( nl->SymbolAtom(Record::BasicType()), attrList)); } /* 4.2 Value mapping function of operator ~transformT2Rstream~ */ int transformT2RstreamValueMapping(Word* args, Word& result, int message, Word& local, Supplier s) { Word t; Tuple* tup; Record* rec; bool elementAppended; switch (message) { case OPEN : { qp->Open(args[0].addr); return 0; } case REQUEST : { qp->Request(args[0].addr, t); if (qp->Received(args[0].addr)) { tup = (Tuple*)t.addr; rec = new Record(0); ListExpr rest = nl->Rest(nl->Second(qp->GetType( s ))); int i = 0; while( !nl->IsEmpty( rest ) ){ ListExpr cur = nl->First( rest ); rest = nl->Rest( rest ); Attribute * attr = (Attribute*)tup->GetAttribute(i++); elementAppended = rec->AppendElement( attr, nl->IsAtom(nl->Second(cur)) ? nl->ToString(nl->Second(cur)) : nl->ToString(nl->First(nl->Second(cur))), nl->ToString(nl->First(cur))); assert(elementAppended); } tup->DeleteIfAllowed(); result.setAddr(rec); return YIELD; } else return CANCEL; } case CLOSE : { qp->Close(args[0].addr); return 0; } } return 0; } /* 4.3 Specification of operator ~transformT2Rstream~ */ const string transformT2RstreamSpec = "( ( \"Signature\" " "\"Syntax\" \"Meaning\" \"Example\" ) " "( stream(tuple(y)) -> stream(record(y))" "" "_ transformT2Rstream" "Creates a stream of records from a stream " "of tuples." "query ten feed transformT2Rstream" ") )"; /* 4.4 Definition of operator ~transformT2Rstream~ */ Operator rectransformT2Rstream ( "transformT2Rstream", // name transformT2RstreamSpec, // specification transformT2RstreamValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function transformT2RstreamTypeMap // type mapping ); /* 5 Operator ~transformR2Tstream~ Creates a stream of tuples from a stream of records, each tuple contains all attributes of records 5.1 Type mapping function of operator ~transformR2Tstream~ */ ListExpr transformR2TstreamTypeMap(ListExpr args) { if(nl->ListLength(args)!=1){ ErrorReporter::ReportError("one argument expected"); return nl->TypeError(); } if(!Stream::checkType(nl->First(args))){ ErrorReporter::ReportError("stream(record(X)) expected"); return nl->TypeError(); } ListExpr attrList = nl->Rest(nl->Second(nl->First(args))); return nl->TwoElemList(nl->SymbolAtom( Stream::BasicType()), nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()), attrList)); } /* 5.2 Value mapping function of operator ~transformR2Tstream~ */ int transformR2TstreamValueMapping(Word* args, Word& result, int message, Word& local, Supplier s) { TupleType * resultTupleType; Record * rec; Word r; switch (message) { case OPEN : { qp->Open(args[0].addr); ListExpr resultType = GetTupleResultType( s ); resultTupleType = new TupleType( nl->Second( resultType ) ); local.setAddr( resultTupleType ); return 0; } case REQUEST : { resultTupleType = (TupleType *)local.addr; qp->Request(args[0].addr, r); if (qp->Received(args[0].addr)) { Tuple *newTuple = new Tuple( resultTupleType ); rec = static_cast(r.addr); for(int i = 0; i < rec->GetNoElements();i++){ newTuple->PutAttribute( i, ((Attribute*)rec->GetElement(i))); } rec->DeleteIfAllowed(); result.setAddr(newTuple); return YIELD; } else return CANCEL; } case CLOSE : { if(local.addr) { ((TupleType *)local.addr)->DeleteIfAllowed(); local.setAddr(0); } qp->Close(args[0].addr); return 0; } } return 0; } /* 5.3 Specification of operator ~transformR2Tstream~ */ const string transformR2TstreamSpec = "( ( \"Signature\" \"Syntax\" " "\"Meaning\" \"Example\" ) " "( stream(record(y)) -> stream(tuple(y))" "" "_ transformR2Tstream" "Creates a stream of tuples from a stream of " "records. " "query ten feed transformT2Rstream " "transformR2Tstream) )"; /* 5.4 Definition of operator ~transformR2Tstream~ */ Operator rectransformR2Tstream ( "transformR2Tstream", // name transformR2TstreamSpec, // specification transformR2TstreamValueMapping, // value mapping Operator::SimpleSelect, // trivial selection function transformR2TstreamTypeMap // type mapping ); /* 6 Implementation of the Algebra Class */ class RecordAlgebra : public Algebra { public: RecordAlgebra() : Algebra() { /* Registration of Types */ AddTypeConstructor( &recordTC ); recordTC.AssociateKind( Kind::DATA() ); /* Registration of Operators */ AddOperator( createRecordInfo(), createRecordVM, createRecordTM ); AddOperator( attrInfo(), attrVM, attrTM ); AddOperator(&rectransformT2Rstream); AddOperator(&rectransformR2Tstream); } /* Destructor */ ~RecordAlgebra() {}; }; } // end of namespace ~record~ extern "C" Algebra* InitializeRecordAlgebra(NestedList* nlRef, QueryProcessor* qpRef ) { // The C++ scope-operator :: must be used to qualify the full name return new record::RecordAlgebra; }