/** * @param groups * to initialize the codec with * @return an instance that is initialized with the given {@link ColumnGroup}s, for testing purposes */ public static CoveredColumnIndexCodec getCodecForTesting(List<ColumnGroup> groups) { CoveredColumnIndexCodec codec = new CoveredColumnIndexCodec(); codec.groups = Lists.newArrayList(groups); return codec; }
@Override public Iterable<IndexUpdate> getIndexDeletes(TableState state, IndexMetaData context, byte[] regionStartKey, byte[] regionEndKey) { List<IndexUpdate> deletes = new ArrayList<IndexUpdate>(groups.size()); for (ColumnGroup group : groups) { deletes.add(getDeleteForGroup(group, state, context)); } return deletes; }
@Override public Iterable<IndexUpdate> getIndexUpserts(TableState state, IndexMetaData indexMetaData, byte[] regionStartKey, byte[] regionEndKey) { List<IndexUpdate> updates = new ArrayList<IndexUpdate>(groups.size()); for (ColumnGroup group : groups) { IndexUpdate update = getIndexUpdateForGroup(group, state, indexMetaData); updates.add(update); } return updates; }
CoveredColumnIndexCodec.getCodecForTesting(Arrays.asList(group)); Iterable<IndexUpdate> updates = codec.getIndexDeletes(state, IndexMetaData.NULL_INDEX_META_DATA, null, null); assertFalse("Found index updates without any existing kvs in table!", updates.iterator().next() .isValid()); updates = codec.getIndexUpserts(state, IndexMetaData.NULL_INDEX_META_DATA, null, null); assertTrue("Didn't find index updates for pending primary table update!", updates.iterator() .hasNext()); CoveredColumnIndexCodec.composeRowKey(PK, v1.length, Arrays.asList(toColumnEntry(v1))); assertArrayEquals("Didn't get expected index value", expected, m.getRow()); state.setCurrentTimestamp(2); updates = codec.getIndexDeletes(state, IndexMetaData.NULL_INDEX_META_DATA, null, null); for (IndexUpdate update : updates) { assertTrue("Didn't have any index cleanup, even though there is current state", CoveredColumnIndexCodec.composeRowKey(PK, v1.length, Arrays.asList(toColumnEntry(v1))); assertArrayEquals("Didn't get expected index value", expected, m.getRow());
/** * @param group * @param state * @return the update that should be made to the table */ private IndexUpdate getIndexUpdateForGroup(ColumnGroup group, TableState state, IndexMetaData indexMetaData) { List<CoveredColumn> refs = group.getColumns(); try { Pair<CoveredDeleteScanner, IndexUpdate> stateInfo = ((LocalTableState)state).getIndexedColumnsTableState(refs, false, false, indexMetaData); Scanner kvs = stateInfo.getFirst(); Pair<Integer, List<ColumnEntry>> columns = getNextEntries(refs, kvs, state.getCurrentRowKey()); // make sure we close the scanner kvs.close(); if (columns.getFirst().intValue() == 0) { return stateInfo.getSecond(); } // have all the column entries, so just turn it into a Delete for the row // convert the entries to the needed values byte[] rowKey = composeRowKey(state.getCurrentRowKey(), columns.getFirst(), columns.getSecond()); Put p = new Put(rowKey, state.getCurrentTimestamp()); // add the columns to the put addColumnsToPut(p, columns.getSecond()); // update the index info IndexUpdate update = stateInfo.getSecond(); update.setTable(Bytes.toBytes(group.getTable())); update.setUpdate(p); return update; } catch (IOException e) { throw new RuntimeException("Unexpected exception when getting state for columns: " + refs); } }
/** * Ensure that we correctly can determine when a row key is empty (no values). */ @Test public void testCheckRowKeyForAllNulls() { byte[] pk = new byte[] { 'a', 'b', 'z' }; // check positive cases first byte[] result = EMPTY_INDEX_KEY; assertTrue("Didn't correctly read single element as being null in row key", CoveredColumnIndexCodec.checkRowKeyForAllNulls(result)); result = CoveredColumnIndexCodec.composeRowKey(pk, 0, Lists.newArrayList(toColumnEntry(new byte[0]), toColumnEntry(new byte[0]))); assertTrue("Didn't correctly read two elements as being null in row key", CoveredColumnIndexCodec.checkRowKeyForAllNulls(result)); // check cases where it isn't null result = CoveredColumnIndexCodec.composeRowKey(pk, 2, Arrays.asList(toColumnEntry(new byte[] { 1, 2 }))); assertFalse("Found a null key, when it wasn't!", CoveredColumnIndexCodec.checkRowKeyForAllNulls(result)); result = CoveredColumnIndexCodec.composeRowKey(pk, 2, Arrays.asList(toColumnEntry(new byte[] { 1, 2 }), toColumnEntry(new byte[0]))); assertFalse("Found a null key, when it wasn't!", CoveredColumnIndexCodec.checkRowKeyForAllNulls(result)); }
/** * Essentially a short-cut from building a {@link Put}. * * @param pk * row key * @param timestamp * timestamp of all the keyvalues * @param values * expected value--column pair * @return a keyvalues that the index contains for a given row at a timestamp with the given value -- column pairs. */ public static List<Cell> getIndexKeyValueForTesting(byte[] pk, long timestamp, List<Pair<byte[], CoveredColumn>> values) { int length = 0; List<ColumnEntry> expected = new ArrayList<ColumnEntry>(values.size()); for (Pair<byte[], CoveredColumn> value : values) { ColumnEntry entry = new ColumnEntry(value.getFirst(), value.getSecond()); length += value.getFirst().length; expected.add(entry); } byte[] rowKey = CoveredColumnIndexCodec.composeRowKey(pk, length, expected); Put p = new Put(rowKey, timestamp); CoveredColumnIndexCodec.addColumnsToPut(p, expected); List<Cell> kvs = new ArrayList<Cell>(); for (Entry<byte[], List<Cell>> entry : p.getFamilyCellMap().entrySet()) { kvs.addAll(entry.getValue()); } return kvs; }
List<byte[]> stored = CoveredColumnIndexCodec.getValues(indexKey); assertEquals("Found some stored values in an index row key that wasn't created with values!", 0, stored.size()); stored = CoveredColumnIndexCodec.getValues(indexKey); assertEquals("Found some stored values in an index row key that wasn't created with values!", 1, stored.size()); int len = v1.length + v2.length + v3.length; indexKey = CoveredColumnIndexCodec.composeRowKey(PK, len, Arrays.asList(toColumnEntry(v1), toColumnEntry(v2), toColumnEntry(v3))); stored = CoveredColumnIndexCodec.getValues(indexKey); assertEquals("Didn't find expected number of values in index key!", 3, stored.size()); assertTrue("First index keys don't match!", Bytes.equals(v1, stored.get(0)));
/** * Get all the deletes necessary for a group of columns - logically, the cleanup the index table for a given index. * * @param group * index information * @return the cleanup for the given index, or <tt>null</tt> if no cleanup is necessary */ private IndexUpdate getDeleteForGroup(ColumnGroup group, TableState state, IndexMetaData indexMetaData) { List<CoveredColumn> refs = group.getColumns(); try { Pair<CoveredDeleteScanner, IndexUpdate> kvs = ((LocalTableState)state).getIndexedColumnsTableState(refs, false, false, indexMetaData); Pair<Integer, List<ColumnEntry>> columns = getNextEntries(refs, kvs.getFirst(), state.getCurrentRowKey()); // make sure we close the scanner reference kvs.getFirst().close(); // no change, just return the passed update if (columns.getFirst() == 0) { return kvs.getSecond(); } // have all the column entries, so just turn it into a Delete for the row // convert the entries to the needed values byte[] rowKey = composeRowKey(state.getCurrentRowKey(), columns.getFirst(), columns.getSecond()); Delete d = new Delete(rowKey); d.setTimestamp(state.getCurrentTimestamp()); IndexUpdate update = kvs.getSecond(); update.setUpdate(d); update.setTable(Bytes.toBytes(group.getTable())); return update; } catch (IOException e) { throw new RuntimeException("Unexpected exception when getting state for columns: " + refs); } }