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.args;
22  
23  import org.apache.expreval.expr.ExpressionTree;
24  import org.apache.expreval.expr.var.NamedParameter;
25  import org.apache.hadoop.hbase.client.Get;
26  import org.apache.hadoop.hbase.client.Scan;
27  import org.apache.hadoop.hbase.client.tableindexed.IndexSpecification;
28  import org.apache.hadoop.hbase.filter.Filter;
29  import org.apache.hadoop.hbase.hbql.client.HBqlException;
30  import org.apache.hadoop.hbase.hbql.client.Util;
31  import org.apache.hadoop.hbase.hbql.filter.InstrumentedFilter;
32  import org.apache.hadoop.hbase.hbql.filter.PageFilter;
33  import org.apache.hadoop.hbase.hbql.filter.RecordFilter;
34  import org.apache.hadoop.hbase.hbql.impl.HConnectionImpl;
35  import org.apache.hadoop.hbase.hbql.impl.Utils;
36  import org.apache.hadoop.hbase.hbql.mapping.ColumnAttrib;
37  import org.apache.hadoop.hbase.hbql.mapping.Mapping;
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.select.RowRequest;
41  import org.apache.hadoop.hbase.hbql.util.Lists;
42  import org.apache.hadoop.hbase.hbql.util.Sets;
43  
44  import java.util.List;
45  import java.util.Set;
46  
47  public class WithArgs {
48  
49      private String indexName = null;
50      private KeyRangeArgs keyRangeArgs = null;
51      private TimestampArgs timestampArgs = null;
52      private VersionArgs versionArgs = null;
53      private ScannerCacheArgs scannerCacheArgs = null;
54      private LimitArgs limitArgs = null;
55      private boolean verbose = false;
56      private ExpressionTree clientExpressionTree = null;
57      private ExpressionTree serverExpressionTree = null;
58      private MappingContext mappingContext = null;
59  
60      // Keep track of args set multiple times
61      private final Set<String> multipleSetValues = Sets.newHashSet();
62  
63      public void setMappingContext(final MappingContext mappingContext) throws HBqlException {
64  
65          this.mappingContext = mappingContext;
66  
67          this.validateNoDuplicateWithArgs();
68  
69          if (this.getKeyRangeArgs() == null)
70              this.setKeyRangeArgs(new KeyRangeArgs());    // Default to ALL records
71  
72          this.getKeyRangeArgs().setMappingContext(this.getMappingContext());
73  
74          if (this.getTimestampArgs() != null)
75              this.getTimestampArgs().setMappingContext(this.getMappingContext());
76  
77          if (this.getVersionArgs() != null)
78              this.getVersionArgs().setMappingContext(this.getMappingContext());
79  
80          if (this.getLimitArgs() != null)
81              this.getLimitArgs().setMappingContext(this.getMappingContext());
82  
83          if (this.getServerExpressionTree() != null) {
84              this.getServerExpressionTree().setMappingContext(this.getMappingContext());
85              this.getServerExpressionTree().setUseResultData(false);
86          }
87  
88          if (this.getClientExpressionTree() != null) {
89              this.getClientExpressionTree().setMappingContext(this.getMappingContext());
90              this.getClientExpressionTree().setUseResultData(true);
91          }
92      }
93  
94      public void validate(final HConnectionImpl conn, final TableMapping mapping) throws HBqlException {
95          if (conn != null) {
96              if (this.hasAnIndex())
97                  conn.validateIndexExistsForTable(this.getIndexName(), mapping.getTableName());
98          }
99      }
100 
101     public void validateArgTypes() throws HBqlException {
102 
103         if (this.getKeyRangeArgs() != null)
104             this.getKeyRangeArgs().validate();
105 
106         if (this.getTimestampArgs() != null)
107             this.getTimestampArgs().validate();
108 
109         if (this.getVersionArgs() != null)
110             this.getVersionArgs().validate();
111 
112         if (this.getLimitArgs() != null)
113             this.getLimitArgs().validate();
114     }
115 
116     private void validateNoDuplicateWithArgs() throws HBqlException {
117         if (this.multipleSetValues.size() > 0) {
118             final StringBuilder sbuf = new StringBuilder();
119             boolean firstTime = true;
120             for (final String str : this.multipleSetValues) {
121                 if (!firstTime)
122                     sbuf.append(", ");
123                 sbuf.append(str);
124                 firstTime = false;
125             }
126             throw new HBqlException("Select args specificed multiple times: " + sbuf);
127         }
128     }
129 
130     private MappingContext getMappingContext() {
131         return this.mappingContext;
132     }
133 
134     private void addError(final String str) {
135         this.multipleSetValues.add(str);
136     }
137 
138     public KeyRangeArgs getKeyRangeArgs() {
139         return this.keyRangeArgs;
140     }
141 
142     public void setIndexName(final String indexName) {
143         this.indexName = indexName;
144     }
145 
146     public String getIndexName() {
147         return this.indexName;
148     }
149 
150     public boolean hasAnIndex() {
151         return Utils.isValidString(this.getIndexName());
152     }
153 
154     public void setKeyRangeArgs(final KeyRangeArgs keyRangeArgs) {
155         if (this.getKeyRangeArgs() != null)
156             this.addError("Keys");
157         this.keyRangeArgs = keyRangeArgs;
158     }
159 
160     private TimestampArgs getTimestampArgs() {
161         return this.timestampArgs;
162     }
163 
164     public void setTimestampArgs(final TimestampArgs timestampArgs) {
165         if (this.getTimestampArgs() != null)
166             this.addError("Time Range");
167         this.timestampArgs = timestampArgs;
168     }
169 
170     private VersionArgs getVersionArgs() {
171         return this.versionArgs;
172     }
173 
174     public void setVersionArgs(final VersionArgs versionArgs) {
175         if (this.getVersionArgs() != null)
176             this.addError("Version");
177         this.versionArgs = versionArgs;
178     }
179 
180     private ScannerCacheArgs getScannerCacheArgs() {
181         return this.scannerCacheArgs;
182     }
183 
184     public void setScannerCacheArgs(final ScannerCacheArgs scannerCacheArgs) {
185         if (this.getVersionArgs() != null)
186             this.addError("Scanner_Cache_Size");
187         this.scannerCacheArgs = scannerCacheArgs;
188     }
189 
190     public LimitArgs getLimitArgs() {
191         return this.limitArgs;
192     }
193 
194     public void setLimitArgs(final LimitArgs limitArgs) {
195         if (this.getLimitArgs() != null)
196             this.addError("Limit");
197         this.limitArgs = limitArgs;
198     }
199 
200     public boolean getVerbose() {
201         return this.verbose;
202     }
203 
204     public void setVerbose(final boolean verbose) {
205         this.verbose = verbose;
206     }
207 
208     public ExpressionTree getClientExpressionTree() {
209         return this.clientExpressionTree;
210     }
211 
212     public void setClientExpressionTree(final ExpressionTree clientExpressionTree) {
213         if (this.getClientExpressionTree() != null)
214             this.addError("Client Where");
215         this.clientExpressionTree = clientExpressionTree;
216     }
217 
218     public ExpressionTree getServerExpressionTree() {
219         return this.serverExpressionTree;
220     }
221 
222     public void setServerExpressionTree(final ExpressionTree serverExpressionTree) {
223         if (this.getServerExpressionTree() != null)
224             this.addError("Server Where");
225         this.serverExpressionTree = serverExpressionTree;
226     }
227 
228     public long getLimit() throws HBqlException {
229         return (this.getLimitArgs() != null) ? this.getLimitArgs().getValue() : 0;
230     }
231 
232     public String asString() {
233 
234         final StringBuilder sbuf = new StringBuilder("WITH ");
235 
236         if (this.getKeyRangeArgs() != null)
237             sbuf.append(this.getKeyRangeArgs().asString() + "\n");
238 
239         if (this.getTimestampArgs() != null)
240             sbuf.append(this.getTimestampArgs().asString() + "\n");
241 
242         if (this.getVersionArgs() != null)
243             sbuf.append(this.getVersionArgs().asString() + "\n");
244 
245         if (this.getScannerCacheArgs() != null)
246             sbuf.append(this.getScannerCacheArgs().asString() + "\n");
247 
248         if (this.getLimitArgs() != null)
249             sbuf.append(this.getLimitArgs().asString() + "\n");
250 
251         if (this.getServerExpressionTree() != null)
252             sbuf.append("SERVER FILTER " + this.getServerExpressionTree().asString() + "\n");
253 
254         if (this.getClientExpressionTree() != null)
255             sbuf.append("CLIENT FILTER " + this.getClientExpressionTree().asString() + "\n");
256 
257         return sbuf.toString();
258     }
259 
260     public List<NamedParameter> getParameterList() {
261 
262         final List<NamedParameter> parameterList = Lists.newArrayList();
263 
264         if (this.getKeyRangeArgs() != null)
265             parameterList.addAll(this.getKeyRangeArgs().getParameterList());
266 
267         if (this.getTimestampArgs() != null)
268             parameterList.addAll(this.getTimestampArgs().getParameterList());
269 
270         if (this.getVersionArgs() != null)
271             parameterList.addAll(this.getVersionArgs().getParameterList());
272 
273         if (this.getScannerCacheArgs() != null)
274             parameterList.addAll(this.getScannerCacheArgs().getParameterList());
275 
276         if (this.getLimitArgs() != null)
277             parameterList.addAll(this.getLimitArgs().getParameterList());
278 
279         if (this.getServerExpressionTree() != null)
280             parameterList.addAll(this.getServerExpressionTree().getParameterList());
281 
282         if (this.getClientExpressionTree() != null)
283             parameterList.addAll(this.getClientExpressionTree().getParameterList());
284 
285         return parameterList;
286     }
287 
288     public void reset() {
289 
290         if (this.getKeyRangeArgs() != null)
291             this.getKeyRangeArgs().reset();
292 
293         if (this.getTimestampArgs() != null)
294             this.getTimestampArgs().reset();
295 
296         if (this.getVersionArgs() != null)
297             this.getVersionArgs().reset();
298 
299         if (this.getScannerCacheArgs() != null)
300             this.getScannerCacheArgs().reset();
301 
302         if (this.getLimitArgs() != null)
303             this.getLimitArgs().reset();
304 
305         if (this.getServerExpressionTree() != null)
306             this.getServerExpressionTree().reset();
307 
308         if (this.getClientExpressionTree() != null)
309             this.getClientExpressionTree().reset();
310     }
311 
312     public int setParameter(final String name, final Object val) throws HBqlException {
313 
314         int cnt = 0;
315 
316         if (this.getKeyRangeArgs() != null)
317             cnt += this.getKeyRangeArgs().setParameter(name, val);
318 
319         if (this.getTimestampArgs() != null)
320             cnt += this.getTimestampArgs().setParameter(name, val);
321 
322         if (this.getVersionArgs() != null)
323             cnt += this.getVersionArgs().setParameter(name, val);
324 
325         if (this.getScannerCacheArgs() != null)
326             cnt += this.getScannerCacheArgs().setParameter(name, val);
327 
328         if (this.getLimitArgs() != null)
329             cnt += this.getLimitArgs().setParameter(name, val);
330 
331         if (this.getServerExpressionTree() != null)
332             cnt += this.getServerExpressionTree().setParameter(name, val);
333 
334         if (this.getClientExpressionTree() != null)
335             cnt += this.getClientExpressionTree().setParameter(name, val);
336 
337         return cnt;
338     }
339 
340     public Filter getFilterForTableIndex() throws HBqlException {
341         if (this.getServerExpressionTree() != null)
342             return this.getServerExpressionTree().getFilter();
343         else
344             return null;
345     }
346 
347     public byte[][] getColumnsUsedInIndexWhereExpr() {
348         final byte[][] indexColumns;
349         final Set<ColumnAttrib> columnAttribs = this.getColumnsUsedInServerWhereExpr();
350         if (columnAttribs.size() == 0) {
351             indexColumns = null;
352         }
353         else {
354             final List<String> columnList = Lists.newArrayList();
355             for (final ColumnAttrib columnAttrib : columnAttribs) {
356                 // Ignore keys
357                 if (!columnAttrib.isAKeyAttrib())
358                     columnList.add(columnAttrib.isASelectFamilyAttrib() ? columnAttrib.getFamilyName()
359                                                                         : columnAttrib.getFamilyQualifiedName());
360             }
361 
362             indexColumns = Util.getStringsAsBytes(columnList);
363         }
364         return indexColumns;
365     }
366 
367     private Set<ColumnAttrib> getColumnsUsedInServerWhereExpr() {
368         final Set<ColumnAttrib> serverAttribs = Sets.newHashSet();
369         if (this.getServerExpressionTree() != null)
370             serverAttribs.addAll(this.getServerExpressionTree().getAttribsUsedInExpr());
371         return serverAttribs;
372     }
373 
374     private Set<ColumnAttrib> getColumnsUsedInClientWhereExpr() {
375         final Set<ColumnAttrib> clientAttribs = Sets.newHashSet();
376         if (this.getClientExpressionTree() != null)
377             clientAttribs.addAll(this.getClientExpressionTree().getAttribsUsedInExpr());
378         return clientAttribs;
379     }
380 
381     public Set<ColumnAttrib> getColumnsUsedInAllWhereExprs() {
382         final Set<ColumnAttrib> allAttribs = Sets.newHashSet();
383         allAttribs.addAll(this.getColumnsUsedInServerWhereExpr());
384         allAttribs.addAll(this.getColumnsUsedInClientWhereExpr());
385         return allAttribs;
386     }
387 
388 
389     public List<RowRequest> getRowRequestList(final HConnectionImpl conn,
390                                               final Mapping mapping,
391                                               final Set<ColumnAttrib> columnAttribs) throws HBqlException {
392 
393         final ColumnAttrib keyAttrib;
394 
395         if (this.hasAnIndex()) {
396             // Need to look up the index
397             final IndexSpecification index = conn.getIndexForTable(this.getIndexName(), mapping.getTableName());
398             final byte[][] cols = index.getIndexedColumns();
399             final String indexedColumName = new String(cols[0]);
400             keyAttrib = mapping.getAttribByVariableName(indexedColumName);
401         }
402         else {
403             keyAttrib = mapping.getKeyAttrib();
404         }
405 
406         final List<RowRequest> retval = Lists.newArrayList();
407 
408         for (final KeyRange keyRange : this.getKeyRangeArgs().getKeyRangeList()) {
409             final List<RowRequest> rowRequestList = keyRange.getRowRequestList(this, keyAttrib, columnAttribs);
410             retval.addAll(rowRequestList);
411         }
412 
413         return retval;
414     }
415 
416     public void setGetArgs(final Get get, final Set<ColumnAttrib> columnAttribs) throws HBqlException {
417 
418         // Set column names
419         // First add the columns and then add the families -- the order matters!
420         // Do not bother to request key because it will always be returned
421         for (final ColumnAttrib attrib : columnAttribs) {
422             if (!attrib.isAKeyAttrib() && !attrib.isASelectFamilyAttrib())
423                 get.addColumn(attrib.getFamilyNameAsBytes(), attrib.getColumnNameAsBytes());
424         }
425 
426         for (final ColumnAttrib attrib : columnAttribs) {
427             if (!attrib.isAKeyAttrib() && attrib.isASelectFamilyAttrib())
428                 get.addFamily(attrib.getFamilyNameAsBytes());
429         }
430 
431         if (this.getTimestampArgs() != null)
432             this.getTimestampArgs().setTimeStamp(get);
433 
434         if (this.getVersionArgs() != null)
435             this.getVersionArgs().setMaxVersions(get);
436 
437         // Do not call scanner cache args call for get
438 
439         final Filter filter = this.getServerFilter(false);
440         if (filter != null)
441             get.setFilter(filter);
442     }
443 
444     public void setScanArgs(final Scan scan, final Set<ColumnAttrib> columnAttribs) throws HBqlException {
445 
446         // Set column names
447         // First add the columns and then add the families -- the order matters!
448         // Do not bother to request key because it will always be returned
449         for (final ColumnAttrib attrib : columnAttribs) {
450             if (!attrib.isAKeyAttrib() && !attrib.isASelectFamilyAttrib())
451                 scan.addColumn(attrib.getFamilyNameAsBytes(), attrib.getColumnNameAsBytes());
452         }
453 
454         for (final ColumnAttrib attrib : columnAttribs) {
455             if (!attrib.isAKeyAttrib() && attrib.isASelectFamilyAttrib())
456                 scan.addFamily(attrib.getFamilyNameAsBytes());
457         }
458 
459         if (this.getTimestampArgs() != null)
460             this.getTimestampArgs().setTimeStamp(scan);
461 
462         if (this.getVersionArgs() != null)
463             this.getVersionArgs().setMaxVersions(scan);
464 
465         if (this.getScannerCacheArgs() != null)
466             this.getScannerCacheArgs().setScannerCacheSize(scan);
467 
468         final Filter filter = this.getServerFilter(true);
469         if (filter != null)
470             scan.setFilter(filter);
471     }
472 
473     private Filter getServerFilter(final boolean applyLimit) throws HBqlException {
474 
475         Filter exprFilter = null;
476 
477         if (this.getServerExpressionTree() != null) {
478             try {
479                 exprFilter = this.getServerExpressionTree().getFilter();
480             }
481             catch (HBqlException e) {
482                 // Use RecordFilter instead
483                 //if (this.getServerExpressionTree() != null)
484                 this.getServerExpressionTree().setMappingContext(this.getMappingContext());
485                 exprFilter = RecordFilter.newRecordFilter(this.getServerExpressionTree());
486             }
487 
488             if (exprFilter != null && exprFilter instanceof InstrumentedFilter) {
489                 ((InstrumentedFilter)exprFilter).setVerbose(this.getVerbose());
490             }
491         }
492 
493         // Now apply PageFilter if limit was specified
494         if (applyLimit && this.getLimit() > 0) {
495             final InstrumentedFilter filter = new PageFilter(this.getLimit(), exprFilter);
496             filter.setVerbose(this.getVerbose());
497             return filter;
498         }
499         else {
500             return exprFilter;
501         }
502     }
503 
504     public int getMaxVersions() throws HBqlException {
505         return this.getVersionArgs() != null ? this.getVersionArgs().getMaxVersions() : Integer.MAX_VALUE;
506     }
507 }