blob: 56ec0272c40e8aab42c33fb0ac268680d81aaf34 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.phoenix.index;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.phoenix.coprocessor.IndexRebuildRegionScanner;
import org.apache.phoenix.hbase.index.IndexRegionObserver;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.query.BaseConnectionlessQueryTest;
import org.apache.phoenix.query.QueryConstants;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.PTableKey;
import org.apache.phoenix.util.SchemaUtil;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class PrepareIndexMutationsForRebuildTest extends BaseConnectionlessQueryTest {
private static String ROW_KEY = "k1";
private static String TABLE_NAME = "dataTable";
private static String INDEX_NAME = "idx";
class SetupInfo {
public IndexMaintainer indexMaintainer;
public PTable pDataTable;
}
/**
* Get the index maintainer and phoenix table definition of data table.
* @param tableName
* @param indexName
* @param columns
* @param indexColumns
* @param pk
* @param includeColumns
* @return
* @throws Exception
*/
private SetupInfo setup(String tableName,
String indexName,
String columns,
String indexColumns,
String pk,
String includeColumns) throws Exception {
try(Connection conn = DriverManager.getConnection(getUrl())) {
String fullTableName = SchemaUtil.getTableName(SchemaUtil.normalizeIdentifier(""), SchemaUtil.normalizeIdentifier(tableName));
String fullIndexName = SchemaUtil.getTableName(SchemaUtil.normalizeIdentifier(""), SchemaUtil.normalizeIdentifier(indexName));
// construct the data table and index based from the parameters
String str1 = String.format("CREATE TABLE %1$s (%2$s CONSTRAINT pk PRIMARY KEY (%3$s)) COLUMN_ENCODED_BYTES=0",
fullTableName,
columns,
pk);
conn.createStatement().execute(str1);
String str2 = String.format("CREATE INDEX %1$s ON %2$s (%3$s)",
fullIndexName,
fullTableName,
indexColumns);
if (!includeColumns.isEmpty())
str2 += " INCLUDE (" + includeColumns + ")";
conn.createStatement().execute(str2);
// Get the data table, index table and index maintainer reference from the client's ConnectionQueryServiceImpl
// In this way, we don't need to setup a local cluster.
PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
PTable pIndexTable = pconn.getTable(new PTableKey(pconn.getTenantId(), fullIndexName));
PTable pDataTable = pconn.getTable(new PTableKey(pconn.getTenantId(), fullTableName));
IndexMaintainer im = pIndexTable.getIndexMaintainer(pDataTable, pconn);
SetupInfo info = new SetupInfo();
info.indexMaintainer = im;
info.pDataTable = pDataTable;
return info;
}
}
/**
* Simulate one put mutation on the indexed column
* @throws Exception
*/
@Test
public void testSinglePutOnIndexColumn() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, C1 VARCHAR, C2 VARCHAR",
"C1",
"ROW_KEY",
"");
// insert a row
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
1,
Bytes.toBytes("v1"));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 1);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
null);
// Expect one row of index with row key "v1_k1"
Put idxPut1 = new Put(generateIndexRowKey("v1"));
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
assertEqualMutationList(Arrays.asList((Mutation)idxPut1), actualIndexMutations);
}
/**
* Simulate one put mutation on the non-indexed column
* @throws Exception
*/
@Test
public void testSinglePutOnNonIndexColumn() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, C1 VARCHAR, C2 VARCHAR",
"C1",
"ROW_KEY",
"");
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 1);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
null);
// Expect one row of index with row key "_k1", as indexed column C1 is nullable.
Put idxPut1 = new Put(generateIndexRowKey(null));
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
assertEqualMutationList(Arrays.asList((Mutation)idxPut1), actualIndexMutations);
}
/**
* Simulate the column delete on the index column
* @throws Exception
*/
@Test
public void testDelOnIndexColumn() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, C1 VARCHAR, C2 VARCHAR",
"C1",
"ROW_KEY",
"");
// insert the row for deletion
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
1,
Bytes.toBytes("v1"));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 1);
// only delete the value of column C1
Delete dataDel = new Delete(Bytes.toBytes(ROW_KEY));
addCellToDelMutation(dataDel,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
2,
KeyValue.Type.DeleteColumn);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
dataDel);
List<Mutation> expectedIndexMutation = new ArrayList<>();
// generate the index row key "v1_k1"
byte[] idxKeyBytes = generateIndexRowKey("v1");
Put idxPut1 = new Put(idxKeyBytes);
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
expectedIndexMutation.add(idxPut1);
// generate the index row key "_k1"
Put idxPut2 = new Put(generateIndexRowKey(null));
addEmptyColumnToIndexPutMutation(idxPut2, info.indexMaintainer, 2);
expectedIndexMutation.add(idxPut2);
// This deletion is to remove the row added by the idxPut1, as idxPut2 has different row key as idxPut1.
// Otherwise the row "v1_k1" will still be shown in the scan result
Delete idxDel = new Delete(idxKeyBytes);
addCellToDelMutation(idxDel,
QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES,
null,
2,
KeyValue.Type.DeleteFamily);
expectedIndexMutation.add(idxDel);
assertEqualMutationList(expectedIndexMutation, actualIndexMutations);
}
/**
* Simulate the column delete on the non-indexed column
* @throws Exception
*/
@Test
public void testDelOnNonIndexColumn() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, C1 VARCHAR, C2 VARCHAR",
"C1",
"ROW_KEY",
"");
// insert the row for deletion
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
1,
Bytes.toBytes("v1"));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 1);
// delete the value of column C2
Delete dataDel = new Delete(Bytes.toBytes(ROW_KEY));
addCellToDelMutation(dataDel,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C2"),
2,
KeyValue.Type.DeleteColumn);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
dataDel);
List<Mutation> expectedIndexMutations = new ArrayList<>();
byte[] idxKeyBytes = generateIndexRowKey("v1");
// idxPut1 is the corresponding index mutation of dataPut
Put idxPut1 = new Put(idxKeyBytes);
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
expectedIndexMutations.add(idxPut1);
// idxPut2 is required to update the timestamp, so the index row will have the same life time as its corresponding data row.
// No delete mutation is expected on index table, as data mutation happens only on non-indexed column.
Put idxPut2 = new Put(idxKeyBytes);
addEmptyColumnToIndexPutMutation(idxPut2, info.indexMaintainer, 2);
expectedIndexMutations.add(idxPut2);
assertEqualMutationList(expectedIndexMutations, actualIndexMutations);
}
/**
* Simulate the data deletion of all version on the indexed row
* @throws Exception
*/
@Test
public void testDeleteAllVersions() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, C1 VARCHAR",
"C1",
"ROW_KEY",
"");
// insert two versions for a single row
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
1,
Bytes.toBytes("v1"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 1);
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
2,
Bytes.toBytes("v2"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 2);
// DeleteFamily will delete all versions of the columns in that family
// Since C1 is the only column of the default column family, so deleting the default family removes all version
// of column C1
Delete dataDel = new Delete(Bytes.toBytes(ROW_KEY));
addCellToDelMutation(dataDel,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
null,
3,
KeyValue.Type.DeleteFamily);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
dataDel);
List<Mutation> expectedIndexMutations = new ArrayList<>();
byte[] idxKeyBytes1 = generateIndexRowKey("v1");
byte[] idxKeyBytes2 = generateIndexRowKey("v2");
// idxPut1 and idxPut2 are generated by two versions in dataPut
Put idxPut1 = new Put(idxKeyBytes1);
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
expectedIndexMutations.add(idxPut1);
Put idxPut2 = new Put(idxKeyBytes2);
addEmptyColumnToIndexPutMutation(idxPut2, info.indexMaintainer, 2);
expectedIndexMutations.add(idxPut2);
// idxDel1 is required to remove the row key "v1_k1" which is added by idxPut1.
// The ts of idxDel1 is same as idxPut2, because it is a result of idxPut2.
// Since C1 is the only index column, so it is translated to DeleteFamily mutation.
Delete idxDel1 = new Delete(idxKeyBytes1);
addCellToDelMutation(idxDel1,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
null,
2,
KeyValue.Type.DeleteFamily);
expectedIndexMutations.add(idxDel1);
// idxDel2 is corresponding index mutation of dataDel
Delete idxDel2 = new Delete(idxKeyBytes2);
addCellToDelMutation(idxDel2,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
null,
3,
KeyValue.Type.DeleteFamily);
expectedIndexMutations.add(idxDel2);
assertEqualMutationList(expectedIndexMutations, actualIndexMutations);
}
// Simulate the put and delete mutation with the same time stamp on the index
@Test
public void testPutDeleteOnSameTimeStamp() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, C1 VARCHAR",
"C1",
"ROW_KEY",
"");
// insert a row
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
1,
Bytes.toBytes("v1"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable,1);
// delete column of C1 from the inserted row
Delete dataDel = new Delete(Bytes.toBytes(ROW_KEY));
addCellToDelMutation(dataDel,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
1,
KeyValue.Type.DeleteColumn);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
dataDel);
List<Mutation> expectedIndexMutations = new ArrayList<>();
// The dataDel will be applied on top of dataPut when we replay them for index rebuild, when they have the same time stamp.
// idxPut1 is expected as in data table we still see the row of k1 with empty C1, so we need a row in index table with row key "_k1"
Put idxPut1 = new Put(generateIndexRowKey(null));
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
expectedIndexMutations.add(idxPut1);
assertEqualMutationList(Arrays.asList((Mutation)idxPut1), actualIndexMutations);
}
// Simulate the put and delete mutation on the covered column of data table
@Test
public void testCoveredIndexColumns() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, C1 VARCHAR, C2 VARCHAR",
"C1",
"ROW_KEY",
"C2");
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
1,
Bytes.toBytes("v1"));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 1);
Delete dataDel = new Delete(Bytes.toBytes(ROW_KEY));
addCellToDelMutation(dataDel,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C1"),
2,
KeyValue.Type.DeleteColumn);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
dataDel);
List<Mutation> expectedIndexMutations = new ArrayList<>();
byte[] idxKeyBytes = generateIndexRowKey("v1");
// idxPut1 is generated corresponding to dataPut.
// The column "0:C2" is generated from data table column family and column name, its family name is still default family name of index table
Put idxPut1 = new Put(idxKeyBytes);
addCellToPutMutation(idxPut1,
QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES,
Bytes.toBytes("0:C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
expectedIndexMutations.add(idxPut1);
// idxKey2 is required by dataDel, as dataDel change the corresponding row key of index table
List<Byte> idxKey2 = new ArrayList<>();
idxKey2.add(QueryConstants.SEPARATOR_BYTE);
idxKey2.addAll(com.google.common.primitives.Bytes.asList(Bytes.toBytes(ROW_KEY)));
byte[] idxKeyBytes2 = com.google.common.primitives.Bytes.toArray(idxKey2);
Put idxPut2 = new Put(idxKeyBytes2);
addCellToPutMutation(idxPut2,
QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES,
Bytes.toBytes("0:C2"),
2,
Bytes.toBytes("v2"));
addEmptyColumnToIndexPutMutation(idxPut2, info.indexMaintainer, 2);
expectedIndexMutations.add(idxPut2);
// idxDel is required to invalid the index row "v1_k1", dataDel removed the value of indexed column
Delete idxDel = new Delete(idxKeyBytes);
addCellToDelMutation(idxDel,
QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES,
null,
2,
KeyValue.Type.DeleteFamily);
expectedIndexMutations.add(idxDel);
assertEqualMutationList(expectedIndexMutations, actualIndexMutations);
}
// Simulate the scenario that index column, and covered column belong to different column families
@Test
public void testForMultipleFamilies() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, CF1.C1 VARCHAR, CF2.C2 VARCHAR", //define C1 and C2 with different families
"CF1.C1",
"ROW_KEY",
"CF2.C2");
// insert a row to the data table
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
Bytes.toBytes("CF1"),
Bytes.toBytes("C1"),
1,
Bytes.toBytes("v1"));
addCellToPutMutation(dataPut,
Bytes.toBytes("CF2"),
Bytes.toBytes("C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 1);
// delete the indexed column CF1:C1
Delete dataDel = new Delete(Bytes.toBytes(ROW_KEY));
addCellToDelMutation(dataDel,
Bytes.toBytes("CF1"),
Bytes.toBytes("C1"),
2,
KeyValue.Type.DeleteColumn);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
dataDel);
List<Mutation> expectedIndexMutation = new ArrayList<>();
byte[] idxKeyBytes = generateIndexRowKey("v1");
// index table will use the family name of the first covered column, which is CF2 here.
Put idxPut1 = new Put(idxKeyBytes);
addCellToPutMutation(idxPut1,
Bytes.toBytes("CF2"),
Bytes.toBytes("CF2:C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
expectedIndexMutation.add(idxPut1);
// idxPut2 and idxDel are the result of dataDel
// idxPut2 is to create the index row "_k1", idxDel is to invalid the index row "v1_k1".
Put idxPut2 = new Put(generateIndexRowKey(null));
addCellToPutMutation(idxPut2,
Bytes.toBytes("CF2"),
Bytes.toBytes("CF2:C2"),
2,
Bytes.toBytes("v2"));
addEmptyColumnToIndexPutMutation(idxPut2, info.indexMaintainer, 2);
expectedIndexMutation.add(idxPut2);
Delete idxDel = new Delete(idxKeyBytes);
addCellToDelMutation(idxDel,
Bytes.toBytes("CF2"),
null,
2,
KeyValue.Type.DeleteFamily);
expectedIndexMutation.add(idxDel);
assertEqualMutationList(expectedIndexMutation, actualIndexMutations);
}
// Simulate two data put with the same value but different time stamp.
// We expect to see 2 index mutations with same value but different time stamps.
@Test
public void testSameTypeOfMutationWithSameValueButDifferentTimeStamp() throws Exception {
SetupInfo info = setup(TABLE_NAME,
INDEX_NAME,
"ROW_KEY VARCHAR, C1 VARCHAR, C2 VARCHAR",
"C1",
"ROW_KEY",
"");
Put dataPut = new Put(Bytes.toBytes(ROW_KEY));
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C2"),
1,
Bytes.toBytes("v2"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 1);
addCellToPutMutation(dataPut,
info.indexMaintainer.getEmptyKeyValueFamily().copyBytesIfNecessary(),
Bytes.toBytes("C2"),
1,
Bytes.toBytes("v3"));
addEmptyColumnToDataPutMutation(dataPut, info.pDataTable, 2);
List<Mutation> actualIndexMutations = IndexRebuildRegionScanner.prepareIndexMutationsForRebuild(info.indexMaintainer,
dataPut,
null);
byte[] idxKeyBytes = generateIndexRowKey(null);
// idxPut1 and idxPut2 have same value but different time stamp
Put idxPut1 = new Put(idxKeyBytes);
addEmptyColumnToIndexPutMutation(idxPut1, info.indexMaintainer, 1);
Put idxPut2 = new Put(idxKeyBytes);
addEmptyColumnToIndexPutMutation(idxPut2, info.indexMaintainer, 2);
assertEqualMutationList(Arrays.asList((Mutation)idxPut1, (Mutation)idxPut2), actualIndexMutations);
}
/**
* Generate the row key for index table by the value of indexed column
* @param indexVal
* @return
*/
byte[] generateIndexRowKey(String indexVal) {
List<Byte> idxKey = new ArrayList<>();
if (indexVal != null && !indexVal.isEmpty())
idxKey.addAll(com.google.common.primitives.Bytes.asList(Bytes.toBytes(indexVal)));
idxKey.add(QueryConstants.SEPARATOR_BYTE);
idxKey.addAll(com.google.common.primitives.Bytes.asList(Bytes.toBytes(ROW_KEY)));
return com.google.common.primitives.Bytes.toArray(idxKey);
}
void addCellToPutMutation(Put put, byte[] family, byte[] column, long ts, byte[] value) throws Exception {
byte[] rowKey = put.getRow();
Cell cell = CellUtil.createCell(rowKey, family, column, ts, KeyValue.Type.Put.getCode(), value);
put.add(cell);
}
void addCellToDelMutation(Delete del, byte[] family, byte[] column, long ts, KeyValue.Type type) throws Exception {
byte[] rowKey = del.getRow();
Cell cell = CellUtil.createCell(rowKey, family, column, ts, type.getCode(), null);
del.addDeleteMarker(cell);
}
/**
* Add Empty column to the existing data put mutation
* @param put
* @param ptable
* @param ts
* @throws Exception
*/
void addEmptyColumnToDataPutMutation(Put put, PTable ptable, long ts) throws Exception {
addCellToPutMutation(put,
SchemaUtil.getEmptyColumnFamily(ptable),
QueryConstants.EMPTY_COLUMN_BYTES,
ts,
QueryConstants.EMPTY_COLUMN_VALUE_BYTES);
}
/**
* Add the verified flag to the existing index put mutation
* @param put
* @param im
* @param ts
* @throws Exception
*/
void addEmptyColumnToIndexPutMutation(Put put, IndexMaintainer im, long ts) throws Exception {
addCellToPutMutation(put,
im.getEmptyKeyValueFamily().copyBytesIfNecessary(),
QueryConstants.EMPTY_COLUMN_BYTES,
ts,
IndexRegionObserver.VERIFIED_BYTES);
}
/**
* Compare two mutation lists without worrying about the order of the mutations in the lists
* @param expectedMutations
* @param actualMutations
*/
void assertEqualMutationList(List<Mutation> expectedMutations,
List<Mutation> actualMutations) {
assertEquals(expectedMutations.size(), actualMutations.size());
for (Mutation expected : expectedMutations) {
boolean found = false;
for (Mutation actual: actualMutations) {
if (isEqualMutation(expected, actual)) {
actualMutations.remove(actual);
found = true;
break;
}
}
if (!found)
Assert.fail(String.format("Cannot find mutation:%s", expected));
}
}
/**
* Compare two mutations without worrying about the order of cells within each mutation
* @param expectedMutation
* @param actualMutation
* @return
*/
boolean isEqualMutation(Mutation expectedMutation, Mutation actualMutation){
List<Cell> expectedCells = new ArrayList<>();
for (List<Cell> cells : expectedMutation.getFamilyCellMap().values()) {
expectedCells.addAll(cells);
}
List<Cell> actualCells = new ArrayList<>();
for (List<Cell> cells : actualMutation.getFamilyCellMap().values()) {
actualCells.addAll(cells);
}
if (expectedCells.size() != actualCells.size())
return false;
for(Cell expected : expectedCells) {
boolean found = false;
for(Cell actual: actualCells){
if (isEqualCell(expected, actual)) {
actualCells.remove(actual);
found = true;
break;
}
}
if (!found)
return false;
}
return true;
}
boolean isEqualCell(Cell a, Cell b) {
return CellUtil.matchingRow(a, b)
&& CellUtil.matchingFamily(a, b)
&& CellUtil.matchingQualifier(a, b)
&& CellUtil.matchingTimestamp(a, b)
&& CellUtil.matchingType(a, b)
&& CellUtil.matchingValue(a, b);
}
}