/* // $Id: //guest/julian_hyde/saffron/src/main/saffron/opt/VolcanoPlanner.java#1 $ // Saffron preprocessor and data engine // Copyright (C) 2002 Julian Hyde <julian.hyde@mail.com> // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. // // See the COPYING file located in the top-level-directory of // the archive of this library for complete text of license. */ package saffron.opt; import openjava.mop.Environment; import openjava.mop.OJClass; import openjava.mop.OJClassNotFoundException; import openjava.ptree.ClassDeclaration; import openjava.tools.DebugOut; import saffron.Planner; import saffron.rel.*; import saffron.rel.convert.Converter; import saffron.rel.convert.ConverterRule; import saffron.rel.convert.RemoveConverterRule; import saffron.util.Util; import saffron.util.Walker; import java.util.HashMap; import java.util.Hashtable; import java.util.Vector; /** * VolcanoPlanner optimizes queries by transforming expressions selectively * according to a dynamic programming algorithm. **/ public class VolcanoPlanner extends Planner { /** The {@link Cluster} which all {@link saffron.rel.Rel}s belong to. */ Cluster cluster; /** Map each registered expression ({@link saffron.rel.Rel}) to its equivalence set * ({@link RelSubset}). **/ Hashtable mapRel2Subset; /** A set of registered expressions. (We would use {@link HashSet}, but we * need {@link HashMap#get} in order to retrieve the canonical instance of * an element.) */ HashMap allExps; /** List of all rules. */ Rule[] rules; /** Map classes ({@link Class}) to operands ({@link Operand}[]). (I'm not * sure that this works...) */ Hashtable mapClass2Operand; /** List of all operands of all rules. */ Operand[] allOperands; /** List of all sets. Used only for debugging. */ Vector allSets = new Vector(); private int nextSetId = 0; private static final Vector emptyVector = new Vector(); VolcanoPlanner(Cluster cluster, Rule[] rules) { current = this; this.cluster = cluster; this.mapRel2Subset = new Hashtable(); this.allExps = new HashMap(); this.rules = rules; // ModifierList modifierList = new ModifierList(ModifierList.PUBLIC); // ClassDeclaration classDecl = new ClassDeclaration( // modifierList, className, null, null, new MemberDeclarationList()); // OJClass declarer = null; // FileEnvironment env = new FileEnvironment( // openjava.mop.OJSystem.env, packageName, className); // this.tempClass = new OJClass(env, declarer, classDecl); // Build a mapping from classes to rule operands (even non-root // operands). The operands are first stored as vectors, then converted // to arrays. Vector allOperandsVector = new Vector(); this.mapClass2Operand = new Hashtable(); for (int i = 0; i < rules.length; i++) { Rule rule = rules[i]; rule.planner = this; Walker operandWalker = new Walker(rule.operand); int ordinalInRule = 0; Vector operandsOfRule = new Vector(); while (operandWalker.hasMoreElements()) { Operand operand = (Operand) operandWalker.nextElement(); operand.rule = rule; operand.parent = (Operand) operandWalker.getParent(); operand.ordinalInParent = operandWalker.getOrdinal(); operand.ordinalInRule = ordinalInRule++; operandsOfRule.addElement(operand); for (Class clazz = operand.clazz; clazz != null; clazz = clazz.getSuperclass()) { Vector operands = (Vector) mapClass2Operand.get(clazz); if (operands == null) { operands = new Vector(); mapClass2Operand.put(clazz, operands); } operands.addElement(operand); } allOperandsVector.addElement(operand); } // Convert this rule's operands from a vector to an array. rule.operands = new Operand[operandsOfRule.size()]; operandsOfRule.copyInto(rule.operands); // Build each operand's solve-order. Start with itself, then its // parent, up to the root, then the remaining operands in prefix // order. for (int j = 0; j < rule.operands.length; j++) { Operand operand = rule.operands[j]; operand.solveOrder = new int[rule.operands.length]; int m = 0; for (Operand o = operand; o != null; o = o.parent) { operand.solveOrder[m++] = o.ordinalInRule; } for (int k = 0; k < rule.operands.length; k++) { boolean exists = false; for (int n = 0; n < m; n++) { if (operand.solveOrder[n] == k) { exists = true; } } if (!exists) { operand.solveOrder[m++] = k; } } // Assert: operand appears once in the sort-order. Util.assert(m == rule.operands.length); } } // Convert the list of all operands from a vector to an array. allOperands = new Operand[allOperandsVector.size()]; allOperandsVector.copyInto(allOperands); // Convert each class's operands from a vector to an array. java.util.Enumeration classes = mapClass2Operand.keys(); while (classes.hasMoreElements()) { Class clazz = (Class) classes.nextElement(); Vector operandsVector = (Vector) mapClass2Operand.get(clazz); Operand[] operands = new Operand[operandsVector.size()]; for (int i = 0; i < operandsVector.size(); i++) { operands[i] = (Operand) operandsVector.elementAt(i); } mapClass2Operand.put(clazz, operands); } } public VolcanoPlanner(Cluster cluster) { this(cluster, new Rule[] { // todo: enable new Exp.IteratorExpressionRule(), // todo: enable new Exp.PlanExpressionRule(), // new Query.TranslateRule(), new NestedLoopJoinRule(CallingConvention.JAVA), new ConverterRule(CallingConvention.JAVA, OneRow.class), // new ArrayReader.ArrayReaderRule(CallingConvention.INLINE), // new ArrayReader.ArrayReaderRule(CallingConvention.PLAN), // new Project.ProjectRule(CallingConvention.INLINE), // new Project.ProjectRule(CallingConvention.PLAN), // todo: enable new In.InSemiJoinRule(), // todo: enable new Permute.PermuteToProjectRule(), // new VolcanoPlanner.ConverterRule(CallingConvention.PLAN), // new VolcanoPlanner.ConverterRule(CallingConvention.INLINE), // new VolcanoPlanner.ConverterRule(CallingConvention.ITERATOR), new RemoveConverterRule(CallingConvention.PLAN), new RemoveConverterRule(CallingConvention.JAVA), // todo: enable new RelWrapper.RemoveWrapperRule(), // todo: rule which makes Project({OrdinalRef}) disappear // todo: rule which folds Projects & Filters new DistinctToExistsRule(), new UnionToDistinctRule(), // new ExpressionReader.MapToHashtableReaderRule(), // new Converter.SwitchConventionRule(CallingConvention.ITERATOR, CallingConvention.INLINE), // new Converter.SwitchConventionRule(CallingConvention.INLINE, CallingConvention.ITERATOR), new RemoveTrivialProjectRule(), }); } // implement Planner public Cost makeCost(double dRows, double dCpu, double dIo) { return new VolcanoCost(dRows, dCpu, dIo); } // implement Planner public Cost makeInfiniteCost() { return VolcanoCost.INFINITY; } // implement Planner public Cost makeHugeCost() { return VolcanoCost.HUGE; } // implement Planner public Cost makeZeroCost() { return VolcanoCost.ZERO; } // implement Planner public Rel convert(Rel rel, int convention) { RelSubset subset = register(rel, null, MAY_BE_REGISTERED); RelSet set = subset.set; // first, see if there is an existing subset for "convention" RelSubset result = set.getSubset(convention); if (result != null) { return result; } // second, try to transform one of the expressions in the set for (int i = 0, n = set.rels.size(); i < n; i++) { Rel rel2 = (Rel) set.rels.elementAt(i); Rel converted = rel2.changeConvention(this, convention); if (converted != null) { register(converted, set, MAY_BE_REGISTERED); return converted; } } // expand the existing subsets until one is convertible to our // convention loop: while (true) { for (int i = 0, n = set.subsets.size(); i < n; i++) { RelSubset subset2 = (RelSubset) set.subsets.elementAt(i); if (Converter.canConvert( subset2.getConvention(), convention)) { break loop; } } // create all possible converters from the current subsets int count = 0; inner: for (int toConvention = 0; toConvention < CallingConvention.MAX; toConvention++) { if (set.getSubset(toConvention) != null) { continue; } // Try to convert each of the expressions in the set to // "toConvention" (it's better than using a converter -- and // it's the only way to convert rels of 'NONE' convention). for (int i = 0, n = set.rels.size(); i < n; i++) { Rel rel2 = (Rel) set.rels.elementAt(i); Rel converted = rel2.changeConvention(this, toConvention); if (converted != null) { register(converted, set, MAY_BE_REGISTERED); count++; continue inner; } } for (int fromConvention = 0; fromConvention < CallingConvention.MAX; fromConvention++) { if (fromConvention == toConvention) { continue; } if (Converter.canConvert(fromConvention, toConvention)) { RelSubset subset2 = set.getSubset(fromConvention); if (subset2 != null) { // create an expression for "toConvention" convert(subset2, toConvention); count++; continue inner; } } } } if (count == 0) { // no conversions were done this loop, so no further are // possible break; } } // create a converter from each subset whose convention != 'convention' // to 'convention' for (int i = 0, n = set.subsets.size(); i < n; i++) { RelSubset subset2 = (RelSubset) set.subsets.elementAt(i); int inConvention = subset2.getConvention(); if (inConvention == convention) { continue; } if (Converter.canConvert(inConvention, convention)) { Rel converter = Converter.create( cluster, subset2, subset2.getConvention(), convention); result = register(converter, set, CHILDREN_MUST_BE_REGISTERED); } } if (result != null) { Util.assert(result.getConvention() == convention); } return result; } // implement Planner public Rel findBestExp() { RelSubset subset = register(rel, null, MAY_BE_REGISTERED); Cost cost = makeHugeCost(); Rel previousPlan, plan = null; while (true) { previousPlan = plan; findBestPlan(subset, cost); if (!subset.bestCost.isLt(cost)) { // we couldn't do it break; } cost = subset.bestCost.multiplyBy(0.9); // lower the (limbo) bar } dump(); return subset.buildCheapestPlan(this); } void dump() { DebugOut.println("Sets:"); for (int i = 0, n = allSets.size(); i < n; i++) { RelSet set = (RelSet) allSets.elementAt(i); DebugOut.println("Set#" + i); for (int j = 0; j < set.subsets.size(); j++) { RelSubset subset = (RelSubset) set.subsets.elementAt(j); DebugOut.println( "\tSubset#" + j + ", rel#" + subset.getId() + ", convention=" + subset.getConvention() + ", best=" + (subset.best == null ? "null" : ("Rel#" + subset.best.getId()))); for (int k = 0; k < subset.rels.size(); k++) { Rel rel = (Rel) subset.rels.elementAt(k); DebugOut.print( "\t\tRel#" + rel.getId() + ": " + rel.getRelTypeName() + "("); Rel[] inputs = rel.getInputs(); for (int l = 0; l < inputs.length; l++) { if (l > 0) { DebugOut.print("; "); } Rel input = inputs[l]; RelSubset inputSubset = getSubset( input, input.getConvention()); RelSet inputSet = inputSubset.set; if (input instanceof RelSubset) { input = (Rel) inputSubset.rels.elementAt(0); DebugOut.print( "Set#" + inputSet.id + ", convention=" + input.getConvention() + ", Rel#" + input.getId() + "(" + input.getRelTypeName() + ")"); } else { DebugOut.print( "Rel#" + input.getId() + "(" + input.getRelTypeName() + ", set#" + inputSet.id + ", convention=" + input.getConvention() + ")"); } } DebugOut.println("), cost=" + getCost(rel)); } } } DebugOut.println(); } /** * Finds the cheapest plan for the expression-set <code>set</code> which * costs less than <code>targetCost</code>. This method uses a dynamic * programming algorithm. If it cannot find a plan cheap enough, the * previous best plan (if any) remains. **/ void findBestPlan(RelSubset subset, Cost targetCost) { if (subset.active) { return; // prevent cycles } if (subset.convention == CallingConvention.NONE) { return; // don't even bother } subset.active = true; // for (int i = 0, n = subset.rels.size(); i < n; i++) { for (int i = 0; i < subset.rels.size(); i++) { Rel rel = (Rel) subset.rels.elementAt(i); Util.assert( rel.getConvention() == subset.convention); Cost minCost = targetCost; if (subset.bestCost.isLt(minCost)) { // not enough to do better than our target -- we have to do better than // the best we already have minCost = subset.bestCost; } Cost cost = optimize(rel, minCost); if (cost.isLt(minCost)) { subset.best = rel; subset.bestCost = cost; } } subset.active = false; } /** * By optimizing its children, find the best implementation of relational * expression <code>rel</code>. The cost is bounded by * <code>targetCost</code>. */ Cost optimize(Rel rel, Cost targetCost) { // First, try to do the node itself. Cost nodeCost = rel.computeSelfCost(this); if (!nodeCost.isLt(targetCost)) { DebugOut.println( "Optimize: cannot implement [" + rel + "] in less than [" + targetCost + "]"); return makeInfiniteCost(); // no can do } Cost usedCost = nodeCost; // Second, figure out if we can do the children using the remaining // resources. Rel[] inputs = rel.getInputs(); for (int j = 0; j < inputs.length; j++) { // Because exp is registered, each relational child is a // RelSubset. RelSubset childSubset = (RelSubset) inputs[j]; Cost remainingCost = targetCost.minus(usedCost); findBestPlan(childSubset, remainingCost); // Use RelSubset.bestCost, not Rel.getCost(), because (a) it // includes children, (b) it prevents cycles during optimize, (c) // it potentially prevents expensive cost calculations on deep // trees. if (!childSubset.bestCost.isLt(remainingCost)) { DebugOut.println( "Optimize: cannot implement2 " + rel + ", cost=" + childSubset.bestCost); return makeInfiniteCost(); // no can do } usedCost = usedCost.plus(childSubset.bestCost); } DebugOut.println("Optimize: rel=" + rel.getId() + ", cost=" + usedCost); return usedCost; } /** * Finds the cost of a node. Similar to {@link #optimize}, but does not * create any expressions. **/ Cost getCost(Rel rel) { if (rel instanceof RelSubset) { return ((RelSubset) rel).bestCost; } if (rel.getConvention() == CallingConvention.NONE) { return makeInfiniteCost(); } Cost cost = rel.computeSelfCost(this); Rel[] inputs = rel.getInputs(); for (int i = 0, n = inputs.length; i < n; i++) { cost = cost.plus(getCost(inputs[i])); } return cost; } // implement Planner public Rel register(Rel rel, int fail) { return register(rel, null, fail); } private ClassDeclaration getTempClassDecl() { String className = cluster.env.currentClassName(); OJClass clazz; try { clazz = OJClass.forName(className); } catch (OJClassNotFoundException e) { throw Util.newInternal( e, "while looking up holder class " + className); } try { return clazz.getSourceCode(); } catch (openjava.mop.CannotAlterException e) { throw Util.newInternal( e, "holder class must be OJClassSourceCode"); } } /** * Register a new expression <code>exp</code>. If <code>set</code> is not * null, make the expression part of that equivalence set. If an identical * expression is already registered, we don't need to register this one. * * @param rel relational expression to register * @param set set that rel belongs to, or <code>null</code> * @param convention calling convention which <code>rel</code> supports * @param fail one of {@link #MUST_BE_REGISTERED}, {@link * #CHILDREN_MUST_BE_REGISTERED}, {@link #MAY_BE_REGISTERED} * @return the equivalence-set */ public RelSubset register(Rel rel, RelSet set, int fail) { if (rel instanceof RelSubset) { RelSubset subset = (RelSubset) rel; if (set != subset.set && set != null) { DebugOut.println( "Register rel#" + rel.getId() + " in set#" + subset.set.id + ", equivalent to set#" + set.id); set.mergeWith(this, subset.set); } return subset; } // Ensure that its sub-expressions are registered. rel.onRegister(this, fail); // If it is equivalent to an existing expression, return the set that // the equivalent expression belongs to. Rel equivExp = (Rel) lookup(rel); if (equivExp == null) { if (fail == MUST_BE_REGISTERED) { throw Util.newInternal(); } } else { Util.assert( equivExp.getConvention() == rel.getConvention() && equivExp.getClass() == rel.getClass()); RelSet equivSet = getSet(equivExp); if (equivSet != null) { RelSubset subset = equivSet.add(equivExp); return subset; } } // Place the expression in the appropriate equivalence set. if (set == null) { set = new RelSet(); set.id = nextSetId++; this.allSets.addElement(set); } RelSubset subset = set.add(rel); mapRel2Subset.put(rel, subset); allExps.put(rel, rel); // Create back-links from its children. Rel[] inputs = rel.getInputs(); for (int i = 0; i < inputs.length; i++) { RelSubset childSubset = (RelSubset) inputs[i]; childSubset.parents.addElement(rel); } // Fire rules. fireRules(rel.getParentEnv(), rel); return subset; } /** * Find an expression's equivalence set. If the expression is not * registered, return null. */ public RelSet getSet(Rel rel) { return getSubset(rel).set; } RelSubset getSubset(Rel rel) { if (rel instanceof RelSubset) { return (RelSubset) rel; } else { return (RelSubset) mapRel2Subset.get(rel); } } RelSubset getSubset(Rel rel, int convention) { if (rel instanceof RelSubset) { RelSubset subset = (RelSubset) rel; Util.assert(subset.convention == convention); return subset; } RelSet set = getSet(rel); if (set == null) { return null; } return set.getSubset(convention); } /** * Find a registered expression which is equal to <code>rel</code>. */ Rel lookup(Rel rel) { return (Rel) allExps.get(rel); } // todo: It would useful to be able to register an expression along with // the map required to permute its outputs into an equivalent form to // another expression, e.g. /* Expression register(Expression equivalent, int[] outputMap) { } */ /** * Find a list of rules which will be triggered by the addition of this * element. * * @param exp Expression which has just been created. * @param convention Convention by which <code>exp</code> will return its * results. Not currently used. */ void fireRules(Environment env, Rel rel) { Class clazz = rel.getClass(); /* Operand[] operands = (Operand[]) mapClass2Operand.get(clazz); if (operands == null) { return; } */ for (int i = 0; i < allOperands.length; i++) { Operand operand = allOperands[i]; if (operand.clazz.isAssignableFrom(clazz)) { RuleCall ruleCall = new RuleCall(env, operand); int solve = 0; int operandOrdinal = operand.solveOrder[solve]; ruleCall.rels[operandOrdinal] = rel; ruleCall.match(solve + 1); } } } private class RuleCall { Environment env; Operand operand0; Rel[] rels; Rule rule; RuleCall(Environment env, Operand operand) { this.env = env; this.operand0 = operand; this.rule = operand0.rule; this.rels = new Rel[operand0.rule.operands.length]; } Rule getRule() { return operand0.rule; } void match(int solve) { if (solve == rule.operands.length) { try { Rel rel = (Rel) rule.apply(env, rels); if (rel == null) { // the rule was not applicable } else { // Check that expression supports the desired // convention. DebugOut.println( "Rule " + rule + " arguments " + Util.toString(rels) + " created " + rel); Util.assert( rule.getConvention() == rel.getConvention()); RelSubset subset = register( rel, getSet(rels[0]), MAY_BE_REGISTERED); // Ensure that there is a converter between the // original convention and the new convention. Rel converted = convert( subset, rels[0].getConvention()); } } catch (Exception e) { throw Util.newInternal( e, "while applying rule " + rule.getDescription()); } } else { int operandOrdinal = operand0.solveOrder[solve], previousOperandOrdinal = operand0.solveOrder[solve - 1]; boolean ascending = operandOrdinal < previousOperandOrdinal; Operand operand = rule.operands[operandOrdinal]; Vector successors; if (ascending) { Util.assert( rule.operands[previousOperandOrdinal].parent == operand); RelSubset subset = getSubset( rels[previousOperandOrdinal], rule.getConvention()); if (subset == null) { successors = emptyVector; } else { successors = subset.parents; } } else { int parentOrdinal = operand.parent.ordinalInRule; Rel parentRel = rels[parentOrdinal]; Rel[] inputs = parentRel.getInputs(); RelSubset subset = (RelSubset) inputs[operand.ordinalInParent]; successors = subset.rels; } for (int i = 0, n = successors.size(); i < n; i++) { Rel rel = (Rel) successors.elementAt(i); if (operand.clazz.isAssignableFrom(rel.getClass())) { rels[operandOrdinal] = rel; match(solve + 1); } } } } } // private void matchUp(Expression exp, Operand operand) // { // Util.assert( // operand.clazz.isAssignableFrom(exp.getClass())); // Operand parentOperand = operand.parent; // if (parentOperand == null) { // // we have got to the top - now need to come down again // matchDown(operand.rule, exp); // } // int convention = operand.rule.getConvention(); // todo: // RelSubset subset = getSubset(exp, convention); // Vector parents = subset.parents; // for (int j = 0, nParents = parents.size(); j < nParents; j++) { // Expression parent = (Expression) parents.elementAt(j); // Class parentClass = parent.getClass(); // if (parentOperand.clazz.isAssignableFrom(parentClass)) { // matchUp(parent, parentOperand); // } // } // } // private void matchDown(Rule rule, Expression exp) // { // int iOperand = 0; // Expression[] exps = new Expression[rule.operands.length]; // exps[0] = exp; // matchDown(rule, exps, iOperand + 1); // } // private void matchDown(Rule rule, Expression[] exps, int iOperand) // { // if (iOperand == rule.operands.length) { // // have matched the rule // } else { // Operand operand = rule.operands[iOperand]; // int parentOrdinal = operand.parent.ordinalInRule; // Expression parentExp = exps[parentOrdinal]; // ParseTree[] children = Util.getChildren(parentExp); // RelSet set = (RelSet) children[operand.ordinalInParent]; // for (int i = 0, nExps = set.exps.size(); i < nExps; i++) { // Expression exp = (Expression) set.exps.elementAt(i); // if (operand.clazz.isAssignableFrom(exp.getClass())) { // exps[iOperand] = exp; // matchDown(rule, exps, iOperand + 1); // } // } // } // } // ------------------------------------------------------------------------ ; ; // ------------------------------------------------------------------------ ; // ------------------------------------------------------------------------ /** * A <code>RelSetUsage</code> is a usage of an {@link RelSet} with a * particular calling convention. Each node in an expression has an * RelSetUsage as input. When a node is created, its default convention * depends upon the conventions of its inputs. For a given set of input * conventions, a node supports one or more output conventions. One of * these is the default convention. * */ // static class RelSetUsage extends LogicalRel // { // RelSet expSet; // int convention; // // RelSetUsage(RelSet expSet, int convention) // { // this.expSet = expSet; // this.convention = convention; // } // // /** An <code>RelSetUsage</code> is its own clone. */ // public Object clone() throws CloneNotSupportedException // { // return this; // } // // // implement Rel // protected OJClass deriveRowType() // { // return expSet.rel.getRowType(); // } // // /** There are no children, as such. We throw an exception because you // * probably don't want to be walking over trees which contain // * <code>RelSet</code>s. // */ // public Object[] getChildren() // { // throw new UnsupportedOperationException(); // } // }; // ------------------------------------------------------------------------ /** Note that none of the methods modifies any member variables (besides * {@link #set}) */ static class VolcanoCost implements Cost { static final VolcanoCost INFINITY = new VolcanoCost( Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); static final VolcanoCost HUGE = new VolcanoCost( Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); static final VolcanoCost ZERO = new VolcanoCost( 0.0, 0.0, 0.0); double dRows, dCpu, dIo; VolcanoCost() { } VolcanoCost(double dRows, double dCpu, double dIo) { set(dRows, dCpu, dIo); } public Cost multiplyBy(double factor) { if (this == INFINITY) { return this; } return new VolcanoCost( dRows * factor, dCpu * factor, dIo * factor); } public Cost minus(Cost _that) { if (this == INFINITY) { return this; } VolcanoCost that = (VolcanoCost) _that; return new VolcanoCost( this.dRows - that.dRows, this.dCpu - that.dCpu, this.dIo - that.dIo); } public Cost plus(Cost _that) { VolcanoCost that = (VolcanoCost) _that; if (this == INFINITY || that == INFINITY) { return INFINITY; } return new VolcanoCost( this.dRows + that.dRows, this.dCpu + that.dCpu, this.dIo + that.dIo); } public boolean equals(Cost _that) { if (!(_that instanceof VolcanoCost)) { return false; } VolcanoCost that = (VolcanoCost) _that; return this == that || this.dRows == that.dRows && this.dCpu == that.dCpu && this.dIo == that.dIo; } public boolean isLe(Cost _that) { VolcanoCost that = (VolcanoCost) _that; return this == that || this.dRows <= that.dRows && this.dCpu <= that.dCpu && this.dIo <= that.dIo; } public boolean isLt(Cost _that) { return isLe(_that) && !equals(_that); } public boolean isInfinite() { return this == INFINITY || this.dRows == Double.POSITIVE_INFINITY || this.dCpu == Double.POSITIVE_INFINITY || this.dIo == Double.POSITIVE_INFINITY; } public String toString() { return "{" + dRows + " rows, " + dCpu + " cpu, " + dIo + " io}"; } public double getRows() { return dRows; } public double getCpu() { return dCpu; } public double getIo() { return dIo; } public void set(double dRows, double dCpu, double dIo) { this.dRows = dRows; this.dCpu = dCpu; this.dIo = dIo; } }; // ------------------------------------------------------------------------ /** * ExpCompiler walks over an expression tree, calling {@link * Rel#getRowType} on each relational node, and replacing each class * with the compiled form of that class. */ // static private class ExpCompiler extends EvaluationShuttle // { // public void visit( // Rel p, int ordinalInParent, ParseTree parent) // { // p.compileType(); // p.childrenAccept(this); // } // }; } // End VolcanoPlanner.java
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#4 | 1853 | Julian Hyde |
saffron: Further improve binding of rows to variables. |
||
#3 | 1801 | Julian Hyde |
saffron: add ObjectSchema; rules can now be matched more than once; started to implement correlations in queries in from list. |
||
#2 | 1474 | Julian Hyde |
saffron: Aggregations are working. Renamed 'aggregator' to 'aggregation'. |
||
#1 | 1467 | Julian Hyde |
saffron: First saffron check-in; incorporate my changes to openjava. |