001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import java.util.List;
021import java.util.Map;
022import org.apache.hadoop.hbase.exceptions.DeserializationException;
023import org.apache.hadoop.hbase.filter.Filter;
024import org.apache.hadoop.hbase.io.TimeRange;
025import org.apache.hadoop.hbase.security.access.AccessControlConstants;
026import org.apache.hadoop.hbase.security.access.AccessControlUtil;
027import org.apache.hadoop.hbase.security.access.Permission;
028import org.apache.hadoop.hbase.security.visibility.Authorizations;
029import org.apache.hadoop.hbase.security.visibility.VisibilityConstants;
030import org.apache.hadoop.hbase.util.Bytes;
031import org.apache.yetus.audience.InterfaceAudience;
032
033import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap;
034import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap;
035import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
036
037import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
038
039/**
040 * Base class for HBase read operations; e.g. Scan and Get.
041 */
042@InterfaceAudience.Public
043public abstract class Query extends OperationWithAttributes {
044  private static final String ISOLATION_LEVEL = "_isolationlevel_";
045  protected Filter filter = null;
046  protected int targetReplicaId = -1;
047  protected Consistency consistency = Consistency.STRONG;
048  protected Map<byte[], TimeRange> colFamTimeRangeMap = Maps.newTreeMap(Bytes.BYTES_COMPARATOR);
049  protected Boolean loadColumnFamiliesOnDemand = null;
050  protected boolean queryMetricsEnabled = false;
051
052  public Filter getFilter() {
053    return filter;
054  }
055
056  /**
057   * Apply the specified server-side filter when performing the Query. Only
058   * {@link Filter#filterCell(org.apache.hadoop.hbase.Cell)} is called AFTER all tests for ttl,
059   * column match, deletes and column family's max versions have been run.
060   * @param filter filter to run on the server
061   * @return this for invocation chaining
062   */
063  public Query setFilter(Filter filter) {
064    this.filter = filter;
065    return this;
066  }
067
068  /**
069   * Sets the authorizations to be used by this Query
070   */
071  public Query setAuthorizations(Authorizations authorizations) {
072    this.setAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY,
073      ProtobufUtil.toAuthorizations(authorizations).toByteArray());
074    return this;
075  }
076
077  /** Returns The authorizations this Query is associated with. n */
078  public Authorizations getAuthorizations() throws DeserializationException {
079    byte[] authorizationsBytes = this.getAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY);
080    if (authorizationsBytes == null) return null;
081    return ProtobufUtil.toAuthorizations(authorizationsBytes);
082  }
083
084  /** Returns The serialized ACL for this operation, or null if none */
085  public byte[] getACL() {
086    return getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL);
087  }
088
089  /**
090   * Set the ACL for the operation.
091   * @param user  User short name
092   * @param perms Permissions for the user
093   */
094  public Query setACL(String user, Permission perms) {
095    setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL,
096      AccessControlUtil.toUsersAndPermissions(user, perms).toByteArray());
097    return this;
098  }
099
100  /**
101   * Set the ACL for the operation.
102   * @param perms A map of permissions for a user or users
103   */
104  public Query setACL(Map<String, Permission> perms) {
105    ListMultimap<String, Permission> permMap = ArrayListMultimap.create();
106    for (Map.Entry<String, Permission> entry : perms.entrySet()) {
107      permMap.put(entry.getKey(), entry.getValue());
108    }
109    setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL,
110      AccessControlUtil.toUsersAndPermissions(permMap).toByteArray());
111    return this;
112  }
113
114  /**
115   * Returns the consistency level for this operation
116   * @return the consistency level
117   */
118  public Consistency getConsistency() {
119    return consistency;
120  }
121
122  /**
123   * Sets the consistency level for this operation
124   * @param consistency the consistency level
125   */
126  public Query setConsistency(Consistency consistency) {
127    this.consistency = consistency;
128    return this;
129  }
130
131  /**
132   * Specify region replica id where Query will fetch data from. Use this together with
133   * {@link #setConsistency(Consistency)} passing {@link Consistency#TIMELINE} to read data from a
134   * specific replicaId. <br>
135   * <b> Expert: </b>This is an advanced API exposed. Only use it if you know what you are doing
136   */
137  public Query setReplicaId(int Id) {
138    this.targetReplicaId = Id;
139    return this;
140  }
141
142  /**
143   * Returns region replica id where Query will fetch data from.
144   * @return region replica id or -1 if not set.
145   */
146  public int getReplicaId() {
147    return this.targetReplicaId;
148  }
149
150  /**
151   * Set the isolation level for this query. If the isolation level is set to READ_UNCOMMITTED, then
152   * this query will return data from committed and uncommitted transactions. If the isolation level
153   * is set to READ_COMMITTED, then this query will return data from committed transactions only. If
154   * a isolation level is not explicitly set on a Query, then it is assumed to be READ_COMMITTED.
155   * @param level IsolationLevel for this query
156   */
157  public Query setIsolationLevel(IsolationLevel level) {
158    setAttribute(ISOLATION_LEVEL, level.toBytes());
159    return this;
160  }
161
162  /**
163   * Enables the return of {@link QueryMetrics} alongside the corresponding result(s) for this query
164   * <p>
165   * This is intended for advanced users who need result-granular, server-side metrics
166   * <p>
167   * Does not work with calls to {@link Table#exists(Get)} and {@link Table#exists(List)}
168   * @param enabled {@code true} to enable collection of per-result query metrics {@code false} to
169   *                disable metrics collection (resulting in {@code null} metrics)
170   */
171  public Query setQueryMetricsEnabled(boolean enabled) {
172    this.queryMetricsEnabled = enabled;
173    return this;
174  }
175
176  /**
177   * Returns whether query metrics are enabled
178   * @return {@code true} if query metrics are enabled, {@code false} otherwise
179   */
180  public boolean isQueryMetricsEnabled() {
181    return queryMetricsEnabled;
182  }
183
184  /**
185   * Returns The isolation level of this query. If no isolation level was set for this query object,
186   * then it returns READ_COMMITTED.
187   */
188  public IsolationLevel getIsolationLevel() {
189    byte[] attr = getAttribute(ISOLATION_LEVEL);
190    return attr == null ? IsolationLevel.READ_COMMITTED : IsolationLevel.fromBytes(attr);
191  }
192
193  /**
194   * Set the value indicating whether loading CFs on demand should be allowed (cluster default is
195   * false). On-demand CF loading doesn't load column families until necessary, e.g. if you filter
196   * on one column, the other column family data will be loaded only for the rows that are included
197   * in result, not all rows like in normal case. With column-specific filters, like
198   * SingleColumnValueFilter w/filterIfMissing == true, this can deliver huge perf gains when
199   * there's a cf with lots of data; however, it can also lead to some inconsistent results, as
200   * follows: - if someone does a concurrent update to both column families in question you may get
201   * a row that never existed, e.g. for { rowKey = 5, { cat_videos =&gt; 1 }, { video =&gt; "my cat"
202   * } } someone puts rowKey 5 with { cat_videos =&gt; 0 }, { video =&gt; "my dog" }, concurrent
203   * scan filtering on "cat_videos == 1" can get { rowKey = 5, { cat_videos =&gt; 1 }, { video =&gt;
204   * "my dog" } }. - if there's a concurrent split and you have more than 2 column families, some
205   * rows may be missing some column families.
206   */
207  public Query setLoadColumnFamiliesOnDemand(boolean value) {
208    this.loadColumnFamiliesOnDemand = value;
209    return this;
210  }
211
212  /**
213   * Get the raw loadColumnFamiliesOnDemand setting; if it's not set, can be null.
214   */
215  public Boolean getLoadColumnFamiliesOnDemandValue() {
216    return this.loadColumnFamiliesOnDemand;
217  }
218
219  /**
220   * Get the logical value indicating whether on-demand CF loading should be allowed.
221   */
222  public boolean doLoadColumnFamiliesOnDemand() {
223    return (this.loadColumnFamiliesOnDemand != null) && this.loadColumnFamiliesOnDemand;
224  }
225
226  /**
227   * Get versions of columns only within the specified timestamp range, [minStamp, maxStamp) on a
228   * per CF bases. Note, default maximum versions to return is 1. If your time range spans more than
229   * one version and you want all versions returned, up the number of versions beyond the default.
230   * Column Family time ranges take precedence over the global time range.
231   * @param cf       the column family for which you want to restrict
232   * @param minStamp minimum timestamp value, inclusive
233   * @param maxStamp maximum timestamp value, exclusive
234   */
235
236  public Query setColumnFamilyTimeRange(byte[] cf, long minStamp, long maxStamp) {
237    colFamTimeRangeMap.put(cf, TimeRange.between(minStamp, maxStamp));
238    return this;
239  }
240
241  /** Returns A map of column families to time ranges */
242  public Map<byte[], TimeRange> getColumnFamilyTimeRange() {
243    return this.colFamTimeRangeMap;
244  }
245}