/******************************************************************************** * * File: evaluation.gen * Title: The evaluator for OQL. * Programmer: Leonidas Fegaras, UTA * Date: 4/24/97 * *********************************************************************************/ #include "evaluation.h" #include "typecheck.h" #include "constant.h" list* Onil = new list; #define DEBUG(name,action) catch (object::evaluation_exception)\ { cout << "********************************************************************************\n"\ << #name << ": \n";\ action;\ throw object::evaluation_exception(); } /* a type error is a fatal error: it causes the program to abort */ TypeError ( string msg, Expr e ) { cout << "\n*** Type Error: " << msg << " (" << e << ")\n"; throw object::evaluation_exception(); }; /* the actual database (schema & data) */ database* DATABASE; /* all_plans holds the names of all physical plans (defined at optimizer.gen) */ extern list* all_plans; /* all primitive methods (eg. plus, and, etc) (defined at constant.cc) */ extern Internals internal_methods; Object TRUE; Object FALSE; /* defined in optimizer.gen: is computed_order stronger then expected_order? */ bool subsumes ( Expr computed_order, Expr expected_order ); template list* ntimes ( int n, T x ) { if (n) return Cons(x,ntimes(n-1,x)); else return new list; }; /* an evaluation error is a fatal error: it causes the program to abort * after it dumps the calling stack (all the functions that use the DEBUG macro) */ evaluation_error ( string msg, Object o ) { cout << "\n*** Run-time Error: " << msg << " (" << o << ")\n"; throw object::evaluation_exception(); }; Type schema_to_type ( Schema sch ) { list* args = Nil; for(Schema n = sch; n->more(); n=n->next()) args = args->cons(#current()->first)),`(n->current()->second))>); args = args->reverse(); return #; }; Schema type_to_schema ( Type tp ) { Schema res = new binding; #case tp | struct(...args) => for(list* r = args->reverse(); r->consp(); r=r->tl) #case r->hd | bind(`x,`t) => res = res->extend(x->name(),t); | _ => evaluation_error("(in type_to_schema) Ill-formed record type",new object()); #end; | _ => evaluation_error("(in type_to_schema) Expected a record type",new object()); #end; return res; }; Environment object_to_env ( Object o, Type tp ) { Environment res = new binding; #case tp | struct(...args) => if (o->aggregationp()) { list* s = o->info.Aggregation.Components; for(list* r = args->reverse(); r->consp(); r=r->tl, s=s->tl) #case r->hd | bind(`x,`t) => res = res->extend(x->name(),s->hd); | _ => evaluation_error("(in object_to_env) Ill-formed record type",new object()); #end; } else evaluation_error("(in object_to_env) Expected an aggregation",new object()); | _ => evaluation_error("(in object_to_env) Expected a record type",new object()); #end; return res; }; /*********************************************************************************/ interface::interface () { name = new_name(); primary_key = new list; schema = new binding; methods = new binding; supertype = NULL; subtypes = new list; data = NULL; }; interface::interface ( String nm ) { name = nm; primary_key = new list; schema = new binding; methods = new binding; supertype = NULL; subtypes = new list; data = NULL; }; interface::interface ( Interface intrf ) { name = intrf->name; primary_key = intrf->primary_key; schema = intrf->schema; methods = intrf->methods; supertype = intrf->supertype; subtypes = intrf->subtypes; data = intrf->data; }; interface::interface ( Expr class_def ) { /* compile an ODL class definition */ name = new_name(); primary_key = new list; schema = new binding; methods = new binding; supertype = NULL; subtypes = new list; data = NULL; #case class_def | interface(`nm,`super,info(...keys),...attrbs) => { name = nm->name(); schema = new binding; for(list* r = attrbs->reverse(); r->consp(); r=r->tl) #case r->hd | attribute(`v,`tp) => schema = schema->extend(v->name(),tp); | relationship(`v,`tp,_,_) => schema = schema->extend(v->name(),tp); | method(`f,`body,`tp,...params) => methods = methods->extend(f->name(), new pair(new pair(type_to_schema(#), tp),body)); #end; if (!(super->eq(#))) { Interface SI = find_interface(super->name()); supertype = SI; schema = SI->schema->extend(schema); SI->subtypes = SI->subtypes->cons(this); }; for(list* r = keys; r->consp(); r=r->tl) #case r->hd | extent(`ext) => { data = new extent(ext->name(),schema); DATABASE->content = DATABASE->content->extend(ext->name(),data); DATABASE->extents = DATABASE->extents->extend(name,ext->name()); }; | key(...keys) => { primary_key = new list; for(list* k = keys->reverse(); k->consp(); k=k->tl) primary_key = primary_key->cons(k->hd->name()); }; #end; DATABASE->schema = DATABASE->schema->extend(nm->name(),this); }; #end; }; /* display an interface */ ostream& operator<< ( ostream& s, Interface itrf ) { s << "interface: " << itrf->name << " " << schema_to_type(itrf->schema) << "\n ( key = " << itrf->primary_key; if (itrf->supertype != NULL) s << ", supertype = " << itrf->supertype->name; if (itrf->subtypes->consp()) { Names n = new list; for(list* r = itrf->subtypes->reverse(); r->consp(); r=r->tl) n = n->cons(r->hd->name); s << ", subtypes = " << n; }; s << " )\n"; s << "methods: \n"; for(binding* r = itrf->methods; r->more(); r=r->next()) s << " " << r->current()->first << r->current()->second->first << ": " << r->current()->second->second << endl; return s; }; Expr names_to_expr ( Names n ) { list* args = Nil; for(Names r = n->reverse(); r->consp(); r=r->tl) args = args->cons(variable(r->hd)); return #; }; Names expr_to_names ( Expr e ) { Names args = new list; for(list* r = e->arguments()->reverse(); r->consp(); r=r->tl) args = args->cons(r->hd->name()); return args; }; Interface interface_read ( ifstream& from ) { Interface I = new interface(); char c; int len; from.get(c); I->name = reads(from); I->primary_key = expr_to_names(read(from)); I->schema = type_to_schema(read(from)); from >> len; for(int i = 0; imethods = I->methods->extend(reads(from), new pair(new pair(type_to_schema(read(from)), read(from)),read(from))); String name = reads(from); if (!name->eq("")) I->supertype = new interface(name); /* will be set properly later */ Names ns = new list; from >> len; for(int i = 0; icons(reads(from)); for(Names r = ns->reverse(); r->consp(); r=r->tl) I->subtypes = I->subtypes->cons(new interface(r->hd)); /* will be set properly later */ I->data = new extent(reads(from),new binding); /* will be set properly later */ return I; }; interface::interface_write ( ofstream& to ) { name->write(to); names_to_expr(primary_key)->write(to); schema_to_type(schema)->write(to); to << ' ' << methods->names()->length(); for(binding* m = methods; m->more(); m=m->next()) { schema_to_type(m->current()->second->first->first)->write(to); m->current()->second->first->second->write(to); m->current()->second->second->write(to); }; if (supertype == NULL) (new string(""))->write(to); else supertype->name->write(to); to << ' ' << subtypes->length(); for(list* r = subtypes->reverse(); r->consp(); r=r->tl) r->hd->name->write(to); data->name->write(to); }; /*********************************************************************************/ extent::extent ( String nm, Schema sch ) { name = nm; schema = sch; indexes = new binding; data = Onil; cardinality = 0; object_size = sch->names()->length()+1; }; extent::extent ( Extent r ) { name = r->name; schema = r->schema; indexes = r->indexes; data = Onil; cardinality = 0; object_size = r->object_size; }; /* insert a new object in the extent */ extent::insert ( Object x ) { data = data->append(Cons(x,Onil)); cardinality++; }; /* find an index for this interface that delivers this order */ Expr extent::find_index ( Names order ) { if (order->nullp()) return #; for(binding* n = indexes; n->more(); n=n->next()) { list* idx = Nil; for(Names r = n->current()->second->reverse(); r->consp(); r=r->tl) idx = idx->cons(variable(r->hd)); if (subsumes(#,#)) return #current()->first)),order(...idx))>; }; return #; }; /* create a collection object that contains the extent */ Object extent::scan ( String var, Expr pred ) { Type element_type = schema_to_type(schema); Type etype = #; list* el = Onil; Environment env = new binding; for(list* r = data->reverse(); r->consp(); r=r->tl) { Object o = new object(etype,Onil->cons(r->hd)); if (o->predicate(pred,env)) el = el->cons(o); }; Type ctype = #; Object res = new object(ctype); res->info.Collection = el; return res; }; /* print the data content of an extent */ ostream& operator<< ( ostream& s, Extent e ) { s << "Extent: " << e->name << " " << schema_to_type(e->schema) << "\n ( size = " << e->object_size << ", cardinality = " << e->cardinality << " )\n" << e->data; return s; }; Extent extent_read ( ifstream& from ) { int len; String s = reads(from); Extent E = new extent(s,type_to_schema(read(from))); from >> E->object_size >> E->cardinality >> len; for(int i = 0; iindexes = E->indexes->extend(reads(from),expr_to_names(read(from))); from >> len; for(int i = 0; idata = E->data->cons(object_read(from)); return E; }; extent::extent_write ( ofstream& to ) { name->write(to); schema_to_type(schema)->write(to); to << ' ' << object_size << ' ' << cardinality << ' ' << indexes->names()->length(); for(binding* r = indexes; r->more(); r=r->next()) { r->current()->first->write(to); names_to_expr(r->current()->second)->write(to); }; to << ' ' << data->length(); for(list* r = data; r->consp(); r=r->tl) r->hd->object_write(to); }; /*********************************************************************************/ int object::available_OID = 0; object::object ( Type tp, list* r ) { tag = AGGREGATION; info.Aggregation.OID = available_OID; available_OID++; info.Aggregation.Components = r; otype = tp; }; object::object ( Type tp ) { tag = COLLECTION; info.Collection = Onil; otype = tp; }; /* shallow equality */ bool object::eq ( Object o ) { if (nullp() && o->nullp()) return true; if (integerp() && o->integerp()) return info.Integer == o->info.Integer; if (boolp() && o->boolp()) return info.Boolean == o->info.Boolean; if (stringp() && o->stringp()) return info.Estring->eq(o->info.Estring); if (aggregationp() && o->aggregationp()) return info.Aggregation.OID == o->info.Aggregation.OID; return (this == o); }; /* evaluate a projection */ Object object::projection ( String attribute ) { if (nullp()) return new object(); if (!aggregationp()) evaluation_error("(in method object::projection) Expected a record",this); Schema schema = type_to_schema(otype); list* r = info.Aggregation.Components; for(Schema n = schema; n->more(); n=n->next(), r=r->tl) if (n->current()->first->eq(attribute)) return r->hd; evaluation_error(form("(in method object::projection) No such attribute %s", attribute->value()),this); }; Object object::projection ( Names attributes ) { if (nullp()) return new object(); if (!aggregationp()) evaluation_error("(in method object::projection) Expected a record",this); Schema schema = type_to_schema(otype); list* atrbs = Onil; Schema sch = new binding; list* r; for(Names n = attributes; n->consp(); n=n->tl) { r = info.Aggregation.Components; for(Schema m = schema; m->more(); m=m->next(), r=r->tl) if (m->current()->first->eq(n->hd)) { sch = sch->extend(n->hd,m->current()->second); atrbs = atrbs->cons(r->hd); }; }; return new object(schema_to_type(sch),atrbs->reverse()); }; /* evaluate a method */ Object object::method ( String fnc, list* arguments, Environment& env ) { try { binding* methods = new binding; if (otype->variablep()) methods = find_interface(otype->name())->methods; if (methods->in(fnc)) { Method m = methods->find(fnc); if (m->first->first->length() != arguments->length()) evaluation_error("(in method object::method) Mismatched arguments in method",this); list* r = arguments->reverse(); binding* new_env = env; for(Names s = m->first->first->names()->reverse(); s->consp(); s=s->tl, r=r->tl) new_env = new_env->extend(s->hd,r->hd); return expression(m->second,new_env); } else evaluation_error(form("(in method object::method) No such method: %s",fnc->value()),this); } DEBUG(object::method,cout << this << endl << fnc << " " << arguments << endl << env << endl) }; /* functions that correspond to various monoid accumulators */ int sum (int x,int y) { return x+y; }; int max (int x,int y) { return x* zeros = (new binding) ->extend(new string("sum"),new object((int) 0)) ->extend(new string("max"),new object((int) 0)) ->extend(new string("min"),new object(32000)) ->extend(new string("all"),new object(true)) ->extend(new string("some"),new object(false)); if (zeros->in(monoid->name())) return zeros->find(monoid->name()); else return new object(#<`monoid(`(fresh_type_variable()))>); }; /* evaluate an OQL expression */ Object object::expression ( Expr e, Environment& env ) { try { if (false && trace_evaluation) cout << "*** EVAL: " << e << endl << " " << this << endl; Object res; #case e | nulll => res = new object(); | true => res = new object ((bool) true); | false => res = new object ((bool) false); | zero(`M) => res = zero(M); | unit(`M,`e) => { Object o = expression(e,env); if (M->name()->eq("sum") || M->name()->eq("min") || M->name()->eq("max") || M->name()->eq("all") || M->name()->eq("some")) res = o; else { res = new object(#<`M(`(o->otype))>); res->info.Collection = Onil->cons(o); }; }; | MERGE(`M,`x,`y) => { Object ox = evaluate_plan(x,env); Object oy = evaluate_plan(y,env); String accumulator = M->name(); if (accumulator->eq("sum") || accumulator->eq("min") || accumulator->eq("max")) { int (*acc) (int,int) = accumulator->eq("sum") ? sum : (accumulator->eq("min") ? min : max); res = new object((*acc)(ox->info.Integer,oy->info.Integer)); } else if (accumulator->eq("all") || accumulator->eq("some")) { bool (*acc) (bool,bool) = accumulator->eq("all") ? and : or; res = new object((*acc)(ox->info.Boolean,oy->info.Boolean)); } else { res = new object(ox->otype); res->info.Collection = ox->info.Collection->append(oy->info.Collection); }; }; | project(`e,`atrb) => res = expression(e,env)->projection(atrb->name()); | method(`f,...args) => { list* vals = Onil; for(list* r = args->reverse(); r->consp(); r=r->tl) vals = vals->cons(expression(r->hd,env)); res = method(f->name(),vals,env); }; | struct(...r) => { list* args = Onil; Schema schema = new binding; for(list* s = r->reverse(); s->consp(); s=s->tl) #case s->hd | bind(`x,`v) => args = args->cons(expression(v,env)); schema = schema->extend(x->name(),fresh_type_variable()); #end; res = new object(schema_to_type(schema),args); }; | if(`pred,`x,`y) => if (predicate(pred,env)) res = expression(x,env); else res = expression(y,env); | eq(`x,`y) => res = new object(expression(x,env)->eq(expression(y,env))); | neq(`x,`y) => res = new object(!expression(x,env)->eq(expression(y,env))); | and(...args) => { bool b = true; for(; b && args->consp(); args=args->tl) b = predicate(args->hd,env); res = new object(b); }; | or(...args) => { bool b = false; for(; !b && args->consp(); args=args->tl) b = predicate(args->hd,env); res = new object(b); }; | OID(`x) => { Object o = expression(x,env); if (o->nullp()) res = new object(-1); else if (o->aggregationp()) res = new object(o->info.Aggregation.OID); else evaluation_error("(in method object::expression) OID must be applied on a structure",o); }; | null => return new object(); | `f(...args) => if (member(f,all_interfaces())) /* method is a constructor */ { list* vals = Onil; for(list* r = args->reverse(); r->consp(); r=r->tl) vals = vals->cons(expression(r->hd,env)); Interface I = find_interface(f->name()); res = new object(schema_to_type(I->schema),vals); I->data->insert(res); for(Interface r = I->supertype; r; r=r->supertype) r->data->insert(new object(schema_to_type(r->schema),vals)); } else if (member(f,Nil->cons(#)->cons(#)->cons(#))) { list* vals = Onil; for(list* r = args; r->consp(); r=r->tl) vals = vals->cons(expression(r->hd,env)); res = new object(#); res->info.Collection = vals->reverse(); } else if (member(f,all_plans)) return evaluate_plan(e,env); else if (internal_methods->in(f->name())) { list* params = Onil; pair* p = internal_methods->find(f->name()); bool null = false; for(list* r = args; r->consp(); r=r->tl) { Object o = expression(r->hd,env); if (o->nullp()) null = true; params = params->cons(o); }; if (null) res = new object(); else res = (*p->second)(params->reverse()); } else evaluation_error(form("(in method object::expression) No such function: %s",f->name()->value()),this); | _ => if (e->integerp()) res = new object(e->info.Integer); else if (e->stringp()) res = new object(e->info.Estring); else if (e->variablep()) if (env->in(e->name())) res = env->find(e->name()); else if (type_to_schema(otype)->in(e->name())) res = projection(e->name()); else TypeError("Invalid expression",e); else TypeError("Invalid expression",e); #end; if (false && trace_evaluation) cout << "--> " << res << endl; return res; } DEBUG(object::expression,cout << this << endl << e << " " << env << endl) }; /* evaluate an OQL predicate */ bool object::predicate ( Expr pred, Environment& env ) { try { Object o = expression(pred,env); if (o->boolp()) return o->info.Boolean; if (o->nullp()) return false; evaluation_error("(in method object::predicate) Expected a boolean value",o); } DEBUG(object::predicate,cout << this << endl << pred << " " << env << endl) }; /* C style comparison: it returns attribute-x.attribute */ int object::compare ( Object x, String attribute ) { try { Object rx = projection(attribute); Object ry = x->projection(attribute); if (rx->integerp() && ry->integerp()) return rx->info.Integer - ry->info.Integer; if (rx->boolp() && ry->boolp()) return rx->info.Boolean - ry->info.Boolean; else if (rx->stringp() && ry->stringp()) return strcmp(rx->info.Estring->value(),ry->info.Estring->value()); else if (rx->aggregationp() && ry->aggregationp()) return rx->info.Aggregation.OID - ry->info.Aggregation.OID; else if (rx->nullp() && ry->nullp()) return 0; else if (rx->nullp()) return 1; else if (ry->nullp()) return -1; else evaluation_error("Can't do the comparison",this); } DEBUG(object::compare,cout << this << endl << x << endl << attribute << endl) }; /* true if for all attributes A in attributes: A=x.A */ bool object::equal_to ( Object x, Names attributes ) { try { for(Names r = attributes; r->consp(); r=r->tl) { int c = compare(x,r->hd); if (c != 0) return false; }; return true; } DEBUG(object::equal_to,cout << this << endl << x << endl << attributes << endl) }; /* true if for all attributes A in attributes: Aconsp(); r=r->tl) { int c = compare(x,r->hd); if (c>0) return false; if (c<0) return true; }; return false; } DEBUG(object::less_than,cout << this << endl << x << endl << attributes << endl) }; /* the NF2 unnest operator */ Object object::unnest ( String var, Expr path, Expr pred, bool keep, Environment& env ) { try { Schema schema; list* args = Nil; #case otype | list(struct(...n)) => { schema = type_to_schema(#); args = n; }; | _ => TypeError("(in method object::unnest) Expected a collection of records",otype); #end; Type tp; #case type_of(path,schema) | `f(`t) => tp = t; | `t => TypeError("(in method object::unnest) Unnesting a non-collection type",t); #end; Type rtp = #; Type itp = #; Object res = new object(#); for(list* s = info.Collection->reverse(); s->consp(); s=s->tl) { Object inner = s->hd->expression(path,env); if (inner->nullp()) { Object o = new object(rtp,s->hd->info.Aggregation.Components->cons(new object())); if (keep) res->info.Collection = res->info.Collection->cons(o); } else if (inner->collectionp()) { list* filtered_inner = Onil; Environment new_env = env->extend(object_to_env(s->hd,#)); for(list* r = inner->info.Collection; r->consp(); r=r->tl) if ((new object(itp,Onil->cons(r->hd)))->predicate(pred,new_env)) filtered_inner = filtered_inner->cons(r->hd); if (keep && filtered_inner->nullp()) res->info.Collection = res->info.Collection->cons( new object(rtp,s->hd->info.Aggregation.Components->cons(new object()))); for(list* k = filtered_inner; k->consp(); k=k->tl) res->info.Collection = res->info.Collection->cons( new object(rtp,s->hd->info.Aggregation.Components->cons(k->hd))); } else evaluation_error("(in method object::unnest) Expected a collection",inner); }; return res; } DEBUG(object::unnest,cout << this << endl << var << " " << path << " " << pred << " " << keep << endl << env << endl) }; /* map the function f (of parameter v) over the collection object */ Object object::map ( String var, Expr f, Environment &env ) { try { Schema schema; #case otype | list(`s) => schema = type_to_schema(s); | _ => evaluation_error("(in method object::map) Expected a collection",this); #end; Type tp = schema_to_type(schema->extend(var,type_of(f,schema))); Object res = new object(#); for(list* s = info.Collection->reverse(); s->consp(); s=s->tl) if (s->hd->nullp()); else { Object v = s->hd->expression(f,env); if (!v->nullp()) res->info.Collection = res->info.Collection->cons( new object(tp,s->hd->info.Aggregation.Components->cons(v))); }; return res; } DEBUG(object::map,cout << this << endl << var << " " << f << endl) }; /* reduces a collection to a value or collection */ Object object::reduce ( String accumulator, String var, Expr head, Expr pred, Environment& env ) { try { Schema schema; #case otype | list(`s) => schema = type_to_schema(s); | _ => evaluation_error("(in method object::reduce) Expected a collection",this); #end; Type tp = type_of(head,schema); if (accumulator->eq("list") || accumulator->eq("bag") || accumulator->eq("set")) { Object res = new object(#); for(list* s = info.Collection->reverse(); s->consp(); s=s->tl) if (s->hd->nullp()); else if (s->hd->predicate(pred,env)) { Object v = s->hd->expression(head,env); if (!v->nullp()) res->info.Collection = res->info.Collection->cons(v); }; return res; } else if (accumulator->eq("sum") || accumulator->eq("min") || accumulator->eq("max")) { int (*acc) (int,int) = accumulator->eq("sum") ? sum : (accumulator->eq("min") ? min : max); int zero = accumulator->eq("sum") ? 0 : (accumulator->eq("min") ? 32000 : 0); Object res = new object(zero); for(list* s = info.Collection->reverse(); s->consp(); s=s->tl) if (s->hd->nullp()); else if (s->hd->predicate(pred,env)) { Object v = s->hd->expression(head,env); if (v->integerp()) res->info.Integer = (*acc)(res->info.Integer,v->info.Integer); else if (!v->nullp()) evaluation_error("(in method object::reduce) Expected an integer",v); }; return res; } else if (accumulator->eq("all") || accumulator->eq("some")) { bool (*acc) (bool,bool) = accumulator->eq("all") ? and : or; bool zero = accumulator->eq("all") ? true : false; Object res = new object((bool) zero); for(list* s = info.Collection->reverse(); s->consp(); s=s->tl) if (s->hd->nullp()); else if (!s->hd->nullp() && s->hd->predicate(pred,env)) { Object v = s->hd->expression(head,env); if (v->boolp()) res->info.Boolean = (*acc)(res->info.Boolean,v->info.Boolean); else if (!v->nullp()) evaluation_error("(in method object::reduce) Expected a boolean",v); }; return res; }; TypeError("(in method object::reduce) Invalid monoid",variable(accumulator)); } DEBUG(object::reduce,cout << this << endl << accumulator << " " << var << " " << head << " " << pred << endl) }; /* group by groupby_vars; the input collection object must be sorted by groupby_vars; keep also the other_nestvars (from other nestings) */ Object object::group_by ( String accumulator, String var, Expr head, Names groupby_vars, Names other_nestvars, Expr pred, Environment& env ) { return nest(accumulator,var,head,groupby_vars,other_nestvars,pred,env); }; /* the NF2 nest operator */ Object object::nest ( String accumulator, String var, Expr head, Names groupby_vars, Names other_nestvars, Expr pred, Environment& env ) { try { Names gvars = groupby_vars->append(other_nestvars); if (!collectionp()) evaluation_error("(in method object::group_by) Expected a collection",this); Schema schema; #case otype | `f(`x) => schema = type_to_schema(x); | _ => TypeError("(in method object::group_by) Expected a collection",otype); #end; Schema sch = new binding; for(Names r = gvars->reverse(); r->consp(); r=r->tl) if (schema->in(r->hd)) sch = sch->extend(r->hd,schema->find(r->hd)); else TypeError("(in method object::group_by) The nesting attribute is not in the scope", variable(r->hd)); Type nesting_type = type_of(head,schema); Type tp; if (accumulator->eq("list") || accumulator->eq("bag") || accumulator->eq("set")) tp = #; else tp = nesting_type; Type rtp = schema_to_type(sch->extend(var,tp)); Object res = new object(#); for(list* r = info.Collection->reverse(); r->consp(); r=r->tl) { bool found = false; for(list* s = res->info.Collection; s->consp() && !found; s=s->tl) { if (r->hd->equal_to(s->hd,groupby_vars)) { found = true; s->hd->info.Aggregation.Components->hd->info.Collection = s->hd->info.Aggregation.Components->hd->info.Collection->append(Cons(r->hd,Onil)); }; }; if (!found) { Object o = new object(otype); o->info.Collection = o->info.Collection->cons(r->hd); Object co = new object(rtp,r->hd->projection(gvars)->info.Aggregation.Components->cons(o)); res->info.Collection = res->info.Collection->cons(co); }; }; for(list* r = res->info.Collection; r->consp(); r=r->tl) { r->hd->info.Aggregation.Components->hd /* destructive */ = r->hd->info.Aggregation.Components->hd->reduce(accumulator,var,head,#,env); r->hd->info.Aggregation.Components->hd->otype = tp; /* destructive */ }; return res; } DEBUG(object::nest,cout << this << endl << accumulator << " " << var << " " << head << " " << groupby_vars << " " << other_nestvars << " " << pred << endl << env << endl) }; /* nested-loop join */ Object object::nested_loop ( Object r, Expr pred, Expr keep, Environment& env ) { try { if (!collectionp() || !r->collectionp()) evaluation_error("(in method object::nested_loop) Expected collections",this); bool left_outerp = keep->eq(#); /* is it a left outer-join? */ bool right_outerp = keep->eq(#); /* is it a right outer-join? */ list* left_nulls; /* nulls for the left tuple */ list* right_nulls; /* nulls for the right tuple */ list* args; #case otype | list(struct(...m)) => #case r->otype | list(struct(...n)) => { args = m->append(n); left_nulls = ntimes(m->length(),new object()); right_nulls = ntimes(n->length(),new object()); }; | _ => TypeError("(in method object::nested_loop) Expected a collection type",r->otype); #end; | _ => TypeError("(in method object::nested_loop) Expected a collection type",otype); #end; Type rtp = #; Object res = new object(#); if (right_outerp) /* it's a right outer-join */ for(list* right = r->info.Collection->reverse(); right->consp(); right=right->tl) { int c = 0; for(list* left = info.Collection->reverse(); left->consp(); left=left->tl) { Object o = new object(rtp,left->hd->info.Aggregation.Components->append(right->hd->info.Aggregation.Components)); if (o->predicate(pred,env)) { res->info.Collection = res->info.Collection->cons(o); c++; }; }; if (c == 0) res->info.Collection = res->info.Collection->cons( new object(rtp,left_nulls->append(right->hd->info.Aggregation.Components))); } else for(list* left = info.Collection->reverse(); left->consp(); left=left->tl) { int c = 0; for(list* right = r->info.Collection->reverse(); right->consp(); right=right->tl) { Object o = new object(rtp,left->hd->info.Aggregation.Components->append(right->hd->info.Aggregation.Components)); if (o->predicate(pred,env)) { res->info.Collection = res->info.Collection->cons(o); c++; }; }; if (c == 0 && left_outerp) /* it's a left outer-join */ res->info.Collection = res->info.Collection->cons( new object(rtp,left->hd->info.Aggregation.Components->append(right_nulls))); }; return res; } DEBUG(object::nested_loop,cout << this << endl << r << endl << pred << endl << env << endl) }; /* construct the join predicate for sorting and merging; use the requested orders * of the two inputs for the equality tests */ Expr make_pred_for_merge ( const char* comparison, list* left_order, list* right_order, Schema sch ) { list* res = Nil; list* right = right_order; for(list* left = left_order; left->consp() && right->consp(); left=left->tl, right=right->tl) { /* due to the lack of polymorphism, I had to check types to select the right comparison function */ Expr cmp = (type_of(left->hd,sch)->eq(#)) ? variable(new string(form("s%s",comparison))) : variable(new string(comparison)); res = res->cons(#<`cmp(`(left->hd),`(right->hd))>); }; return #; }; /* sort a collection object by the attributes */ Object object::sort ( list* attributes, Environment& env ) { try { if (!collectionp()) evaluation_error("(in method object::sort) Expected a collection",this); Names ns; Type tp; #case otype | list(`x) => { tp = x; ns = type_to_schema(x)->names(); }; | _ => TypeError("(in method object::sort) Expected a collection type",otype); #end; String X = new_name(); String Y = new_name(); list* left = attributes; list* right = attributes; for(Names r = ns; r->consp(); r=r->tl) left = subst_list(variable(r->hd),#hd)))>,left); for(Names r = ns; r->consp(); r=r->tl) right = subst_list(variable(r->hd),#hd)))>,right); Expr pred = make_pred_for_merge("lt",left,right,(new binding)->extend(X,tp)->extend(Y,tp)); Object res = new object(otype); int len = info.Collection->length(); Object A[len]; int k = 0; for(list* r = info.Collection; r->consp() && ktl) A[k++] = r->hd; /* bubble sort */ for(int j = len-1; j>=1; j--) for(int i = 1; i<=j; i++) { Environment new_env = env->extend(X,A[i])->extend(Y,A[i-1]); if (predicate(pred,new_env)) { Object e = A[i-1]; A[i-1] = A[i]; A[i] = e; }; }; for(int i = len-1; i>=0; i--) res->info.Collection = res->info.Collection->cons(A[i]); return res; } DEBUG(object::sort,cout << this << endl << attributes << endl << env << endl); }; /* sort-merge join: it scans the sorted left and right inputs simultaneously */ Object object::merge_join ( Object r, Expr pred, list* left_order, list* right_order, Expr keep, Environment& env ) { try { if (!collectionp() || !r->collectionp()) evaluation_error("(in method object::merge_join) Expected collections",this); list* args = Nil; bool left_outerp = keep->eq(#); /* is it a left outer-join? */ bool right_outerp = keep->eq(#); /* is it a right outer-join? */ list* left_nulls; /* nulls for the left tuple */ list* right_nulls; /* nulls for the right tuple */ #case otype | list(struct(...m)) => #case r->otype | list(struct(...n)) => { args = m->append(n); left_nulls = ntimes(m->length(),new object()); right_nulls = ntimes(n->length(),new object()); }; | _ => TypeError("(in method object::merge_join) Expected a collection type",r->otype); #end; | _ => TypeError("(in method object::merge_join) Expected a collection type",otype); #end; Type rtp = #; Object res = new object(#); Schema sch = type_to_schema(rtp); Expr greater_than = make_pred_for_merge("gt",left_order,right_order,sch); Expr less_than = make_pred_for_merge("lt",left_order,right_order,sch); list* left = info.Collection; list* right = r->info.Collection; list* rd; while(left->consp() && right->consp()) { Object o = new object(rtp,left->hd->info.Aggregation.Components->append(right->hd->info.Aggregation.Components)); if (o->predicate(greater_than,env)) { if (right_outerp) { Object on = new object(rtp,left_nulls->append(right->hd->info.Aggregation.Components)); res->info.Collection = res->info.Collection->cons(on); }; right = right->tl; } else if (o->predicate(less_than,env)) { if (left_outerp) { Object on = new object(rtp,left->hd->info.Aggregation.Components->append(right_nulls)); res->info.Collection = res->info.Collection->cons(on); }; left = left->tl; } else { do { rd = right; do { o = new object(rtp,left->hd->info.Aggregation.Components->append(rd->hd->info.Aggregation.Components)); if (o->predicate(less_than,env)) break; if (o->predicate(pred,env)) res->info.Collection = res->info.Collection->cons(o); else if (right_outerp) res->info.Collection = res->info.Collection->cons(new object(rtp, left_nulls->append(left->hd->info.Aggregation.Components))); else if (left_outerp) res->info.Collection = res->info.Collection->cons(new object(rtp, left->hd->info.Aggregation.Components->append(right_nulls))); rd = rd->tl; } while (rd->consp()); left = left->tl; } while (left->consp() && rd->consp() && o->predicate(greater_than,env)); right = rd; }; }; if (left_outerp) while(left->consp()) { Object o = new object(rtp,left->hd->info.Aggregation.Components->append(right_nulls)); res->info.Collection = res->info.Collection->cons(o); left = left->tl; }; if (right_outerp) while(right->consp()) { Object o = new object(rtp,left_nulls->append(right->hd->info.Aggregation.Components)); res->info.Collection = res->info.Collection->cons(o); right = right->tl; }; res->info.Collection = res->info.Collection->reverse(); return res; } DEBUG(object::merge_join,cout << this << endl << r << endl << pred << endl << left_order << " " << right_order << endl << env << endl) }; Names attributes ( Expr e ) { Names res = new list; for(list* r = e->arguments()->reverse(); r->consp(); r=r->tl) res = res->cons(r->hd->name()); return res; }; String monoid_name ( Expr M ) { #case M | ALPHA(`x,`m) => return m->name(); | _ => return M->name(); #end; }; /* evaluate a plan */ Object evaluate_plan ( Expr plan, Environment& env ) { try { Object res; #case plan | TABLE_SCAN(`extent,`v,`pred) => res = find_extent(extent->name())->scan(v->name(),pred); | REDUCE(`M,`e,`v,`head,`pred) => res = evaluate_plan(e,env)->reduce(monoid_name(M),v->name(),head,pred,env); | NESTED_LOOP(`x,`y,`pred,`keep) => res = evaluate_plan(x,env)->nested_loop(evaluate_plan(y,env),pred,keep,env); | MERGE_JOIN(`x,`y,`pred,`keep,`left_order,`right_order) => res = evaluate_plan(x,env)->merge_join(evaluate_plan(y,env),pred,left_order->arguments(), right_order->arguments(),keep,env); | SORT(`e,`order) => res = evaluate_plan(e,env)->sort(order->arguments(),env); | UNNEST(`e,`v,`path,`pred,`keep) => res = evaluate_plan(e,env)->unnest(v->name(),path,pred,keep->eq(#),env); | NEST(`M,`e,`v,`head,`group,`nests,`pred) => res = evaluate_plan(e,env)->nest(monoid_name(M),v->name(),head,attributes(group),attributes(nests),pred,env); | GROUP_BY(`M,`e,`v,`head,`group,`nests,`pred) => res = evaluate_plan(e,env)->group_by(monoid_name(M),v->name(),head,attributes(group),attributes(nests),pred,env); | MAP( `e, `v, `f ) => res = evaluate_plan(e,env)->map(v->name(),f,env); | define(`tp,`x,`u) => { if (env->in(x->name())) *(env->find(x->name())) = *((new object())->expression(u,env)); /* destructive */ else env = env->extend(x->name(),(new object())->expression(u,env)); res = new object(); }; | _ => res = (new object())->expression(plan,env); #end; if (trace_evaluation) { cout << "The result of: "; plan->pretty_print(15); cout << "\nHas type: "; res->otype->pretty_print(10); cout << "\nResult: "; res->reify(0)->pretty_print(8); cout << "\n--------------------------------------------------------------------------------\n"; }; return res; } DEBUG(evaluate_plan,cout << plan << endl << env << endl) }; /* convert an object to an expr */ Expr object::reify ( int level ) { if (level > max_level) return variable(new string("...")); else if (nullp()) return #; else if (integerp()) return integer(info.Integer); else if (boolp()) return (info.Boolean ? # : #); else if (stringp()) return estring(info.Estring); else if (collectionp()) { list* res = Nil; for(list* r = info.Collection; r->consp(); r=r->tl) res = res->cons(r->hd->reify(level+1)); return #reverse()))>; } else if (aggregationp()) { list* res = Nil; Names n = type_to_schema(otype)->names(); for(list* r = info.Aggregation.Components; r->consp() && n->consp(); r=r->tl, n=n->tl) res = res->cons(#hd)),`(r->hd->reify(level+1)))>); return #reverse()))>; }; }; /* display an object */ ostream& operator<< ( ostream& s, Object o ) { return s << o->reify(0); }; Object object_read ( ifstream& from ) { Object res = new object(); short tag; char c; from >> tag; (int) res->tag = tag; from.get(c); res->otype = read(from); if (res->integerp()) from >> res->info.Integer; else if (res->boolp()) from >> res->info.Boolean; else if (res->stringp()) res->info.Estring = reads(from); else if (res->collectionp()) { int len; from >> len; res->info.Collection = Onil; for(int i=0; iinfo.Collection = res->info.Collection->cons(object_read(from)); res->info.Collection = res->info.Collection->reverse(); } else if (res->aggregationp()) { int len; from >> res->info.Aggregation.OID >> len; res->info.Aggregation.Components = Onil; for(int i=0; iinfo.Aggregation.Components = res->info.Aggregation.Components->cons(object_read(from)); res->info.Aggregation.Components = res->info.Aggregation.Components->reverse(); }; return res; }; object::object_write ( ofstream& to ) { to << " " << tag; otype->write(to); if (integerp()) to << " " << info.Integer; else if (boolp()) to << " " << info.Boolean; else if (stringp()) info.Estring->write(to); else if (collectionp()) { to << " " << info.Collection->length(); for(list* r = info.Collection; r->consp(); r=r->tl) r->hd->object_write(to); } else if (aggregationp()) { to << " " << info.Aggregation.OID << " " << info.Aggregation.Components->length(); for(list* r = info.Aggregation.Components; r->consp(); r=r->tl) r->hd->object_write(to); }; }; /*********************************************************************************/ database::database () { content = new binding; schema = new binding; extents = new binding; varspace = new binding; vartypes = new binding; }; /* read a database from a file */ database::database ( String file_name ) { ifstream from (file_name->value()); content = new binding; int exn; from >> exn; for(int i = 0; iextend(ext->name,ext); }; schema = new binding; int schemas; from >> schemas; for(int i = 0; iextend(inf->name,inf); }; extents = new binding; from >> exn; for(int i = 0; iextend(reads(from),reads(from)); for(Names r = schema->names(); r->consp(); r=r->tl) { Interface I = schema->find(r->hd); if (I->supertype) I->supertype = schema->find(I->supertype->name); for(list* s = I->subtypes->reverse(); s->consp(); s=s->tl) s->hd = schema->find(s->hd->name); I->data = content->find(I->data->name); }; vartypes = type_to_schema(read(from)); int names; from >> names; varspace = new binding; for(int i = 0; iextend(s,object_read(from)); }; from.close(); }; /* write a database to a file */ database::close ( String file_name ) { ofstream to (file_name->value()); to << " " << content->names()->length(); for(Names r = content->names(); r->consp(); r=r->tl) content->find(r->hd)->extent_write(to); to << " " << schema->names()->length(); for(Names r = schema->names(); r->consp(); r=r->tl) schema->find(r->hd)->interface_write(to); to << " " << extents->names()->length(); for(Names r = extents->names(); r->consp(); r=r->tl) { r->hd->write(to); extents->find(r->hd)->write(to); }; schema_to_type(vartypes)->write(to); to << " " << varspace->names()->length(); for(Names r = varspace->names(); r->consp(); r=r->tl) { r->hd->write(to); varspace->find(r->hd)->object_write(to); }; to.close(); }; /* print the entire database schema and content; long output */ ostream& operator<< ( ostream& s, database* db ) { for(Names r = db->schema->names(); r->consp(); r=r->tl) s << db->schema->find(r->hd) << endl; for(Names r = db->content->names(); r->consp(); r=r->tl) s << db->content->find(r->hd) << endl; return s; }; /* find an extent in the database */ Extent find_extent ( String name ) { if (!DATABASE->content->in(name)) TypeError("No such extent",variable(name)); return DATABASE->content->find(name); }; /* find an interface in the database */ Interface find_interface ( String name ) { if (!DATABASE->schema->in(name)) TypeError("No such interface",variable(name)); return DATABASE->schema->find(name); }; Names all_extents () { return DATABASE->content->names(); }; binding* extent_types () { return DATABASE->extents; }; Names all_interfaces () { return DATABASE->schema->names(); }; class_def ( Expr e ) { Interface I = new interface(e); };