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.select;
22  
23  import org.apache.expreval.client.NullColumnValueException;
24  import org.apache.expreval.client.ResultMissingColumnException;
25  import org.apache.expreval.expr.MultipleExpressionContext;
26  import org.apache.expreval.expr.node.GenericValue;
27  import org.apache.expreval.expr.var.DelegateColumn;
28  import org.apache.hadoop.hbase.client.Result;
29  import org.apache.hadoop.hbase.hbql.client.HBqlException;
30  import org.apache.hadoop.hbase.hbql.client.HConnection;
31  import org.apache.hadoop.hbase.hbql.client.HRecord;
32  import org.apache.hadoop.hbase.hbql.impl.AggregateValue;
33  import org.apache.hadoop.hbase.hbql.impl.HConnectionImpl;
34  import org.apache.hadoop.hbase.hbql.impl.HRecordImpl;
35  import org.apache.hadoop.hbase.hbql.impl.Utils;
36  import org.apache.hadoop.hbase.hbql.io.IO;
37  import org.apache.hadoop.hbase.hbql.mapping.ColumnAttrib;
38  import org.apache.hadoop.hbase.hbql.mapping.MappingContext;
39  import org.apache.hadoop.hbase.hbql.mapping.TableMapping;
40  import org.apache.hadoop.hbase.hbql.statement.SelectStatement;
41  
42  import java.util.Collection;
43  import java.util.Map;
44  import java.util.NavigableMap;
45  
46  public class SelectExpressionContext extends MultipleExpressionContext implements SelectElement {
47  
48      private String asName;
49  
50      private ColumnAttrib columnAttrib = null;
51      private String familyName = null;
52      private String columnName = null;
53      private byte[] familyNameBytes = null;
54      private byte[] columnNameBytes = null;
55  
56      private SelectExpressionContext(final GenericValue genericValue, final String asName) {
57          super(null, genericValue);
58          this.asName = asName;
59      }
60  
61      public static SelectExpressionContext newExpression(final GenericValue expr, final String as) {
62          return new SelectExpressionContext(expr, as);
63      }
64  
65      public String getAsName() {
66          return this.asName;
67      }
68  
69      private ColumnAttrib getColumnAttrib() {
70          return this.columnAttrib;
71      }
72  
73      private String getFamilyName() {
74          return this.familyName;
75      }
76  
77      private String getColumnName() {
78          return this.columnName;
79      }
80  
81      private byte[] getFamilyNameBytes() {
82          return this.familyNameBytes;
83      }
84  
85      private byte[] getColumnNameBytes() {
86          return this.columnNameBytes;
87      }
88  
89      public boolean isAnAggregateElement() {
90          return this.getGenericValue().isAnAggregateValue();
91      }
92  
93      private GenericValue getGenericValue() {
94          return this.getGenericValue(0);
95      }
96  
97      public void initAggregateValue(final AggregateValue aggregateValue) throws HBqlException {
98          this.getGenericValue().initAggregateValue(aggregateValue);
99      }
100 
101     public void applyResultToAggregateValue(final AggregateValue aggregateValue,
102                                             final Result result) throws HBqlException,
103                                                                         ResultMissingColumnException,
104                                                                         NullColumnValueException {
105         this.getGenericValue().applyResultToAggregateValue(aggregateValue, result);
106     }
107 
108     public String getElementName() {
109         if (this.hasAsName())
110             return this.getAsName();
111         return this.getColumnAttrib().getFamilyQualifiedName();
112     }
113 
114     public boolean isAFamilySelect() {
115         return false;
116     }
117 
118     public boolean hasAsName() {
119         return Utils.isValidString(this.getAsName());
120     }
121 
122     public boolean isADelegateColumnReference() {
123         return this.getGenericValue() instanceof DelegateColumn;
124     }
125 
126     public boolean isAConstant() {
127         return this.getGenericValue().isAConstant();
128     }
129 
130     public boolean isDefaultKeyword() {
131         return this.getGenericValue().isDefaultKeyword();
132     }
133 
134     public boolean hasAColumnReference() {
135         return this.getGenericValue().hasAColumnReference();
136     }
137 
138     public boolean isAKeyValue() {
139         if (!this.isADelegateColumnReference())
140             return false;
141 
142         if (this.getColumnAttrib() != null)
143             return this.getColumnAttrib().isAKeyAttrib();
144 
145         return false;
146     }
147 
148     public Class<? extends GenericValue> getExpressionType() throws HBqlException {
149         return this.getGenericValue().validateTypes(null, false);
150     }
151 
152     public void validate(final MappingContext mappingContext, final HConnection connection) throws HBqlException {
153 
154         this.setMappingContext(mappingContext);
155 
156         // TODO this needs to be done for expressions with col refs
157 
158         // Look up stuff for simple column references
159         if (this.isADelegateColumnReference()) {
160             final String name = ((DelegateColumn)this.getGenericValue()).getVariableName();
161             this.columnAttrib = this.getResultAccessor().getColumnAttribByName(name);
162 
163             if (this.getColumnAttrib() != null) {
164                 this.familyName = this.getColumnAttrib().getFamilyName();
165                 this.columnName = this.getColumnAttrib().getColumnName();
166             }
167             else {
168                 if (!name.contains(":"))
169                     throw new HBqlException("Unknown select value: " + name);
170                 final String[] strs = name.split(":");
171                 this.familyName = strs[0];
172                 this.columnName = strs[1];
173                 final Collection<String> families = this.getTableMapping().getMappingFamilyNames();
174                 if (!families.contains(this.getFamilyName()))
175                     throw new HBqlException("Unknown family name: " + this.getFamilyName());
176             }
177 
178             this.familyNameBytes = IO.getSerialization().getStringAsBytes(this.getFamilyName());
179             this.columnNameBytes = IO.getSerialization().getStringAsBytes(this.getColumnName());
180         }
181     }
182 
183     public void assignAsNamesForExpressions(final SelectStatement selectStatement) {
184 
185         if (!this.isADelegateColumnReference() && !this.hasAsName()) {
186             while (true) {
187                 // Assign a name that is not in use
188                 final String newAsName = selectStatement.getNextExpressionName();
189                 if (!selectStatement.hasAsName(newAsName)) {
190                     this.asName = newAsName;
191                     break;
192                 }
193             }
194         }
195     }
196 
197     private String getSelectName() {
198         return this.hasAsName() ? this.getAsName() : this.getFamilyName() + ":" + this.getColumnName();
199     }
200 
201     private byte[] getResultCurrentValue(final Result result) {
202 
203         final NavigableMap<byte[], byte[]> columnMap = result.getFamilyMap(this.getFamilyNameBytes());
204 
205         // ColumnMap should not be null at this point, but check just in case
206         if (columnMap == null)
207             return null;
208         else
209             return columnMap.get(this.getColumnNameBytes());
210     }
211 
212     private void assignCalculation(final HConnectionImpl conn,
213                                    final Object obj,
214                                    final Result result) throws HBqlException {
215         // If it is a calculation, then assign according to the AS name
216         final String name = this.getAsName();
217         final ColumnAttrib attrib = this.getResultAccessor().getColumnAttribByName(name);
218 
219         final Object elementValue = this.getValue(conn, result);
220 
221         if (attrib == null) {
222             // Find value in results and assign the byte[] value to Record, but bail on Annotated object because
223             // it cannot deal with unknown/unmapped values
224             if (!(obj instanceof HRecord))
225                 return;
226 
227             ((HRecordImpl)obj).setCurrentValue(name, 0, elementValue, false);
228         }
229         else {
230             attrib.setCurrentValue(obj, 0, elementValue);
231         }
232     }
233 
234     public void assignSelectValue(final HConnectionImpl conn,
235                                   final Object obj,
236                                   final int maxVerions,
237                                   final Result result) throws HBqlException {
238 
239         if (obj instanceof HRecordImpl) {
240             final HRecordImpl record = (HRecordImpl)obj;
241             record.addNameToPositionList(this.getSelectName());
242         }
243 
244         if (this.isAKeyValue())
245             return;
246 
247         // If it is a calculation, take care of it and then bail since calculations have no history
248         if (!this.isADelegateColumnReference()) {
249             this.assignCalculation(conn, obj, result);
250             return;
251         }
252 
253         final TableMapping tableMapping = this.getTableMapping();
254 
255         // Column reference is not known to mapping, so just assign byte[] value
256         if (this.getColumnAttrib() == null) {
257             final ColumnAttrib unMappedAttrib = tableMapping.getUnMappedAttrib(this.getFamilyName());
258             if (unMappedAttrib != null) {
259                 final byte[] b = result.getValue(this.getFamilyNameBytes(), this.getColumnNameBytes());
260                 unMappedAttrib.setUnMappedCurrentValue(obj, this.getSelectName(), b);
261             }
262         }
263         else {
264             if (this.getColumnAttrib().isACurrentValue()) {
265                 final byte[] b = result.getValue(this.getFamilyNameBytes(), this.getColumnNameBytes());
266                 this.getColumnAttrib().setCurrentValue(obj, 0, b);
267             }
268         }
269 
270         // Now assign versions if they were requested. Do not process if it doesn't support version values
271         if (maxVerions > 1) {
272 
273             // Bail if a known column is not a version attrib
274             if (this.getColumnAttrib() != null && !this.getColumnAttrib().isAVersionValue())
275                 return;
276 
277             final NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> familyMap = result.getMap();
278             final NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap = familyMap.get(this.getFamilyNameBytes());
279 
280             if (columnMap == null)
281                 return;
282 
283             final NavigableMap<Long, byte[]> timeStampMap = columnMap.get(this.getColumnNameBytes());
284 
285             if (this.getColumnAttrib() == null) {
286                 final ColumnAttrib unMappedAttrib = tableMapping.getUnMappedAttrib(this.getFamilyName());
287                 if (unMappedAttrib != null)
288                     unMappedAttrib.setUnMappedVersionMap(obj, this.getSelectName(), timeStampMap);
289             }
290             else {
291                 final Map<Long, Object> mapVal = this.getColumnAttrib().getVersionMap(obj);
292                 for (final Long timestamp : timeStampMap.keySet()) {
293                     final byte[] b = timeStampMap.get(timestamp);
294                     final Object val = this.getColumnAttrib().getValueFromBytes(obj, b);
295                     mapVal.put(timestamp, val);
296                 }
297             }
298         }
299     }
300 
301     public AggregateValue newAggregateValue() throws HBqlException {
302         return new AggregateValue(this.getSelectName(), this);
303     }
304 
305     public Object getValue(final HConnectionImpl conn, final Result result) throws HBqlException {
306         try {
307             return this.evaluate(conn, 0, this.allowColumns(), false, result);
308         }
309         catch (ResultMissingColumnException e) {
310             return null;
311         }
312         catch (NullColumnValueException e) {
313             return null;
314         }
315     }
316 
317     public String asString() {
318         return this.getGenericValue().asString();
319     }
320 
321     public boolean useResultData() {
322         return true;
323     }
324 
325     public boolean allowColumns() {
326         return true;
327     }
328 }