View Javadoc

1   /*
2    * Copyright (c) 2010.  The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.hbql.statement;
22  
23  import org.apache.hadoop.hbase.hbql.client.HBqlException;
24  import org.apache.hadoop.hbase.hbql.impl.HConnectionImpl;
25  import org.apache.hadoop.hbase.hbql.impl.InvalidColumnException;
26  import org.apache.hadoop.hbase.hbql.mapping.ColumnAttrib;
27  import org.apache.hadoop.hbase.hbql.statement.args.WithArgs;
28  import org.apache.hadoop.hbase.hbql.statement.select.SelectElement;
29  import org.apache.hadoop.hbase.hbql.util.Lists;
30  import org.apache.hadoop.hbase.hbql.util.Sets;
31  
32  import java.util.List;
33  import java.util.Set;
34  import java.util.concurrent.atomic.AtomicInteger;
35  
36  public class SelectStatement extends StatementWithParameters implements HBqlStatement {
37  
38      private final List<ColumnAttrib> selectColumnAttribList = Lists.newArrayList();
39      private final AtomicInteger expressionCounter = new AtomicInteger(-1);
40      private final List<SelectElement> selectElementList;
41      private final WithArgs withArgs;
42  
43      private boolean validated = false;
44      private boolean aggregateQuery = false;
45  
46      public SelectStatement(final List<SelectElement> selectElementList,
47                             final String mappingName,
48                             final WithArgs withArgs) {
49          super(null, mappingName);
50          this.selectElementList = selectElementList;
51          this.withArgs = withArgs != null ? withArgs : new WithArgs();
52      }
53  
54      public synchronized String getNextExpressionName() {
55          return "expr-" + this.expressionCounter.incrementAndGet();
56      }
57  
58      private boolean isValidated() {
59          return this.validated;
60      }
61  
62      public List<SelectElement> getSelectElementList() {
63          return this.selectElementList;
64      }
65  
66      public List<ColumnAttrib> getSelectAttribList() {
67          return this.selectColumnAttribList;
68      }
69  
70      public WithArgs getWithArgs() {
71          return this.withArgs;
72      }
73  
74      public synchronized void validate(final HConnectionImpl conn) throws HBqlException {
75  
76          if (this.isValidated())
77              return;
78  
79          this.validated = true;
80  
81          this.getMappingContext().validateMappingName(conn);
82  
83          this.getSelectAttribList().clear();
84  
85          for (final SelectElement element : this.getSelectElementList()) {
86              element.validate(this.getMappingContext(), conn);
87              element.assignAsNamesForExpressions(this);
88              this.getSelectAttribList().addAll(element.getAttribsUsedInExpr());
89          }
90  
91          this.getWithArgs().setMappingContext(this.getMappingContext());
92          this.getWithArgs().validate(conn, this.getMappingContext().getTableMapping());
93  
94          // Make sure there are no duplicate aliases in list
95          this.checkForDuplicateAsNames();
96  
97          // Build sorted set of parameters
98          this.collectParameters();
99      }
100 
101     public void validateTypes() throws HBqlException {
102         this.getWithArgs().validateArgTypes();
103     }
104 
105     public void determineIfAggregateQuery() throws HBqlException {
106 
107         // This is required before the checkIfAggregateQuery() call.
108         for (final SelectElement element : this.getSelectElementList()) {
109             try {
110                 element.validateTypes(true, false);
111             }
112             catch (InvalidColumnException e) {
113                 // No op
114                 e.printStackTrace();
115             }
116         }
117 
118         this.aggregateQuery = this.checkIfAggregateQuery();
119     }
120 
121     private boolean checkIfAggregateQuery() throws HBqlException {
122         final SelectElement firstElement = this.getSelectElementList().get(0);
123         final boolean firstIsAggregate = firstElement.isAnAggregateElement();
124         for (final SelectElement selectElement : this.getSelectElementList()) {
125             if (selectElement.isAnAggregateElement() != firstIsAggregate)
126                 throw new HBqlException("Cannot mix aggregate and non-aggregate select elements");
127         }
128         return firstIsAggregate;
129     }
130 
131     public boolean isAnAggregateQuery() {
132         return this.aggregateQuery;
133     }
134 
135     private void checkForDuplicateAsNames() throws HBqlException {
136         final Set<String> asNameSet = Sets.newHashSet();
137         for (final SelectElement selectElement : this.getSelectElementList()) {
138             if (selectElement.hasAsName()) {
139                 final String asName = selectElement.getAsName();
140                 if (asNameSet.contains(asName))
141                     throw new HBqlException("Duplicate select name " + asName + " in select list");
142                 asNameSet.add(asName);
143             }
144         }
145     }
146 
147     public boolean hasAsName(final String name) {
148         for (final SelectElement selectElement : this.getSelectElementList())
149             if (selectElement.hasAsName() && selectElement.getAsName().equals(name))
150                 return true;
151         return false;
152     }
153 
154     private void collectParameters() {
155         for (final SelectElement selectElement : this.getSelectElementList())
156             this.getNamedParameters().addParameters(selectElement.getParameterList());
157 
158         this.getNamedParameters().addParameters(this.getWithArgs().getParameterList());
159     }
160 
161     public void resetParameters() {
162         for (final SelectElement selectElement : this.getSelectElementList())
163             selectElement.reset();
164 
165         this.getWithArgs().reset();
166     }
167 
168 
169     public int setStatementParameter(final String name, final Object val) throws HBqlException {
170         int cnt = 0;
171         for (final SelectElement selectElement : this.getSelectElementList())
172             cnt += selectElement.setParameter(name, val);
173 
174         cnt += this.getWithArgs().setParameter(name, val);
175         return cnt;
176     }
177 
178     public String asString() {
179         final StringBuilder sbuf = new StringBuilder("SELECT  ");
180         boolean firstTime = true;
181         for (final SelectElement element : this.getSelectElementList()) {
182             if (!firstTime)
183                 sbuf.append(", ");
184             firstTime = false;
185 
186             sbuf.append(element.asString());
187         }
188 
189         sbuf.append(" FROM ");
190         sbuf.append(this.getMappingContext().getMappingName());
191         sbuf.append(" ");
192         sbuf.append(this.getWithArgs().asString());
193         return sbuf.toString();
194     }
195 
196     public static String usage() {
197         return "SELECT select_element_list FROM [MAPPING] mapping_name with_clause";
198     }
199 }