View Javadoc

1   /*
2    * Copyright (c) 2011.  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.mapping;
22  
23  import org.apache.expreval.expr.ExpressionTree;
24  import org.apache.hadoop.hbase.hbql.client.HBqlException;
25  import org.apache.hadoop.hbase.hbql.client.HMapping;
26  import org.apache.hadoop.hbase.hbql.client.HRecord;
27  import org.apache.hadoop.hbase.hbql.filter.RecordFilter;
28  import org.apache.hadoop.hbase.hbql.impl.HConnectionImpl;
29  import org.apache.hadoop.hbase.hbql.impl.HRecordImpl;
30  import org.apache.hadoop.hbase.hbql.io.IO;
31  import org.apache.hadoop.hbase.hbql.parser.ParserUtil;
32  import org.apache.hadoop.hbase.hbql.statement.args.KeyInfo;
33  import org.apache.hadoop.hbase.hbql.util.Lists;
34  import org.apache.hadoop.hbase.hbql.util.Maps;
35  
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  
40  public class TableMapping extends Mapping implements HMapping {
41  
42      private static final long serialVersionUID = 3L;
43  
44      private transient HConnectionImpl connection;
45      private           boolean         tempMapping;
46      private           boolean         systemMapping;
47      private KeyInfo keyInfo = null;
48  
49      private final Map<String, HRecordAttrib>       columnAttribByFamilyQualifiedNameMap = Maps.newHashMap();
50      private final Map<String, HRecordAttrib>       versionAttribMap                     = Maps.newHashMap();
51      private final Map<String, List<HRecordAttrib>> columnAttribListByFamilyNameMap      = Maps.newHashMap();
52  
53      private final Map<String, HRecordAttrib> unMappedAttribsMap = Maps.newHashMap();
54  
55      // For serialization
56      public TableMapping() {
57      }
58  
59      public TableMapping(final HConnectionImpl conn,
60                          final boolean tempMapping,
61                          final boolean systemMapping,
62                          final String mappingName,
63                          final String tableName,
64                          final KeyInfo keyInfo,
65                          final List<FamilyMapping> familyMappingList) throws HBqlException {
66  
67          super(mappingName, tableName);
68  
69          this.connection = conn;
70          this.tempMapping = tempMapping;
71          this.systemMapping = systemMapping;
72          this.keyInfo = keyInfo;
73  
74          // Add KEY column
75          if (keyInfo != null)
76              processColumnDefintion(ColumnDefinition.newKeyColumn(keyInfo));
77  
78          if (familyMappingList != null) {
79              for (final FamilyMapping familyMapping : familyMappingList) {
80                  // Add columns
81                  if (familyMapping.getColumnDefinitionList() != null)
82                      for (final ColumnDefinition columnDefinition : familyMapping.getColumnDefinitionList())
83                          processColumnDefintion(columnDefinition);
84  
85                  // Add Family Defaults
86                  if (familyMapping.includeUnmapped()) {
87                      final String familyName = familyMapping.getFamilyName();
88                      final ColumnDefinition columnDefinition = ColumnDefinition.newUnMappedColumn(familyName);
89                      final HRecordAttrib attrib = new HRecordAttrib(columnDefinition);
90                      this.addUnMappedAttrib(attrib);
91                  }
92              }
93          }
94      }
95  
96      private HConnectionImpl getConnection() {
97          return this.connection;
98      }
99  
100     public KeyInfo getKeyInfo() {
101         return this.keyInfo;
102     }
103 
104     public void validateKeyInfo(final String indexName) throws HBqlException {
105         if (!this.getKeyInfo().isWidthSpecified())
106             throw new HBqlException("Cannot use index " + indexName + " without a width value for KEY "
107                                     + this.getKeyInfo().getKeyName() + " in mapping " + this.getMappingName());
108     }
109 
110     public HRecord newHRecord() throws HBqlException {
111         final MappingContext mappingContext = new MappingContext(this);
112         mappingContext.setResultAccessor(new HRecordResultAccessor(mappingContext));
113         return new HRecordImpl(mappingContext);
114     }
115 
116     public HRecord newHRecord(final Map<String, Object> initMap) throws HBqlException {
117         final HRecord newrec = this.newHRecord();
118 
119         for (final String name : initMap.keySet())
120             newrec.setCurrentValue(name, initMap.get(name));
121 
122         return newrec;
123     }
124 
125     private void processColumnDefintion(final ColumnDefinition columnDefinition) throws HBqlException {
126 
127         final HRecordAttrib attrib = new HRecordAttrib(columnDefinition);
128 
129         this.addAttribToVariableNameMap(attrib, attrib.getNamesForColumn());
130         this.addAttribToFamilyQualifiedNameMap(attrib);
131         this.addVersionAttrib(attrib);
132         this.addAttribToFamilyNameColumnListMap(attrib);
133 
134         if (attrib.isAKeyAttrib()) {
135             if (this.getKeyAttrib() != null)
136                 throw new HBqlException("Mapping " + this + " has multiple instance variables marked as keys");
137             this.setKeyAttrib(attrib);
138         }
139     }
140 
141     public byte[] getTableNameAsBytes() throws HBqlException {
142         return IO.getSerialization().getStringAsBytes(this.getTableName());
143     }
144 
145     // *** columnAttribByFamilyQualifiedNameMap calls
146     protected Map<String, HRecordAttrib> getAttribByFamilyQualifiedNameMap() {
147         return this.columnAttribByFamilyQualifiedNameMap;
148     }
149 
150     public ColumnAttrib getAttribFromFamilyQualifiedName(final String familyName, final String columnName) {
151         return this.getAttribFromFamilyQualifiedName(familyName + ":" + columnName);
152     }
153 
154     public ColumnAttrib getAttribFromFamilyQualifiedName(final String familyQualifiedName) {
155         return this.getAttribByFamilyQualifiedNameMap().get(familyQualifiedName);
156     }
157 
158     protected void addAttribToFamilyQualifiedNameMap(final HRecordAttrib attrib) throws HBqlException {
159 
160         final String name = attrib.getFamilyQualifiedName();
161         if (this.getAttribByFamilyQualifiedNameMap().containsKey(name))
162             throw new HBqlException(name + " already declared");
163         this.getAttribByFamilyQualifiedNameMap().put(name, attrib);
164     }
165 
166     // *** unMappedMap calls
167     private Map<String, HRecordAttrib> getUnMappedAttribsMap() {
168         return this.unMappedAttribsMap;
169     }
170 
171     public ColumnAttrib getUnMappedAttrib(final String familyName) {
172         return this.getUnMappedAttribsMap().get(familyName);
173     }
174 
175     private boolean includeUnMappedForFamiily(final String familyName) {
176         return this.getUnMappedAttribsMap().containsKey(familyName);
177     }
178 
179     private void addUnMappedAttrib(final HRecordAttrib attrib) throws HBqlException {
180 
181         final String familyName = attrib.getFamilyName();
182         if (this.getUnMappedAttribsMap().containsKey(familyName))
183             throw new HBqlException(familyName + " already declared");
184 
185         this.getUnMappedAttribsMap().put(familyName, attrib);
186 
187         final String aliasName = attrib.getAliasName();
188         if (aliasName == null || aliasName.length() == 0 || aliasName.equals(familyName))
189             return;
190 
191         if (this.getUnMappedAttribsMap().containsKey(aliasName))
192             throw new HBqlException(aliasName + " already declared");
193 
194         this.getUnMappedAttribsMap().put(aliasName, attrib);
195     }
196 
197     // *** versionAttribByFamilyQualifiedNameMap calls
198     private Map<String, HRecordAttrib> getVersionAttribMap() {
199         return this.versionAttribMap;
200     }
201 
202     public ColumnAttrib getVersionAttrib(final String name) {
203         return this.getVersionAttribMap().get(name);
204     }
205 
206     protected void addVersionAttrib(final HRecordAttrib attrib) throws HBqlException {
207 
208         if (!attrib.isAVersionValue())
209             return;
210 
211         final String familyQualifiedName = attrib.getFamilyQualifiedName();
212         if (this.getVersionAttribMap().containsKey(familyQualifiedName))
213             throw new HBqlException(familyQualifiedName + " already declared");
214 
215         this.getVersionAttribMap().put(familyQualifiedName, attrib);
216     }
217 
218     // *** columnAttribListByFamilyNameMap
219     private Map<String, List<HRecordAttrib>> getColumnAttribListByFamilyNameMap() {
220         return this.columnAttribListByFamilyNameMap;
221     }
222 
223     public Set<String> getFamilySet() {
224         return this.getColumnAttribListByFamilyNameMap().keySet();
225     }
226 
227     public boolean containsFamily(final String familyName) {
228         return this.getFamilySet().contains(familyName);
229     }
230 
231     public List<HRecordAttrib> getColumnAttribListByFamilyName(final String familyName) {
232         return this.getColumnAttribListByFamilyNameMap().get(familyName);
233     }
234 
235     public boolean containsFamilyNameInFamilyNameMap(final String familyName) {
236         return this.getColumnAttribListByFamilyNameMap().containsKey(familyName);
237     }
238 
239     public void addAttribToFamilyNameColumnListMap(final String familyName,
240                                                    final List<HRecordAttrib> attribList) throws HBqlException {
241         if (this.containsFamilyNameInFamilyNameMap(familyName))
242             throw new HBqlException(familyName + " already declared");
243         this.getColumnAttribListByFamilyNameMap().put(familyName, attribList);
244     }
245 
246     public void addAttribToFamilyNameColumnListMap(HRecordAttrib attrib) throws HBqlException {
247 
248         if (attrib.isAKeyAttrib())
249             return;
250 
251         final String familyName = attrib.getFamilyName();
252 
253         if (familyName == null || familyName.length() == 0)
254             return;
255 
256         final List<HRecordAttrib> attribList;
257         if (!this.containsFamilyNameInFamilyNameMap(familyName)) {
258             attribList = Lists.newArrayList();
259             this.addAttribToFamilyNameColumnListMap(familyName, attribList);
260         }
261         else {
262             attribList = this.getColumnAttribListByFamilyName(familyName);
263         }
264         attribList.add(attrib);
265     }
266 
267     public synchronized Set<String> getMappingFamilyNames() throws HBqlException {
268 
269         // Connction will be null from tests
270         return (this.getConnection() == null)
271                ? this.getFamilySet()
272                : this.getConnection().getFamilyNames(this.getTableName());
273     }
274 
275     public RecordFilter newRecordFilter(final String query) throws HBqlException {
276         final MappingContext mappingContext = new MappingContext(this);
277         mappingContext.setResultAccessor(new HRecordResultAccessor(mappingContext));
278         final ExpressionTree expressionTree = ParserUtil.parseWhereExpression(query, mappingContext);
279         return RecordFilter.newRecordFilter(expressionTree);
280     }
281 
282     public boolean isTempMapping() {
283         return this.tempMapping;
284     }
285 
286     public boolean isSystemMapping() {
287         return this.systemMapping;
288     }
289 
290     public void dropMapping() throws HBqlException {
291         this.getConnection().dropMapping(this.getMappingName());
292     }
293 
294     public void validate(final String mappingName) throws HBqlException {
295         for (final ColumnAttrib attrib : this.getColumnAttribSet()) {
296             if (attrib.getFieldType() == null)
297                 throw new HBqlException(mappingName + " attribute "
298                                         + attrib.getFamilyQualifiedName() + " has unknown type.");
299         }
300     }
301 
302     public String asString() throws HBqlException {
303 
304         final StringBuilder sbuf = new StringBuilder();
305 
306         sbuf.append("CREATE ")
307             .append(this.isTempMapping() ? "TEMP " : "")
308             .append("MAPPING ")
309             .append(this.getMappingName());
310 
311         if (!(this.getMappingName().equals(this.getTableName())))
312             sbuf.append(" FOR TABLE ").append(this.getTableName());
313 
314         sbuf.append(" (\n  ")
315             .append(this.getKeyAttrib().getColumnName())
316             .append(" KEY");
317 
318         for (final String familyName : this.getColumnAttribListByFamilyNameMap().keySet()) {
319 
320             sbuf.append(",\n  ").append(familyName);
321 
322             if (this.includeUnMappedForFamiily(familyName))
323                 sbuf.append(" INCLUDE UNMAPPED");
324 
325             sbuf.append(" (");
326 
327             boolean first = true;
328             for (final HRecordAttrib column : this.getColumnAttribListByFamilyNameMap().get(familyName)) {
329                 if (!first)
330                     sbuf.append(",");
331                 else
332                     first = false;
333 
334                 sbuf.append("\n    ").append(column.asString());
335             }
336 
337             sbuf.append("\n  )");
338         }
339 
340         if (this.getColumnAttribListByFamilyNameMap().size() > 0)
341             sbuf.append("\n");
342 
343         sbuf.append(")");
344 
345         return sbuf.toString();
346     }
347 }