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