1   /**
2    * Copyright 2009 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  package org.apache.hadoop.hbase;
21  
22  import java.io.IOException;
23  import java.util.Set;
24  import java.util.TreeSet;
25  
26  import junit.framework.TestCase;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.hbase.KeyValue.KVComparator;
31  import org.apache.hadoop.hbase.KeyValue.MetaComparator;
32  import org.apache.hadoop.hbase.KeyValue.Type;
33  import org.apache.hadoop.hbase.util.Bytes;
34  
35  public class TestKeyValue extends TestCase {
36    private final Log LOG = LogFactory.getLog(this.getClass().getName());
37  
38    public void testColumnCompare() throws Exception {
39      final byte [] a = Bytes.toBytes("aaa");
40      byte [] family1 = Bytes.toBytes("abc");
41      byte [] qualifier1 = Bytes.toBytes("def");
42      byte [] family2 = Bytes.toBytes("abcd");
43      byte [] qualifier2 = Bytes.toBytes("ef");
44  
45      KeyValue aaa = new KeyValue(a, family1, qualifier1, 0L, Type.Put, a);
46      assertFalse(aaa.matchingColumn(family2, qualifier2));
47      assertTrue(aaa.matchingColumn(family1, qualifier1));
48      aaa = new KeyValue(a, family2, qualifier2, 0L, Type.Put, a);
49      assertFalse(aaa.matchingColumn(family1, qualifier1));
50      assertTrue(aaa.matchingColumn(family2,qualifier2));
51      byte [] nullQualifier = new byte[0];
52      aaa = new KeyValue(a, family1, nullQualifier, 0L, Type.Put, a);
53      assertTrue(aaa.matchingColumn(family1,null));
54      assertFalse(aaa.matchingColumn(family2,qualifier2));
55    }
56  
57    public void testBasics() throws Exception {
58      LOG.info("LOWKEY: " + KeyValue.LOWESTKEY.toString());
59      check(Bytes.toBytes(getName()),
60        Bytes.toBytes(getName()), Bytes.toBytes(getName()), 1,
61        Bytes.toBytes(getName()));
62      // Test empty value and empty column -- both should work. (not empty fam)
63      check(Bytes.toBytes(getName()), Bytes.toBytes(getName()), null, 1, null);
64      check(HConstants.EMPTY_BYTE_ARRAY, Bytes.toBytes(getName()), null, 1, null);
65    }
66  
67    private void check(final byte [] row, final byte [] family, byte [] qualifier,
68      final long timestamp, final byte [] value) {
69      KeyValue kv = new KeyValue(row, family, qualifier, timestamp, value);
70      assertTrue(Bytes.compareTo(kv.getRow(), row) == 0);
71      assertTrue(kv.matchingColumn(family, qualifier));
72      // Call toString to make sure it works.
73      LOG.info(kv.toString());
74    }
75  
76    public void testPlainCompare() throws Exception {
77      final byte [] a = Bytes.toBytes("aaa");
78      final byte [] b = Bytes.toBytes("bbb");
79      final byte [] fam = Bytes.toBytes("col");
80      final byte [] qf = Bytes.toBytes("umn");
81  //    final byte [] column = Bytes.toBytes("col:umn");
82      KeyValue aaa = new KeyValue(a, fam, qf, a);
83      KeyValue bbb = new KeyValue(b, fam, qf, b);
84      byte [] keyabb = aaa.getKey();
85      byte [] keybbb = bbb.getKey();
86      assertTrue(KeyValue.COMPARATOR.compare(aaa, bbb) < 0);
87      assertTrue(KeyValue.KEY_COMPARATOR.compare(keyabb, 0, keyabb.length, keybbb,
88        0, keybbb.length) < 0);
89      assertTrue(KeyValue.COMPARATOR.compare(bbb, aaa) > 0);
90      assertTrue(KeyValue.KEY_COMPARATOR.compare(keybbb, 0, keybbb.length, keyabb,
91        0, keyabb.length) > 0);
92      // Compare breaks if passed same ByteBuffer as both left and right arguments.
93      assertTrue(KeyValue.COMPARATOR.compare(bbb, bbb) == 0);
94      assertTrue(KeyValue.KEY_COMPARATOR.compare(keybbb, 0, keybbb.length, keybbb,
95        0, keybbb.length) == 0);
96      assertTrue(KeyValue.COMPARATOR.compare(aaa, aaa) == 0);
97      assertTrue(KeyValue.KEY_COMPARATOR.compare(keyabb, 0, keyabb.length, keyabb,
98        0, keyabb.length) == 0);
99      // Do compare with different timestamps.
100     aaa = new KeyValue(a, fam, qf, 1, a);
101     bbb = new KeyValue(a, fam, qf, 2, a);
102     assertTrue(KeyValue.COMPARATOR.compare(aaa, bbb) > 0);
103     assertTrue(KeyValue.COMPARATOR.compare(bbb, aaa) < 0);
104     assertTrue(KeyValue.COMPARATOR.compare(aaa, aaa) == 0);
105     // Do compare with different types.  Higher numbered types -- Delete
106     // should sort ahead of lower numbers; i.e. Put
107     aaa = new KeyValue(a, fam, qf, 1, KeyValue.Type.Delete, a);
108     bbb = new KeyValue(a, fam, qf, 1, a);
109     assertTrue(KeyValue.COMPARATOR.compare(aaa, bbb) < 0);
110     assertTrue(KeyValue.COMPARATOR.compare(bbb, aaa) > 0);
111     assertTrue(KeyValue.COMPARATOR.compare(aaa, aaa) == 0);
112   }
113 
114   public void testMoreComparisons() throws Exception {
115     // Root compares
116     long now = System.currentTimeMillis();
117     KeyValue a = new KeyValue(Bytes.toBytes(".META.,,99999999999999"), now);
118     KeyValue b = new KeyValue(Bytes.toBytes(".META.,,1"), now);
119     KVComparator c = new KeyValue.RootComparator();
120     assertTrue(c.compare(b, a) < 0);
121     KeyValue aa = new KeyValue(Bytes.toBytes(".META.,,1"), now);
122     KeyValue bb = new KeyValue(Bytes.toBytes(".META.,,1"),
123         Bytes.toBytes("info"), Bytes.toBytes("regioninfo"), 1235943454602L,
124         (byte[])null);
125     assertTrue(c.compare(aa, bb) < 0);
126 
127     // Meta compares
128     KeyValue aaa = new KeyValue(
129         Bytes.toBytes("TestScanMultipleVersions,row_0500,1236020145502"), now);
130     KeyValue bbb = new KeyValue(
131         Bytes.toBytes("TestScanMultipleVersions,,99999999999999"), now);
132     c = new KeyValue.MetaComparator();
133     assertTrue(c.compare(bbb, aaa) < 0);
134 
135     KeyValue aaaa = new KeyValue(Bytes.toBytes("TestScanMultipleVersions,,1236023996656"),
136         Bytes.toBytes("info"), Bytes.toBytes("regioninfo"), 1236024396271L,
137         (byte[])null);
138     assertTrue(c.compare(aaaa, bbb) < 0);
139 
140     KeyValue x = new KeyValue(Bytes.toBytes("TestScanMultipleVersions,row_0500,1236034574162"),
141         Bytes.toBytes("info"), Bytes.toBytes(""), 9223372036854775807L,
142         (byte[])null);
143     KeyValue y = new KeyValue(Bytes.toBytes("TestScanMultipleVersions,row_0500,1236034574162"),
144         Bytes.toBytes("info"), Bytes.toBytes("regioninfo"), 1236034574912L,
145         (byte[])null);
146     assertTrue(c.compare(x, y) < 0);
147     comparisons(new KeyValue.MetaComparator());
148     comparisons(new KeyValue.KVComparator());
149     metacomparisons(new KeyValue.RootComparator());
150     metacomparisons(new KeyValue.MetaComparator());
151   }
152 
153   public void testBadMetaCompareSingleDelim() {
154     MetaComparator c = new KeyValue.MetaComparator();
155     long now = System.currentTimeMillis();
156     // meta keys values are not quite right.  A users can enter illegal values 
157     // from shell when scanning meta.
158     KeyValue a = new KeyValue(Bytes.toBytes("table,a1"), now);
159     KeyValue b = new KeyValue(Bytes.toBytes("table,a2"), now);
160     try {
161       c.compare(a, b);
162     } catch (IllegalArgumentException iae) { 
163       assertEquals(".META. key must have two ',' delimiters and have the following" +
164       		" format: '<table>,<key>,<etc>'", iae.getMessage());
165       return;
166     }
167     fail("Expected IllegalArgumentException");
168   }
169 
170   public void testMetaComparatorTableKeysWithCommaOk() {
171     MetaComparator c = new KeyValue.MetaComparator();
172     long now = System.currentTimeMillis();
173     // meta keys values are not quite right.  A users can enter illegal values 
174     // from shell when scanning meta.
175     KeyValue a = new KeyValue(Bytes.toBytes("table,key,with,commas1,1234"), now);
176     KeyValue b = new KeyValue(Bytes.toBytes("table,key,with,commas2,0123"), now);
177     assertTrue(c.compare(a, b) < 0);
178   }
179   
180   /**
181    * Tests cases where rows keys have characters below the ','.
182    * See HBASE-832
183    * @throws IOException
184    */
185   public void testKeyValueBorderCases() throws IOException {
186     // % sorts before , so if we don't do special comparator, rowB would
187     // come before rowA.
188     KeyValue rowA = new KeyValue(Bytes.toBytes("testtable,www.hbase.org/,1234"),
189       Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null);
190     KeyValue rowB = new KeyValue(Bytes.toBytes("testtable,www.hbase.org/%20,99999"),
191         Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null);
192     assertTrue(KeyValue.META_COMPARATOR.compare(rowA, rowB) < 0);
193 
194     rowA = new KeyValue(Bytes.toBytes("testtable,,1234"), Bytes.toBytes("fam"),
195         Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null);
196     rowB = new KeyValue(Bytes.toBytes("testtable,$www.hbase.org/,99999"),
197         Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null);
198     assertTrue(KeyValue.META_COMPARATOR.compare(rowA, rowB) < 0);
199 
200     rowA = new KeyValue(Bytes.toBytes(".META.,testtable,www.hbase.org/,1234,4321"),
201         Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null);
202     rowB = new KeyValue(Bytes.toBytes(".META.,testtable,www.hbase.org/%20,99999,99999"),
203         Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[])null);
204     assertTrue(KeyValue.ROOT_COMPARATOR.compare(rowA, rowB) < 0);
205   }
206 
207   private void metacomparisons(final KeyValue.MetaComparator c) {
208     long now = System.currentTimeMillis();
209     assertTrue(c.compare(new KeyValue(Bytes.toBytes(".META.,a,,0,1"), now),
210       new KeyValue(Bytes.toBytes(".META.,a,,0,1"), now)) == 0);
211     KeyValue a = new KeyValue(Bytes.toBytes(".META.,a,,0,1"), now);
212     KeyValue b = new KeyValue(Bytes.toBytes(".META.,a,,0,2"), now);
213     assertTrue(c.compare(a, b) < 0);
214     assertTrue(c.compare(new KeyValue(Bytes.toBytes(".META.,a,,0,2"), now),
215       new KeyValue(Bytes.toBytes(".META.,a,,0,1"), now)) > 0);
216   }
217 
218   private void comparisons(final KeyValue.KVComparator c) {
219     long now = System.currentTimeMillis();
220     assertTrue(c.compare(new KeyValue(Bytes.toBytes(".META.,,1"), now),
221       new KeyValue(Bytes.toBytes(".META.,,1"), now)) == 0);
222     assertTrue(c.compare(new KeyValue(Bytes.toBytes(".META.,,1"), now),
223       new KeyValue(Bytes.toBytes(".META.,,2"), now)) < 0);
224     assertTrue(c.compare(new KeyValue(Bytes.toBytes(".META.,,2"), now),
225       new KeyValue(Bytes.toBytes(".META.,,1"), now)) > 0);
226   }
227 
228   public void testBinaryKeys() throws Exception {
229     Set<KeyValue> set = new TreeSet<KeyValue>(KeyValue.COMPARATOR);
230     final byte [] fam = Bytes.toBytes("col");
231     final byte [] qf = Bytes.toBytes("umn");
232     final byte [] nb = new byte[0];
233     KeyValue [] keys = {new KeyValue(Bytes.toBytes("aaaaa,\u0000\u0000,2"), fam, qf, 2, nb),
234       new KeyValue(Bytes.toBytes("aaaaa,\u0001,3"), fam, qf, 3, nb),
235       new KeyValue(Bytes.toBytes("aaaaa,,1"), fam, qf, 1, nb),
236       new KeyValue(Bytes.toBytes("aaaaa,\u1000,5"), fam, qf, 5, nb),
237       new KeyValue(Bytes.toBytes("aaaaa,a,4"), fam, qf, 4, nb),
238       new KeyValue(Bytes.toBytes("a,a,0"), fam, qf, 0, nb),
239     };
240     // Add to set with bad comparator
241     for (int i = 0; i < keys.length; i++) {
242       set.add(keys[i]);
243     }
244     // This will output the keys incorrectly.
245     boolean assertion = false;
246     int count = 0;
247     try {
248       for (KeyValue k: set) {
249         assertTrue(count++ == k.getTimestamp());
250       }
251     } catch (junit.framework.AssertionFailedError e) {
252       // Expected
253       assertion = true;
254     }
255     assertTrue(assertion);
256     // Make set with good comparator
257     set = new TreeSet<KeyValue>(new KeyValue.MetaComparator());
258     for (int i = 0; i < keys.length; i++) {
259       set.add(keys[i]);
260     }
261     count = 0;
262     for (KeyValue k: set) {
263       assertTrue(count++ == k.getTimestamp());
264     }
265     // Make up -ROOT- table keys.
266     KeyValue [] rootKeys = {
267         new KeyValue(Bytes.toBytes(".META.,aaaaa,\u0000\u0000,0,2"), fam, qf, 2, nb),
268         new KeyValue(Bytes.toBytes(".META.,aaaaa,\u0001,0,3"), fam, qf, 3, nb),
269         new KeyValue(Bytes.toBytes(".META.,aaaaa,,0,1"), fam, qf, 1, nb),
270         new KeyValue(Bytes.toBytes(".META.,aaaaa,\u1000,0,5"), fam, qf, 5, nb),
271         new KeyValue(Bytes.toBytes(".META.,aaaaa,a,0,4"), fam, qf, 4, nb),
272         new KeyValue(Bytes.toBytes(".META.,,0"), fam, qf, 0, nb),
273       };
274     // This will output the keys incorrectly.
275     set = new TreeSet<KeyValue>(new KeyValue.MetaComparator());
276     // Add to set with bad comparator
277     for (int i = 0; i < keys.length; i++) {
278       set.add(rootKeys[i]);
279     }
280     assertion = false;
281     count = 0;
282     try {
283       for (KeyValue k: set) {
284         assertTrue(count++ == k.getTimestamp());
285       }
286     } catch (junit.framework.AssertionFailedError e) {
287       // Expected
288       assertion = true;
289     }
290     // Now with right comparator
291     set = new TreeSet<KeyValue>(new KeyValue.RootComparator());
292     // Add to set with bad comparator
293     for (int i = 0; i < keys.length; i++) {
294       set.add(rootKeys[i]);
295     }
296     count = 0;
297     for (KeyValue k: set) {
298       assertTrue(count++ == k.getTimestamp());
299     }
300   }
301 
302   public void testStackedUpKeyValue() {
303     // Test multiple KeyValues in a single blob.
304 
305     // TODO actually write this test!
306 
307   }
308 
309   private final byte[] rowA = Bytes.toBytes("rowA");
310   private final byte[] rowB = Bytes.toBytes("rowB");
311 
312   private final byte[] family = Bytes.toBytes("family");
313   private final byte[] qualA = Bytes.toBytes("qfA");
314 
315   private void assertKVLess(KeyValue.KVComparator c,
316                             KeyValue less,
317                             KeyValue greater) {
318     int cmp = c.compare(less,greater);
319     assertTrue(cmp < 0);
320     cmp = c.compare(greater,less);
321     assertTrue(cmp > 0);
322   }
323 
324   public void testFirstLastOnRow() {
325     final KVComparator c = KeyValue.COMPARATOR;
326     long ts = 1;
327 
328     // These are listed in sort order (ie: every one should be less
329     // than the one on the next line).
330     final KeyValue firstOnRowA = KeyValue.createFirstOnRow(rowA);
331     final KeyValue kvA_1 = new KeyValue(rowA, null, null, ts, Type.Put);
332     final KeyValue kvA_2 = new KeyValue(rowA, family, qualA, ts, Type.Put);
333         
334     final KeyValue lastOnRowA = KeyValue.createLastOnRow(rowA);
335     final KeyValue firstOnRowB = KeyValue.createFirstOnRow(rowB);
336     final KeyValue kvB = new KeyValue(rowB, family, qualA, ts, Type.Put);
337 
338     assertKVLess(c, firstOnRowA, firstOnRowB);
339     assertKVLess(c, firstOnRowA, kvA_1);
340     assertKVLess(c, firstOnRowA, kvA_2);
341     assertKVLess(c, kvA_1, kvA_2);
342     assertKVLess(c, kvA_2, firstOnRowB);
343     assertKVLess(c, kvA_1, firstOnRowB);
344 
345     assertKVLess(c, lastOnRowA, firstOnRowB);
346     assertKVLess(c, firstOnRowB, kvB);
347     assertKVLess(c, lastOnRowA, kvB);
348 
349     assertKVLess(c, kvA_2, lastOnRowA);
350     assertKVLess(c, kvA_1, lastOnRowA);
351     assertKVLess(c, firstOnRowA, lastOnRowA);
352   }
353 
354   public void testCreateKeyOnly() throws Exception {
355     long ts = 1;
356     byte [] value = Bytes.toBytes("a real value");
357     byte [] evalue = new byte[0]; // empty value
358 
359     for (byte[] val : new byte[][]{value, evalue}) {
360       for (boolean useLen : new boolean[]{false,true}) {
361         KeyValue kv1 = new KeyValue(rowA, family, qualA, ts, val);
362         KeyValue kv1ko = kv1.createKeyOnly(useLen);
363         // keys are still the same
364         assertTrue(kv1.equals(kv1ko));
365         // but values are not
366         assertTrue(kv1ko.getValue().length == (useLen?Bytes.SIZEOF_INT:0));
367         if (useLen) {
368           assertEquals(kv1.getValueLength(), Bytes.toInt(kv1ko.getValue()));
369         }
370       }
371     }
372   }
373 }