1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.table; |
7 | |
8 | import java.util.ArrayList; |
9 | import java.util.HashMap; |
10 | import org.h2.engine.Session; |
11 | import org.h2.expression.Expression; |
12 | import org.h2.expression.ExpressionVisitor; |
13 | import org.h2.table.TableFilter.TableFilterVisitor; |
14 | import org.h2.util.New; |
15 | |
16 | /** |
17 | * A possible query execution plan. The time required to execute a query depends |
18 | * on the order the tables are accessed. |
19 | */ |
20 | public class Plan { |
21 | |
22 | private final TableFilter[] filters; |
23 | private final HashMap<TableFilter, PlanItem> planItems = New.hashMap(); |
24 | private final Expression[] allConditions; |
25 | private final TableFilter[] allFilters; |
26 | |
27 | /** |
28 | * Create a query plan with the given order. |
29 | * |
30 | * @param filters the tables of the query |
31 | * @param count the number of table items |
32 | * @param condition the condition in the WHERE clause |
33 | */ |
34 | public Plan(TableFilter[] filters, int count, Expression condition) { |
35 | this.filters = new TableFilter[count]; |
36 | System.arraycopy(filters, 0, this.filters, 0, count); |
37 | final ArrayList<Expression> allCond = New.arrayList(); |
38 | final ArrayList<TableFilter> all = New.arrayList(); |
39 | if (condition != null) { |
40 | allCond.add(condition); |
41 | } |
42 | for (int i = 0; i < count; i++) { |
43 | TableFilter f = filters[i]; |
44 | f.visit(new TableFilterVisitor() { |
45 | @Override |
46 | public void accept(TableFilter f) { |
47 | all.add(f); |
48 | if (f.getJoinCondition() != null) { |
49 | allCond.add(f.getJoinCondition()); |
50 | } |
51 | } |
52 | }); |
53 | } |
54 | allConditions = new Expression[allCond.size()]; |
55 | allCond.toArray(allConditions); |
56 | allFilters = new TableFilter[all.size()]; |
57 | all.toArray(allFilters); |
58 | } |
59 | |
60 | /** |
61 | * Get the plan item for the given table. |
62 | * |
63 | * @param filter the table |
64 | * @return the plan item |
65 | */ |
66 | public PlanItem getItem(TableFilter filter) { |
67 | return planItems.get(filter); |
68 | } |
69 | |
70 | /** |
71 | * The the list of tables. |
72 | * |
73 | * @return the list of tables |
74 | */ |
75 | public TableFilter[] getFilters() { |
76 | return filters; |
77 | } |
78 | |
79 | /** |
80 | * Remove all index conditions that can not be used. |
81 | */ |
82 | public void removeUnusableIndexConditions() { |
83 | for (int i = 0; i < allFilters.length; i++) { |
84 | TableFilter f = allFilters[i]; |
85 | setEvaluatable(f, true); |
86 | if (i < allFilters.length - 1 || |
87 | f.getSession().getDatabase().getSettings().earlyFilter) { |
88 | // the last table doesn't need the optimization, |
89 | // otherwise the expression is calculated twice unnecessarily |
90 | // (not that bad but not optimal) |
91 | f.optimizeFullCondition(false); |
92 | } |
93 | f.removeUnusableIndexConditions(); |
94 | } |
95 | for (TableFilter f : allFilters) { |
96 | setEvaluatable(f, false); |
97 | } |
98 | } |
99 | |
100 | /** |
101 | * Calculate the cost of this query plan. |
102 | * |
103 | * @param session the session |
104 | * @return the cost |
105 | */ |
106 | public double calculateCost(Session session) { |
107 | double cost = 1; |
108 | boolean invalidPlan = false; |
109 | int level = 1; |
110 | for (TableFilter tableFilter : allFilters) { |
111 | PlanItem item = tableFilter.getBestPlanItem(session, level++); |
112 | planItems.put(tableFilter, item); |
113 | cost += cost * item.cost; |
114 | setEvaluatable(tableFilter, true); |
115 | Expression on = tableFilter.getJoinCondition(); |
116 | if (on != null) { |
117 | if (!on.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR)) { |
118 | invalidPlan = true; |
119 | break; |
120 | } |
121 | } |
122 | } |
123 | if (invalidPlan) { |
124 | cost = Double.POSITIVE_INFINITY; |
125 | } |
126 | for (TableFilter f : allFilters) { |
127 | setEvaluatable(f, false); |
128 | } |
129 | return cost; |
130 | } |
131 | |
132 | private void setEvaluatable(TableFilter filter, boolean b) { |
133 | filter.setEvaluatable(filter, b); |
134 | for (Expression e : allConditions) { |
135 | e.setEvaluatable(filter, b); |
136 | } |
137 | } |
138 | } |