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 */
018
019package org.apache.hadoop.hbase;
020
021import java.util.Comparator;
022
023import org.apache.hadoop.hbase.KeyValue.Type;
024import org.apache.hadoop.hbase.util.ByteBufferUtils;
025import org.apache.hadoop.hbase.util.Bytes;
026import org.apache.hbase.thirdparty.com.google.common.primitives.Longs;
027import org.apache.yetus.audience.InterfaceAudience;
028import org.apache.yetus.audience.InterfaceStability;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032
033
034/**
035 * Compare two HBase cells.  Do not use this method comparing <code>-ROOT-</code> or
036 * <code>hbase:meta</code> cells.  Cells from these tables need a specialized comparator, one that
037 * takes account of the special formatting of the row where we have commas to delimit table from
038 * regionname, from row.  See KeyValue for how it has a special comparator to do hbase:meta cells
039 * and yet another for -ROOT-.
040 * <p>While using this comparator for {{@link #compareRows(Cell, Cell)} et al, the hbase:meta cells
041 * format should be taken into consideration, for which the instance of this comparator
042 * should be used.  In all other cases the static APIs in this comparator would be enough
043 * <p>HOT methods. We spend a good portion of CPU comparing. Anything that makes the compare
044 * faster will likely manifest at the macro level. See also
045 * {@link BBKVComparator}. Use it when mostly {@link ByteBufferKeyValue}s.
046 * </p>
047 */
048@edu.umd.cs.findbugs.annotations.SuppressWarnings(
049    value="UNKNOWN",
050    justification="Findbugs doesn't like the way we are negating the result of a compare in below")
051@InterfaceAudience.Private
052@InterfaceStability.Evolving
053public class CellComparatorImpl implements CellComparator {
054  static final Logger LOG = LoggerFactory.getLogger(CellComparatorImpl.class);
055  /**
056   * Comparator for plain key/values; i.e. non-catalog table key/values. Works on Key portion
057   * of KeyValue only.
058   */
059  public static final CellComparatorImpl COMPARATOR = new CellComparatorImpl();
060  /**
061   * A {@link CellComparatorImpl} for <code>hbase:meta</code> catalog table
062   * {@link KeyValue}s.
063   */
064  public static final CellComparatorImpl META_COMPARATOR = new MetaCellComparator();
065
066  @Override
067  public final int compare(final Cell a, final Cell b) {
068    return compare(a, b, false);
069  }
070
071  @Override
072  public int compare(final Cell a, final Cell b, boolean ignoreSequenceid) {
073
074    int diff = 0;
075    // "Peel off" the most common path.
076    if (a instanceof ByteBufferKeyValue && b instanceof ByteBufferKeyValue) {
077      diff = BBKVComparator.compare((ByteBufferKeyValue)a, (ByteBufferKeyValue)b, ignoreSequenceid);
078      if (diff != 0) {
079        return diff;
080      }
081    } else {
082      diff = compareRows(a, b);
083      if (diff != 0) {
084        return diff;
085      }
086
087      diff = compareWithoutRow(a, b);
088      if (diff != 0) {
089        return diff;
090      }
091    }
092
093    // Negate following comparisons so later edits show up first mvccVersion: later sorts first
094    return ignoreSequenceid? diff: Long.compare(b.getSequenceId(), a.getSequenceId());
095  }
096
097  /**
098   * Compares the family and qualifier part of the cell
099   * @return 0 if both cells are equal, 1 if left cell is bigger than right, -1 otherwise
100   */
101  public final int compareColumns(final Cell left, final Cell right) {
102    int diff = compareFamilies(left, right);
103    if (diff != 0) {
104      return diff;
105    }
106    return compareQualifiers(left, right);
107  }
108
109  /**
110   * Compare the families of left and right cell
111   * @return 0 if both cells are equal, 1 if left cell is bigger than right, -1 otherwise
112   */
113  @Override
114  public final int compareFamilies(Cell left, Cell right) {
115    if (left instanceof ByteBufferExtendedCell && right instanceof ByteBufferExtendedCell) {
116      return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) left).getFamilyByteBuffer(),
117          ((ByteBufferExtendedCell) left).getFamilyPosition(), left.getFamilyLength(),
118          ((ByteBufferExtendedCell) right).getFamilyByteBuffer(),
119          ((ByteBufferExtendedCell) right).getFamilyPosition(), right.getFamilyLength());
120    }
121    if (left instanceof ByteBufferExtendedCell) {
122      return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) left).getFamilyByteBuffer(),
123          ((ByteBufferExtendedCell) left).getFamilyPosition(), left.getFamilyLength(),
124          right.getFamilyArray(), right.getFamilyOffset(), right.getFamilyLength());
125    }
126    if (right instanceof ByteBufferExtendedCell) {
127      // Notice how we flip the order of the compare here. We used to negate the return value but
128      // see what FindBugs says
129      // http://findbugs.sourceforge.net/bugDescriptions.html#RV_NEGATING_RESULT_OF_COMPARETO
130      // It suggest flipping the order to get same effect and 'safer'.
131      return ByteBufferUtils.compareTo(
132          left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(),
133          ((ByteBufferExtendedCell)right).getFamilyByteBuffer(),
134          ((ByteBufferExtendedCell)right).getFamilyPosition(), right.getFamilyLength());
135    }
136    return Bytes.compareTo(left.getFamilyArray(), left.getFamilyOffset(), left.getFamilyLength(),
137        right.getFamilyArray(), right.getFamilyOffset(), right.getFamilyLength());
138  }
139
140  /**
141   * Compare the qualifiers part of the left and right cells.
142   * @return 0 if both cells are equal, 1 if left cell is bigger than right, -1 otherwise
143   */
144  @Override
145  public final int compareQualifiers(Cell left, Cell right) {
146    if (left instanceof ByteBufferExtendedCell && right instanceof ByteBufferExtendedCell) {
147      return ByteBufferUtils
148          .compareTo(((ByteBufferExtendedCell) left).getQualifierByteBuffer(),
149              ((ByteBufferExtendedCell) left).getQualifierPosition(),
150              left.getQualifierLength(), ((ByteBufferExtendedCell) right).getQualifierByteBuffer(),
151              ((ByteBufferExtendedCell) right).getQualifierPosition(),
152              right.getQualifierLength());
153    }
154    if (left instanceof ByteBufferExtendedCell) {
155      return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) left).getQualifierByteBuffer(),
156          ((ByteBufferExtendedCell) left).getQualifierPosition(), left.getQualifierLength(),
157          right.getQualifierArray(), right.getQualifierOffset(), right.getQualifierLength());
158    }
159    if (right instanceof ByteBufferExtendedCell) {
160      // Notice how we flip the order of the compare here. We used to negate the return value but
161      // see what FindBugs says
162      // http://findbugs.sourceforge.net/bugDescriptions.html#RV_NEGATING_RESULT_OF_COMPARETO
163      // It suggest flipping the order to get same effect and 'safer'.
164      return ByteBufferUtils.compareTo(left.getQualifierArray(),
165          left.getQualifierOffset(), left.getQualifierLength(),
166          ((ByteBufferExtendedCell)right).getQualifierByteBuffer(),
167          ((ByteBufferExtendedCell)right).getQualifierPosition(), right.getQualifierLength());
168    }
169    return Bytes.compareTo(left.getQualifierArray(), left.getQualifierOffset(),
170        left.getQualifierLength(), right.getQualifierArray(), right.getQualifierOffset(),
171        right.getQualifierLength());
172  }
173
174  /**
175   * Compares the rows of the left and right cell.
176   * For the hbase:meta case this method is overridden such that it can handle hbase:meta cells.
177   * The caller should ensure using the appropriate comparator for hbase:meta.
178   * @return 0 if both cells are equal, 1 if left cell is bigger than right, -1 otherwise
179   */
180  @Override
181  public int compareRows(final Cell left, final Cell right) {
182    return compareRows(left, left.getRowLength(), right, right.getRowLength());
183  }
184
185  static int compareRows(final Cell left, int leftRowLength, final Cell right, int rightRowLength) {
186    // left and right can be exactly the same at the beginning of a row
187    if (left == right) {
188      return 0;
189    }
190    if (left instanceof ByteBufferExtendedCell && right instanceof ByteBufferExtendedCell) {
191      return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) left).getRowByteBuffer(),
192          ((ByteBufferExtendedCell) left).getRowPosition(), leftRowLength,
193          ((ByteBufferExtendedCell) right).getRowByteBuffer(),
194          ((ByteBufferExtendedCell) right).getRowPosition(), rightRowLength);
195    }
196    if (left instanceof ByteBufferExtendedCell) {
197      return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) left).getRowByteBuffer(),
198          ((ByteBufferExtendedCell) left).getRowPosition(), leftRowLength,
199          right.getRowArray(), right.getRowOffset(), rightRowLength);
200    }
201    if (right instanceof ByteBufferExtendedCell) {
202      // Notice how we flip the order of the compare here. We used to negate the return value but
203      // see what FindBugs says
204      // http://findbugs.sourceforge.net/bugDescriptions.html#RV_NEGATING_RESULT_OF_COMPARETO
205      // It suggest flipping the order to get same effect and 'safer'.
206      return ByteBufferUtils.compareTo(left.getRowArray(), left.getRowOffset(), leftRowLength,
207          ((ByteBufferExtendedCell)right).getRowByteBuffer(),
208          ((ByteBufferExtendedCell)right).getRowPosition(), rightRowLength);
209    }
210    return Bytes.compareTo(left.getRowArray(), left.getRowOffset(), left.getRowLength(),
211        right.getRowArray(), right.getRowOffset(), right.getRowLength());
212  }
213
214  /**
215   * Compares the row part of the cell with a simple plain byte[] like the
216   * stopRow in Scan. This should be used with context where for hbase:meta
217   * cells the {{@link #META_COMPARATOR} should be used
218   *
219   * @param left
220   *          the cell to be compared
221   * @param right
222   *          the kv serialized byte[] to be compared with
223   * @param roffset
224   *          the offset in the byte[]
225   * @param rlength
226   *          the length in the byte[]
227   * @return 0 if both cell and the byte[] are equal, 1 if the cell is bigger
228   *         than byte[], -1 otherwise
229   */
230  @Override
231  public int compareRows(Cell left, byte[] right, int roffset, int rlength) {
232    if (left instanceof ByteBufferExtendedCell) {
233      return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) left).getRowByteBuffer(),
234          ((ByteBufferExtendedCell) left).getRowPosition(), left.getRowLength(), right,
235          roffset, rlength);
236    }
237    return Bytes.compareTo(left.getRowArray(), left.getRowOffset(), left.getRowLength(), right,
238        roffset, rlength);
239  }
240
241  @Override
242  public final int compareWithoutRow(final Cell left, final Cell right) {
243    // If the column is not specified, the "minimum" key type appears the
244    // latest in the sorted order, regardless of the timestamp. This is used
245    // for specifying the last key/value in a given row, because there is no
246    // "lexicographically last column" (it would be infinitely long). The
247    // "maximum" key type does not need this behavior.
248    // Copied from KeyValue. This is bad in that we can't do memcmp w/ special rules like this.
249    int lFamLength = left.getFamilyLength();
250    int rFamLength = right.getFamilyLength();
251    int lQualLength = left.getQualifierLength();
252    int rQualLength = right.getQualifierLength();
253    if (lFamLength + lQualLength == 0
254          && left.getTypeByte() == Type.Minimum.getCode()) {
255      // left is "bigger", i.e. it appears later in the sorted order
256      return 1;
257    }
258    if (rFamLength + rQualLength == 0
259        && right.getTypeByte() == Type.Minimum.getCode()) {
260      return -1;
261    }
262    if (lFamLength != rFamLength) {
263      // comparing column family is enough.
264      return compareFamilies(left, right);
265    }
266    // Compare cf:qualifier
267    int diff = compareColumns(left, right);
268    if (diff != 0) {
269      return diff;
270    }
271
272    diff = compareTimestamps(left.getTimestamp(), right.getTimestamp());
273    if (diff != 0) {
274      return diff;
275    }
276
277    // Compare types. Let the delete types sort ahead of puts; i.e. types
278    // of higher numbers sort before those of lesser numbers. Maximum (255)
279    // appears ahead of everything, and minimum (0) appears after
280    // everything.
281    return (0xff & right.getTypeByte()) - (0xff & left.getTypeByte());
282  }
283
284  @Override
285  public int compareTimestamps(final Cell left, final Cell right) {
286    return compareTimestamps(left.getTimestamp(), right.getTimestamp());
287  }
288
289  @Override
290  public int compareTimestamps(final long ltimestamp, final long rtimestamp) {
291    // Swap order we pass into compare so we get DESCENDING order.
292    return Long.compare(rtimestamp, ltimestamp);
293  }
294
295  /**
296   * A {@link CellComparatorImpl} for <code>hbase:meta</code> catalog table
297   * {@link KeyValue}s.
298   */
299  public static class MetaCellComparator extends CellComparatorImpl {
300    // TODO: Do we need a ByteBufferKeyValue version of this?
301    @Override
302    public int compareRows(final Cell left, final Cell right) {
303      return compareRows(left.getRowArray(), left.getRowOffset(), left.getRowLength(),
304          right.getRowArray(), right.getRowOffset(), right.getRowLength());
305    }
306
307    @Override
308    public int compareRows(Cell left, byte[] right, int roffset, int rlength) {
309      return compareRows(left.getRowArray(), left.getRowOffset(), left.getRowLength(), right,
310          roffset, rlength);
311    }
312
313    @Override
314    public int compare(final Cell a, final Cell b, boolean ignoreSequenceid) {
315      int diff = compareRows(a, b);
316      if (diff != 0) {
317        return diff;
318      }
319
320      diff = compareWithoutRow(a, b);
321      if (diff != 0) {
322        return diff;
323      }
324
325      // Negate following comparisons so later edits show up first mvccVersion: later sorts first
326      return ignoreSequenceid? diff: Longs.compare(b.getSequenceId(), a.getSequenceId());
327    }
328
329    private static int compareRows(byte[] left, int loffset, int llength, byte[] right, int roffset,
330        int rlength) {
331      int leftDelimiter = Bytes.searchDelimiterIndex(left, loffset, llength, HConstants.DELIMITER);
332      int rightDelimiter = Bytes
333          .searchDelimiterIndex(right, roffset, rlength, HConstants.DELIMITER);
334      // Compare up to the delimiter
335      int lpart = (leftDelimiter < 0 ? llength : leftDelimiter - loffset);
336      int rpart = (rightDelimiter < 0 ? rlength : rightDelimiter - roffset);
337      int result = Bytes.compareTo(left, loffset, lpart, right, roffset, rpart);
338      if (result != 0) {
339        return result;
340      } else {
341        if (leftDelimiter < 0 && rightDelimiter >= 0) {
342          return -1;
343        } else if (rightDelimiter < 0 && leftDelimiter >= 0) {
344          return 1;
345        } else if (leftDelimiter < 0 && rightDelimiter < 0) {
346          return 0;
347        }
348      }
349      // Compare middle bit of the row.
350      // Move past delimiter
351      leftDelimiter++;
352      rightDelimiter++;
353      int leftFarDelimiter = Bytes.searchDelimiterIndexInReverse(left, leftDelimiter, llength
354          - (leftDelimiter - loffset), HConstants.DELIMITER);
355      int rightFarDelimiter = Bytes.searchDelimiterIndexInReverse(right, rightDelimiter, rlength
356          - (rightDelimiter - roffset), HConstants.DELIMITER);
357      // Now compare middlesection of row.
358      lpart = (leftFarDelimiter < 0 ? llength + loffset : leftFarDelimiter) - leftDelimiter;
359      rpart = (rightFarDelimiter < 0 ? rlength + roffset : rightFarDelimiter) - rightDelimiter;
360      result = Bytes.compareTo(left, leftDelimiter, lpart, right, rightDelimiter, rpart);
361      if (result != 0) {
362        return result;
363      } else {
364        if (leftDelimiter < 0 && rightDelimiter >= 0) {
365          return -1;
366        } else if (rightDelimiter < 0 && leftDelimiter >= 0) {
367          return 1;
368        } else if (leftDelimiter < 0 && rightDelimiter < 0) {
369          return 0;
370        }
371      }
372      // Compare last part of row, the rowid.
373      leftFarDelimiter++;
374      rightFarDelimiter++;
375      result = Bytes.compareTo(left, leftFarDelimiter, llength - (leftFarDelimiter - loffset),
376          right, rightFarDelimiter, rlength - (rightFarDelimiter - roffset));
377      return result;
378    }
379
380    @Override
381    public Comparator getSimpleComparator() {
382      return this;
383    }
384  }
385
386  @Override
387  public Comparator getSimpleComparator() {
388    return new BBKVComparator(this);
389  }
390}