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.Collections;
27 import java.util.HashSet;
28 import java.util.LinkedList;
29 import java.util.Set;
30 import net.jcip.annotations.Immutable;
31 import org.modeshape.graph.query.QueryContext;
32 import org.modeshape.graph.query.model.Column;
33 import org.modeshape.graph.query.model.Constraint;
34 import org.modeshape.graph.query.model.EquiJoinCondition;
35 import org.modeshape.graph.query.model.JoinCondition;
36 import org.modeshape.graph.query.model.PropertyExistence;
37 import org.modeshape.graph.query.model.PropertyValue;
38 import org.modeshape.graph.query.model.ReferenceValue;
39 import org.modeshape.graph.query.model.SameNodeJoinCondition;
40 import org.modeshape.graph.query.model.SelectorName;
41 import org.modeshape.graph.query.model.Visitable;
42 import org.modeshape.graph.query.model.Visitors;
43 import org.modeshape.graph.query.model.Visitors.AbstractVisitor;
44 import org.modeshape.graph.query.plan.PlanNode;
45 import org.modeshape.graph.query.plan.PlanUtil;
46 import org.modeshape.graph.query.plan.PlanNode.Property;
47 import org.modeshape.graph.query.plan.PlanNode.Type;
48
49
50
51
52
53 @Immutable
54 public class CopyCriteria implements OptimizerRule {
55
56 public static final CopyCriteria INSTANCE = new CopyCriteria();
57
58
59
60
61
62
63
64 public PlanNode execute( QueryContext context,
65 PlanNode plan,
66 LinkedList<OptimizerRule> ruleStack ) {
67 Set<PlanNode> copiedSelectNodes = new HashSet<PlanNode>();
68
69 for (PlanNode join : plan.findAllAtOrBelow(Type.JOIN)) {
70
71 JoinCondition joinCondition = join.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
72 if (joinCondition instanceof EquiJoinCondition) {
73 EquiJoinCondition equiJoinCondition = (EquiJoinCondition)joinCondition;
74 SelectorName selector1 = equiJoinCondition.selector1Name();
75 SelectorName selector2 = equiJoinCondition.selector2Name();
76 String property1 = equiJoinCondition.property1Name();
77 String property2 = equiJoinCondition.property2Name();
78
79
80 PlanNode node = join.getParent();
81 while (node != null) {
82 if (!copiedSelectNodes.contains(node)) {
83 PlanNode copy = copySelectNode(context, node, selector1, property1, selector2, property2);
84 if (copy != null) {
85 node.insertAsParent(copy);
86 copiedSelectNodes.add(node);
87 copiedSelectNodes.add(copy);
88 } else {
89 copy = copySelectNode(context, node, selector2, property2, selector1, property1);
90 if (copy != null) {
91 node.insertAsParent(copy);
92 copiedSelectNodes.add(node);
93 copiedSelectNodes.add(copy);
94 }
95 }
96 }
97 node = node.getParent();
98 }
99 }
100
101 if (joinCondition instanceof EquiJoinCondition || joinCondition instanceof SameNodeJoinCondition) {
102
103 PlanNode left = join.getFirstChild();
104 PlanNode right = join.getLastChild();
105 copySelectNodes(context, left, right);
106 copySelectNodes(context, right, left);
107 }
108 }
109 return plan;
110 }
111
112 protected void copySelectNodes( QueryContext context,
113 PlanNode fromJoined,
114 PlanNode toJoined ) {
115
116 Set<SelectorName> toSelectors = new HashSet<SelectorName>();
117 for (PlanNode toNode : toJoined.findAllAtOrBelow()) {
118 toSelectors.addAll(toNode.getSelectors());
119 }
120
121 PlanNode nodeBelowSelects = null;
122
123
124 for (PlanNode select : fromJoined.findAllAtOrBelow(Type.SELECT)) {
125
126 if (toSelectors.containsAll(select.getSelectors())) {
127
128 PlanNode copy = new PlanNode(Type.SELECT, select.getSelectors());
129 copy.setProperty(Property.SELECT_CRITERIA, select.getProperty(Property.SELECT_CRITERIA));
130
131 if (nodeBelowSelects == null) {
132 nodeBelowSelects = toJoined.findAtOrBelow(Type.SOURCE, Type.JOIN, Type.SET_OPERATION, Type.NULL);
133 if (nodeBelowSelects == null) {
134 nodeBelowSelects = toJoined;
135 }
136 }
137 nodeBelowSelects.insertAsParent(copy);
138 nodeBelowSelects = copy;
139 }
140 }
141 }
142
143 protected PlanNode copySelectNode( QueryContext context,
144 PlanNode selectNode,
145 SelectorName selectorName,
146 String propertyName,
147 SelectorName copySelectorName,
148 String copyPropertyName ) {
149 if (selectNode.isNot(Type.SELECT)) return null;
150 if (selectNode.getSelectors().size() != 1 || !selectNode.getSelectors().contains(selectorName)) return null;
151
152 Constraint constraint = selectNode.getProperty(Property.SELECT_CRITERIA, Constraint.class);
153 Set<Column> columns = getColumnsReferencedBy(constraint);
154 if (columns.size() != 1) return null;
155 Column column = columns.iterator().next();
156 if (!column.selectorName().equals(selectorName)) return null;
157 if (!column.propertyName().equals(propertyName)) return null;
158
159
160
161
162
163 PlanNode copy = new PlanNode(Type.SELECT, copySelectorName);
164
165
166 PlanUtil.ColumnMapping mappings = new PlanUtil.ColumnMapping(selectorName);
167 mappings.map(propertyName, new Column(copySelectorName, copyPropertyName, copyPropertyName));
168 Constraint newCriteria = PlanUtil.replaceReferences(context, constraint, mappings, copy);
169 copy.setProperty(Property.SELECT_CRITERIA, newCriteria);
170
171 return copy;
172 }
173
174
175
176
177
178
179 @Override
180 public String toString() {
181 return getClass().getSimpleName();
182 }
183
184
185
186
187
188
189
190
191 public static Set<Column> getColumnsReferencedBy( Visitable visitable ) {
192 if (visitable == null) return Collections.emptySet();
193 final Set<Column> symbols = new HashSet<Column>();
194
195 Visitors.visitAll(visitable, new AbstractVisitor() {
196 protected void addColumnFor( SelectorName selectorName,
197 String property ) {
198 symbols.add(new Column(selectorName, property, property));
199 }
200
201 @Override
202 public void visit( Column column ) {
203 symbols.add(column);
204 }
205
206 @Override
207 public void visit( EquiJoinCondition joinCondition ) {
208 addColumnFor(joinCondition.selector1Name(), joinCondition.property1Name());
209 addColumnFor(joinCondition.selector2Name(), joinCondition.property2Name());
210 }
211
212 @Override
213 public void visit( PropertyExistence prop ) {
214 addColumnFor(prop.selectorName(), prop.propertyName());
215 }
216
217 @Override
218 public void visit( PropertyValue prop ) {
219 addColumnFor(prop.selectorName(), prop.propertyName());
220 }
221
222 @Override
223 public void visit( ReferenceValue ref ) {
224 String propertyName = ref.propertyName();
225 if (propertyName != null) {
226 addColumnFor(ref.selectorName(), propertyName);
227 }
228 }
229 });
230 return symbols;
231 }
232
233 }