1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.graph.query.optimize;
25
26 import java.util.HashMap;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Map;
30 import net.jcip.annotations.Immutable;
31 import org.modeshape.graph.GraphI18n;
32 import org.modeshape.graph.query.QueryContext;
33 import org.modeshape.graph.query.model.Column;
34 import org.modeshape.graph.query.model.EquiJoinCondition;
35 import org.modeshape.graph.query.model.JoinCondition;
36 import org.modeshape.graph.query.model.SameNodeJoinCondition;
37 import org.modeshape.graph.query.model.SelectorName;
38 import org.modeshape.graph.query.plan.PlanNode;
39 import org.modeshape.graph.query.plan.PlanUtil;
40 import org.modeshape.graph.query.plan.PlanNode.Property;
41 import org.modeshape.graph.query.plan.PlanNode.Type;
42 import org.modeshape.graph.query.validate.Schemata;
43 import org.modeshape.graph.query.validate.Schemata.Table;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 @Immutable
74 public class RewriteIdentityJoins implements OptimizerRule {
75
76 public static final RewriteIdentityJoins INSTANCE = new RewriteIdentityJoins();
77
78
79
80
81
82
83
84 public PlanNode execute( QueryContext context,
85 PlanNode plan,
86 LinkedList<OptimizerRule> ruleStack ) {
87 if (!context.getHints().hasJoin) return plan;
88
89
90 Map<SelectorName, SelectorName> rewrittenSelectors = null;
91 int rewrittenJoins = 0;
92 int numJoins = 0;
93 for (PlanNode joinNode : plan.findAllAtOrBelow(Type.JOIN)) {
94 ++numJoins;
95 JoinCondition condition = joinNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
96 if (condition instanceof EquiJoinCondition) {
97 PlanNode leftNode = joinNode.getFirstChild().findAtOrBelow(Type.SOURCE);
98 PlanNode rightNode = joinNode.getLastChild().findAtOrBelow(Type.SOURCE);
99 assert leftNode != null;
100 assert rightNode != null;
101 EquiJoinCondition equiJoin = (EquiJoinCondition)condition;
102
103 Schemata schemata = context.getSchemata();
104 assert schemata != null;
105 SelectorName leftTableName = leftNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
106 SelectorName rightTableName = rightNode.getProperty(Property.SOURCE_NAME, SelectorName.class);
107 assert leftTableName != null;
108 assert rightTableName != null;
109
110 if (!leftTableName.equals(rightTableName)) {
111
112 continue;
113 }
114
115 Table table = schemata.getTable(leftTableName);
116 if (table == null) {
117 context.getProblems().addError(GraphI18n.tableDoesNotExist, leftTableName);
118 continue;
119 }
120 String leftColumnName = equiJoin.getProperty1Name();
121 String rightColumnName = equiJoin.getProperty2Name();
122 Schemata.Column leftColumn = table.getColumn(leftColumnName);
123 Schemata.Column rightColumn = table.getColumn(rightColumnName);
124 if (leftColumn == null) {
125 context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, leftColumnName, leftTableName);
126 continue;
127 }
128 if (rightColumn == null) {
129 context.getProblems().addError(GraphI18n.columnDoesNotExistOnTable, rightColumnName, leftTableName);
130 continue;
131 }
132
133 if (table.hasKey(leftColumn) && (rightColumn == leftColumn || table.hasKey(rightColumn))) {
134
135 if (rewrittenSelectors == null) rewrittenSelectors = new HashMap<SelectorName, SelectorName>();
136 rewriteJoinNode(context, joinNode, rewrittenSelectors);
137 ++rewrittenJoins;
138 }
139 } else if (condition instanceof SameNodeJoinCondition) {
140 SameNodeJoinCondition sameNodeCondition = (SameNodeJoinCondition)condition;
141 if (sameNodeCondition.getSelector1Name().equals(sameNodeCondition.getSelector2Name())
142 && sameNodeCondition.getSelector2Path() == null) {
143
144 if (rewrittenSelectors == null) rewrittenSelectors = new HashMap<SelectorName, SelectorName>();
145 rewriteJoinNode(context, joinNode, rewrittenSelectors);
146 ++rewrittenJoins;
147 }
148 }
149 }
150
151 if (rewrittenSelectors != null && !rewrittenSelectors.isEmpty()) {
152
153
154
155 ruleStack.addFirst(this);
156
157
158 if (!(ruleStack.peek() instanceof PushSelectCriteria)) {
159
160 ruleStack.addFirst(PushProjects.INSTANCE);
161 if (context.getHints().hasCriteria) {
162 ruleStack.addFirst(PushSelectCriteria.INSTANCE);
163 }
164 }
165
166
167 PlanUtil.replaceReferencesToRemovedSource(context, plan, rewrittenSelectors);
168
169 assert rewrittenJoins > 0;
170 if (rewrittenJoins == numJoins) {
171 assert plan.findAllAtOrBelow(Type.JOIN).isEmpty();
172 context.getHints().hasJoin = false;
173 }
174 }
175 return plan;
176 }
177
178 protected void rewriteJoinNode( QueryContext context,
179 PlanNode joinNode,
180 Map<SelectorName, SelectorName> rewrittenSelectors ) {
181
182 PlanNode rightChild = joinNode.getLastChild();
183 rightChild.removeFromParent();
184 PlanNode rightSource = rightChild.findAtOrBelow(Type.SOURCE);
185
186
187 PlanNode leftChild = joinNode.getFirstChild();
188 joinNode.extractFromParent();
189 PlanNode leftSource = leftChild.findAtOrBelow(Type.SOURCE);
190
191
192 PlanNode rightProject = rightChild.findAtOrBelow(Type.PROJECT);
193 if (rightProject != null) {
194 PlanNode leftProject = leftChild.findAtOrBelow(Type.PROJECT);
195 if (leftProject != null) {
196 List<Column> leftColumns = leftProject.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class);
197 for (Column rightColumn : rightProject.getPropertyAsList(Property.PROJECT_COLUMNS, Column.class)) {
198 if (!leftColumns.contains(rightColumn)) leftColumns.add(rightColumn);
199 }
200 } else {
201
202 leftProject = new PlanNode(Type.PROJECT);
203 leftProject.setProperty(Property.PROJECT_COLUMNS, rightProject.getProperty(Property.PROJECT_COLUMNS));
204 leftChild.getFirstChild().insertAsParent(leftProject);
205 }
206 }
207
208
209 PlanNode topRightSelect = rightChild.findAtOrBelow(Type.SELECT);
210 if (topRightSelect != null) {
211 PlanNode bottomRightSelect = topRightSelect;
212 while (true) {
213 if (bottomRightSelect.getFirstChild().isNot(Type.SELECT)) break;
214 bottomRightSelect = bottomRightSelect.getFirstChild();
215 }
216 topRightSelect.setParent(null);
217 bottomRightSelect.removeAllChildren();
218
219 leftSource.getParent().addLastChild(topRightSelect);
220 leftSource.setParent(bottomRightSelect);
221 }
222
223
224 SelectorName rightTableName = rightSource.getProperty(Property.SOURCE_NAME, SelectorName.class);
225 SelectorName rightTableAlias = rightSource.getProperty(Property.SOURCE_ALIAS, SelectorName.class);
226 SelectorName leftTableAlias = leftSource.getProperty(Property.SOURCE_ALIAS, SelectorName.class);
227 if (leftTableAlias != null) {
228 if (rightTableName != null) rewrittenSelectors.put(rightTableName, leftTableAlias);
229 if (rightTableAlias != null) rewrittenSelectors.put(rightTableAlias, leftTableAlias);
230 } else {
231 SelectorName leftTableName = leftSource.getProperty(Property.SOURCE_NAME, SelectorName.class);
232 assert leftTableName != null;
233 if (rightTableName != null) rewrittenSelectors.put(rightTableName, leftTableName);
234 if (rightTableAlias != null) rewrittenSelectors.put(rightTableAlias, leftTableName);
235 }
236 }
237
238
239
240
241
242
243 @Override
244 public String toString() {
245 return getClass().getSimpleName();
246 }
247
248 }