HBASE-15323 Hbase Rest CheckAndDeleteAPi should be able to delete more cells (Ajith)

This commit is contained in:
Enis Soztutar 2016-02-29 12:24:18 -08:00
parent bc112888ef
commit 7c54525c89
4 changed files with 142 additions and 13 deletions

View File

@ -553,9 +553,12 @@ public class RowResource extends ResourceBase {
.build(); .build();
} }
List<CellModel> cellModels = rowModel.getCells();
int cellModelCount = cellModels.size();
delete = new Delete(key); delete = new Delete(key);
boolean retValue; boolean retValue;
CellModel valueToDeleteCell = rowModel.getCells().get(0); CellModel valueToDeleteCell = rowModel.getCells().get(cellModelCount -1);
byte[] valueToDeleteColumn = valueToDeleteCell.getColumn(); byte[] valueToDeleteColumn = valueToDeleteCell.getColumn();
if (valueToDeleteColumn == null) { if (valueToDeleteColumn == null) {
try { try {
@ -567,25 +570,62 @@ public class RowResource extends ResourceBase {
.build(); .build();
} }
} }
byte[][] parts = KeyValue.parseColumn(valueToDeleteColumn);
byte[][] parts ;
// Copy all the cells to the Delete request if extra cells are sent
if(cellModelCount > 1) {
for (int i = 0, n = cellModelCount - 1; i < n; i++) {
CellModel cell = cellModels.get(i);
byte[] col = cell.getColumn();
if (col == null) {
servlet.getMetrics().incrementFailedPutRequests(1);
return Response.status(Response.Status.BAD_REQUEST)
.type(MIMETYPE_TEXT).entity("Bad request: Column found to be null." + CRLF)
.build();
}
parts = KeyValue.parseColumn(col);
if (parts.length == 1) {
// Only Column Family is specified
delete.addFamily(parts[0], cell.getTimestamp());
} else if (parts.length == 2) {
delete.addColumn(parts[0], parts[1], cell.getTimestamp());
} else {
servlet.getMetrics().incrementFailedDeleteRequests(1);
return Response.status(Response.Status.BAD_REQUEST)
.type(MIMETYPE_TEXT)
.entity("Bad request: Column to delete incorrectly specified." + CRLF)
.build();
}
}
}
parts = KeyValue.parseColumn(valueToDeleteColumn);
if (parts.length == 2) { if (parts.length == 2) {
if (parts[1].length != 0) { if (parts[1].length != 0) {
delete.addColumns(parts[0], parts[1]); // To support backcompat of deleting a cell
// if that is the only cell passed to the rest api
if(cellModelCount == 1) {
delete.addColumns(parts[0], parts[1]);
}
retValue = table.checkAndDelete(key, parts[0], parts[1], retValue = table.checkAndDelete(key, parts[0], parts[1],
valueToDeleteCell.getValue(), delete); valueToDeleteCell.getValue(), delete);
} else { } else {
// The case of empty qualifier. // The case of empty qualifier.
delete.addColumns(parts[0], Bytes.toBytes(StringUtils.EMPTY)); if(cellModelCount == 1) {
delete.addColumns(parts[0], Bytes.toBytes(StringUtils.EMPTY));
}
retValue = table.checkAndDelete(key, parts[0], Bytes.toBytes(StringUtils.EMPTY), retValue = table.checkAndDelete(key, parts[0], Bytes.toBytes(StringUtils.EMPTY),
valueToDeleteCell.getValue(), delete); valueToDeleteCell.getValue(), delete);
} }
} else { } else {
servlet.getMetrics().incrementFailedDeleteRequests(1); servlet.getMetrics().incrementFailedDeleteRequests(1);
return Response.status(Response.Status.BAD_REQUEST) return Response.status(Response.Status.BAD_REQUEST)
.type(MIMETYPE_TEXT).entity("Bad request: Column incorrectly specified." + CRLF) .type(MIMETYPE_TEXT).entity("Bad request: Column to check incorrectly specified." + CRLF)
.build(); .build();
} }
delete.addColumns(parts[0], parts[1]);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("CHECK-AND-DELETE " + delete.toString() + ", returns " LOG.debug("CHECK-AND-DELETE " + delete.toString() + ", returns "

View File

@ -722,6 +722,7 @@ public class RemoteHTable implements Table {
public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
byte[] value, Delete delete) throws IOException { byte[] value, Delete delete) throws IOException {
Put put = new Put(row); Put put = new Put(row);
put.setFamilyCellMap(delete.getFamilyCellMap());
// column to check-the-value // column to check-the-value
put.add(new KeyValue(row, family, qualifier, value)); put.add(new KeyValue(row, family, qualifier, value));
CellSetModel model = buildModelFromPut(put); CellSetModel model = buildModelFromPut(put);

View File

@ -320,9 +320,16 @@ public class RowResourceBase {
} }
protected static Response checkAndDeleteXML(String url, String table, protected static Response checkAndDeleteXML(String url, String table,
String row, String column, String valueToCheck) String row, String column, String valueToCheck, HashMap<String,String> cellsToDelete)
throws IOException, JAXBException { throws IOException, JAXBException {
RowModel rowModel = new RowModel(row); RowModel rowModel = new RowModel(row);
if(cellsToDelete != null) {
for (Map.Entry<String,String> entry :cellsToDelete.entrySet()) {
rowModel.addCell(new CellModel(Bytes.toBytes(entry.getKey()), Bytes.toBytes(entry.getValue())));
}
}
// Add this at the end
rowModel.addCell(new CellModel(Bytes.toBytes(column), rowModel.addCell(new CellModel(Bytes.toBytes(column),
Bytes.toBytes(valueToCheck))); Bytes.toBytes(valueToCheck)));
CellSetModel cellSetModel = new CellSetModel(); CellSetModel cellSetModel = new CellSetModel();
@ -337,30 +344,46 @@ public class RowResourceBase {
protected static Response checkAndDeleteXML(String table, String row, protected static Response checkAndDeleteXML(String table, String row,
String column, String valueToCheck) throws IOException, JAXBException { String column, String valueToCheck) throws IOException, JAXBException {
return checkAndDeleteXML(table, row, column, valueToCheck, null);
}
protected static Response checkAndDeleteXML(String table, String row,
String column, String valueToCheck, HashMap<String,String> cellsToDelete) throws IOException, JAXBException {
StringBuilder path = new StringBuilder(); StringBuilder path = new StringBuilder();
path.append('/'); path.append('/');
path.append(table); path.append(table);
path.append('/'); path.append('/');
path.append(row); path.append(row);
path.append("?check=delete"); path.append("?check=delete");
return checkAndDeleteXML(path.toString(), table, row, column, valueToCheck); return checkAndDeleteXML(path.toString(), table, row, column, valueToCheck, cellsToDelete);
} }
protected static Response checkAndDeleteJson(String table, String row, protected static Response checkAndDeleteJson(String table, String row,
String column, String valueToCheck) throws IOException, JAXBException { String column, String valueToCheck) throws IOException, JAXBException {
return checkAndDeleteJson(table, row, column, valueToCheck, null);
}
protected static Response checkAndDeleteJson(String table, String row,
String column, String valueToCheck, HashMap<String,String> cellsToDelete) throws IOException, JAXBException {
StringBuilder path = new StringBuilder(); StringBuilder path = new StringBuilder();
path.append('/'); path.append('/');
path.append(table); path.append(table);
path.append('/'); path.append('/');
path.append(row); path.append(row);
path.append("?check=delete"); path.append("?check=delete");
return checkAndDeleteJson(path.toString(), table, row, column, valueToCheck); return checkAndDeleteJson(path.toString(), table, row, column, valueToCheck, cellsToDelete);
} }
protected static Response checkAndDeleteJson(String url, String table, protected static Response checkAndDeleteJson(String url, String table,
String row, String column, String valueToCheck) String row, String column, String valueToCheck, HashMap<String,String> cellsToDelete)
throws IOException, JAXBException { throws IOException, JAXBException {
RowModel rowModel = new RowModel(row); RowModel rowModel = new RowModel(row);
if(cellsToDelete != null) {
for (Map.Entry<String,String> entry :cellsToDelete.entrySet()) {
rowModel.addCell(new CellModel(Bytes.toBytes(entry.getKey()), Bytes.toBytes(entry.getValue())));
}
}
// Add this at the end
rowModel.addCell(new CellModel(Bytes.toBytes(column), rowModel.addCell(new CellModel(Bytes.toBytes(column),
Bytes.toBytes(valueToCheck))); Bytes.toBytes(valueToCheck)));
CellSetModel cellSetModel = new CellSetModel(); CellSetModel cellSetModel = new CellSetModel();
@ -374,19 +397,31 @@ public class RowResourceBase {
protected static Response checkAndDeletePB(String table, String row, protected static Response checkAndDeletePB(String table, String row,
String column, String value) throws IOException { String column, String value) throws IOException {
return checkAndDeletePB(table, row, column, value, null);
}
protected static Response checkAndDeletePB(String table, String row,
String column, String value, HashMap<String,String> cellsToDelete) throws IOException {
StringBuilder path = new StringBuilder(); StringBuilder path = new StringBuilder();
path.append('/'); path.append('/');
path.append(table); path.append(table);
path.append('/'); path.append('/');
path.append(row); path.append(row);
path.append("?check=delete"); path.append("?check=delete");
return checkAndDeleteValuePB(path.toString(), table, row, column, value); return checkAndDeleteValuePB(path.toString(), table, row, column, value, cellsToDelete);
} }
protected static Response checkAndDeleteValuePB(String url, String table, protected static Response checkAndDeleteValuePB(String url, String table,
String row, String column, String valueToCheck) String row, String column, String valueToCheck, HashMap<String,String> cellsToDelete)
throws IOException { throws IOException {
RowModel rowModel = new RowModel(row); RowModel rowModel = new RowModel(row);
if(cellsToDelete != null) {
for (Map.Entry<String,String> entry :cellsToDelete.entrySet()) {
rowModel.addCell(new CellModel(Bytes.toBytes(entry.getKey()), Bytes.toBytes(entry.getValue())));
}
}
// Add this at the end
rowModel.addCell(new CellModel(Bytes.toBytes(column), Bytes rowModel.addCell(new CellModel(Bytes.toBytes(column), Bytes
.toBytes(valueToCheck))); .toBytes(valueToCheck)));
CellSetModel cellSetModel = new CellSetModel(); CellSetModel cellSetModel = new CellSetModel();

View File

@ -197,6 +197,59 @@ public class TestGetAndPutResource extends RowResourceBase {
assertEquals(response.getCode(), 200); assertEquals(response.getCode(), 200);
} }
@Test
public void testMultipleCellCheckDeletePB() throws IOException, JAXBException {
Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
assertEquals(response.getCode(), 404);
// Add 3 Columns to setup the test
response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
assertEquals(response.getCode(), 200);
checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
response = putValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
assertEquals(response.getCode(), 200);
checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
response = putValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
assertEquals(response.getCode(), 200);
checkValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
// Deletes the following columns based on Column1 check
HashMap<String,String> cellsToDelete = new HashMap<String, String>();
cellsToDelete.put(COLUMN_2,VALUE_2); // Value does not matter
cellsToDelete.put(COLUMN_3,VALUE_3); // Value does not matter
// On Success update both the cells
response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1, cellsToDelete);
assertEquals(response.getCode(), 200);
checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
response = getValuePB(TABLE, ROW_1, COLUMN_2);
assertEquals(response.getCode(), 404);
response = getValuePB(TABLE, ROW_1, COLUMN_3);
assertEquals(response.getCode(), 404);
response = putValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
assertEquals(response.getCode(), 200);
checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
response = putValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
assertEquals(response.getCode(), 200);
checkValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
// On Failure, we dont update any cells
response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_3, cellsToDelete);
assertEquals(response.getCode(), 304);
checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
checkValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
response = deleteRow(TABLE, ROW_1);
assertEquals(response.getCode(), 200);
}
@Test @Test
public void testSingleCellGetPutBinary() throws IOException { public void testSingleCellGetPutBinary() throws IOException {
final String path = "/" + TABLE + "/" + ROW_3 + "/" + COLUMN_1; final String path = "/" + TABLE + "/" + ROW_3 + "/" + COLUMN_1;