View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.query.optimize;
25  
26  import java.util.HashSet;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.Set;
30  import org.modeshape.graph.query.QueryContext;
31  import org.modeshape.graph.query.model.Column;
32  import org.modeshape.graph.query.model.SelectorName;
33  import org.modeshape.graph.query.plan.PlanNode;
34  import org.modeshape.graph.query.plan.PlanUtil;
35  import org.modeshape.graph.query.plan.PlanNode.Property;
36  import org.modeshape.graph.query.plan.PlanNode.Type;
37  
38  /**
39   * This rule attempts to ensure the proper location of {@link Type#PROJECT} nodes. For example, every {@link Type#ACCESS} node
40   * needs to have a PROJECT node below it and above any other nodes (e.g., {@link Type#SELECT} or {@link Type#SOURCE} nodes). This
41   * rule ensures that the PROJECT exists, but it also attempts to reduce any unnecessary columns in existing PROJECT nodes.
42   */
43  public class PushProjects implements OptimizerRule {
44  
45      public static final PushProjects INSTANCE = new PushProjects();
46  
47      /**
48       * {@inheritDoc}
49       * 
50       * @see org.modeshape.graph.query.optimize.OptimizerRule#execute(org.modeshape.graph.query.QueryContext,
51       *      org.modeshape.graph.query.plan.PlanNode, java.util.LinkedList)
52       */
53      public PlanNode execute( QueryContext context,
54                               PlanNode plan,
55                               LinkedList<OptimizerRule> ruleStack ) {
56  
57          // Find all of the ACCESS nodes to make sure there is a PROJECT ...
58          for (PlanNode access : plan.findAllAtOrBelow(Type.ACCESS)) {
59              // Find the first node that is below any LIMIT, SORT, or DUP_REMOVE ...
60              PlanNode parentOfProject = access;
61              while (parentOfProject.isOneOf(Type.LIMIT, Type.SORT, Type.DUP_REMOVE)) {
62                  parentOfProject = parentOfProject.getParent();
63              }
64  
65              // Is there already a PROJECT here ?
66              assert parentOfProject.getChildCount() == 1; // should only have one child ...
67              PlanNode child = parentOfProject.getFirstChild(); // should only have one child ...
68              if (child.is(Type.PROJECT)) {
69                  // Check to see if there is a PROJECT above the access node ...
70                  PlanNode accessParent = access.getParent();
71                  if (accessParent == null || accessParent.isNot(Type.PROJECT)) continue;
72                  // Otherwise, the parent is a PROJECT, but there is another PROJECT above the ACCESS node.
73                  // Remove the lower PROJECT so the next code block moves the top PROJECT down below the ACCESS node ...
74                  assert accessParent.is(Type.PROJECT);
75                  child.extractFromParent();
76                  child = parentOfProject.getFirstChild();
77              }
78  
79              // If the parent of the ACCESS node is a PROJECT, then we can simply move it to here ...
80              PlanNode accessParent = access.getParent();
81              if (accessParent != null && accessParent.is(Type.PROJECT)) {
82                  PlanNode project = accessParent;
83                  if (project == plan) {
84                      // The PROJECT node is the root, so the ACCESS node will be the root ...
85                      plan = access;
86                      access.removeFromParent();
87                  } else {
88                      project.extractFromParent();
89                  }
90                  child.insertAsParent(project);
91                  if (plan == access) return plan;
92  
93                  // We need to make sure we have all of the columns needed for any ancestor ...
94                  List<Column> requiredColumns = PlanUtil.findRequiredColumns(context, project);
95                  project.setProperty(Property.PROJECT_COLUMNS, requiredColumns);
96                  project.addSelectors(getSelectorsFor(requiredColumns));
97                  continue;
98              }
99  
100             // There is no PROJECT, so find the columns that are required by the plan above this point ...
101             List<Column> requiredColumns = PlanUtil.findRequiredColumns(context, child);
102 
103             // And insert the PROJECT ...
104             PlanNode projectNode = new PlanNode(Type.PROJECT);
105             projectNode.setProperty(Property.PROJECT_COLUMNS, requiredColumns);
106             projectNode.addSelectors(getSelectorsFor(requiredColumns));
107             child.insertAsParent(projectNode);
108         }
109         return plan;
110     }
111 
112     protected Set<SelectorName> getSelectorsFor( List<Column> columns ) {
113         Set<SelectorName> selectors = new HashSet<SelectorName>();
114         for (Column column : columns) {
115             selectors.add(column.getSelectorName());
116         }
117         return selectors;
118     }
119 
120     /**
121      * {@inheritDoc}
122      * 
123      * @see java.lang.Object#toString()
124      */
125     @Override
126     public String toString() {
127         return getClass().getSimpleName();
128     }
129 }