/*
* 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.cassandra.triggers;
import java.nio.ByteBuffer;
import java.util.*;
import org.junit.Test;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.config.TriggerDefinition;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.composites.CellName;
import org.apache.cassandra.db.marshal.CompositeType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.InvalidRequestException;
import static org.junit.Assert.*;
import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
public class TriggerExecutorTest
{
@Test
public void sameKeySameCfColumnFamilies() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", SameKeySameCfTrigger.class.getName()));
ColumnFamily mutated = TriggerExecutor.instance.execute(bytes("k1"), makeCf(metadata, "v1", null));
assertEquals(bytes("v1"), mutated.getColumn(getColumnName(metadata, "c1")).value());
assertEquals(bytes("trigger"), mutated.getColumn(getColumnName(metadata, "c2")).value());
}
@Test(expected = InvalidRequestException.class)
public void sameKeyDifferentCfColumnFamilies() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", SameKeyDifferentCfTrigger.class.getName()));
TriggerExecutor.instance.execute(bytes("k1"), makeCf(metadata, "v1", null));
}
@Test(expected = InvalidRequestException.class)
public void differentKeyColumnFamilies() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", DifferentKeyTrigger.class.getName()));
TriggerExecutor.instance.execute(bytes("k1"), makeCf(metadata, "v1", null));
}
@Test
public void noTriggerMutations() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", NoOpTrigger.class.getName()));
Mutation rm = new Mutation(bytes("k1"), makeCf(metadata, "v1", null));
assertNull(TriggerExecutor.instance.execute(Collections.singletonList(rm)));
}
@Test
public void sameKeySameCfRowMutations() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", SameKeySameCfTrigger.class.getName()));
ColumnFamily cf1 = makeCf(metadata, "k1v1", null);
ColumnFamily cf2 = makeCf(metadata, "k2v1", null);
Mutation rm1 = new Mutation(bytes("k1"), cf1);
Mutation rm2 = new Mutation(bytes("k2"), cf2);
List<? extends IMutation> tmutations = new ArrayList<>(TriggerExecutor.instance.execute(Arrays.asList(rm1, rm2)));
assertEquals(2, tmutations.size());
Collections.sort(tmutations, new RmComparator());
List<ColumnFamily> mutatedCFs = new ArrayList<>(tmutations.get(0).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertEquals(bytes("k1v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertEquals(bytes("trigger"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")).value());
mutatedCFs = new ArrayList<>(tmutations.get(1).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertEquals(bytes("k2v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertEquals(bytes("trigger"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")).value());
}
@Test
public void sameKeySameCfPartialRowMutations() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", SameKeySameCfPartialTrigger.class.getName()));
ColumnFamily cf1 = makeCf(metadata, "k1v1", null);
ColumnFamily cf2 = makeCf(metadata, "k2v1", null);
Mutation rm1 = new Mutation(bytes("k1"), cf1);
Mutation rm2 = new Mutation(bytes("k2"), cf2);
List<? extends IMutation> tmutations = new ArrayList<>(TriggerExecutor.instance.execute(Arrays.asList(rm1, rm2)));
assertEquals(2, tmutations.size());
Collections.sort(tmutations, new RmComparator());
List<ColumnFamily> mutatedCFs = new ArrayList<>(tmutations.get(0).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertEquals(bytes("k1v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")));
mutatedCFs = new ArrayList<>(tmutations.get(1).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertEquals(bytes("k2v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertEquals(bytes("trigger"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")).value());
}
@Test
public void sameKeyDifferentCfRowMutations() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", SameKeyDifferentCfTrigger.class.getName()));
ColumnFamily cf1 = makeCf(metadata, "k1v1", null);
ColumnFamily cf2 = makeCf(metadata, "k2v1", null);
Mutation rm1 = new Mutation(bytes("k1"), cf1);
Mutation rm2 = new Mutation(bytes("k2"), cf2);
List<? extends IMutation> tmutations = new ArrayList<>(TriggerExecutor.instance.execute(Arrays.asList(rm1, rm2)));
assertEquals(2, tmutations.size());
Collections.sort(tmutations, new RmComparator());
List<ColumnFamily> mutatedCFs = new ArrayList<>(tmutations.get(0).getColumnFamilies());
assertEquals(2, mutatedCFs.size());
Collections.sort(mutatedCFs, new CfComparator());
assertEquals(bytes("k1v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")));
assertNull(mutatedCFs.get(1).getColumn(getColumnName(metadata, "c1")));
assertEquals(bytes("trigger"), mutatedCFs.get(1).getColumn(getColumnName(metadata, "c2")).value());
mutatedCFs = new ArrayList<>(tmutations.get(1).getColumnFamilies());
assertEquals(2, mutatedCFs.size());
Collections.sort(mutatedCFs, new CfComparator());
assertEquals(bytes("k2v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")));
assertNull(mutatedCFs.get(1).getColumn(getColumnName(metadata, "c1")));
assertEquals(bytes("trigger"), mutatedCFs.get(1).getColumn(getColumnName(metadata, "c2")).value());
}
@Test
public void sameKeyDifferentKsRowMutations() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", SameKeyDifferentKsTrigger.class.getName()));
ColumnFamily cf1 = makeCf(metadata, "k1v1", null);
ColumnFamily cf2 = makeCf(metadata, "k2v1", null);
Mutation rm1 = new Mutation(bytes("k1"), cf1);
Mutation rm2 = new Mutation(bytes("k2"), cf2);
List<? extends IMutation> tmutations = new ArrayList<>(TriggerExecutor.instance.execute(Arrays.asList(rm1, rm2)));
assertEquals(4, tmutations.size());
Collections.sort(tmutations, new RmComparator());
List<ColumnFamily> mutatedCFs = new ArrayList<>(tmutations.get(0).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertEquals(bytes("k1v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")));
mutatedCFs = new ArrayList<>(tmutations.get(1).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertEquals(bytes("k2v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")));
mutatedCFs = new ArrayList<>(tmutations.get(2).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")));
assertEquals(bytes("trigger"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")).value());
mutatedCFs = new ArrayList<>(tmutations.get(3).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")));
assertEquals(bytes("trigger"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")).value());
}
@Test
public void differentKeyRowMutations() throws ConfigurationException, InvalidRequestException
{
CFMetaData metadata = makeCfMetaData("ks1", "cf1", TriggerDefinition.create("test", DifferentKeyTrigger.class.getName()));
ColumnFamily cf = makeCf(metadata, "v1", null);
Mutation rm = new Mutation(UTF8Type.instance.fromString("k1"), cf);
List<? extends IMutation> tmutations = new ArrayList<>(TriggerExecutor.instance.execute(Arrays.asList(rm)));
assertEquals(2, tmutations.size());
Collections.sort(tmutations, new RmComparator());
assertEquals(bytes("k1"), tmutations.get(0).key());
assertEquals(bytes("otherKey"), tmutations.get(1).key());
List<ColumnFamily> mutatedCFs = new ArrayList<>(tmutations.get(0).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertEquals(bytes("v1"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")).value());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")));
mutatedCFs = new ArrayList<>(tmutations.get(1).getColumnFamilies());
assertEquals(1, mutatedCFs.size());
assertNull(mutatedCFs.get(0).getColumn(getColumnName(metadata, "c1")));
assertEquals(bytes("trigger"), mutatedCFs.get(0).getColumn(getColumnName(metadata, "c2")).value());
}
private static CFMetaData makeCfMetaData(String ks, String cf, TriggerDefinition trigger)
{
CFMetaData metadata = CFMetaData.sparseCFMetaData(ks, cf, CompositeType.getInstance(UTF8Type.instance));
metadata.keyValidator(UTF8Type.instance);
metadata.addOrReplaceColumnDefinition(ColumnDefinition.partitionKeyDef(metadata,
UTF8Type.instance.fromString("pkey"),
UTF8Type.instance,
null));
metadata.addOrReplaceColumnDefinition(ColumnDefinition.regularDef(metadata,
UTF8Type.instance.fromString("c1"),
UTF8Type.instance,
0));
metadata.addOrReplaceColumnDefinition(ColumnDefinition.regularDef(metadata,
UTF8Type.instance.fromString("c2"),
UTF8Type.instance,
0));
try
{
if (trigger != null)
metadata.addTriggerDefinition(trigger);
}
catch (InvalidRequestException e)
{
throw new AssertionError(e);
}
return metadata.rebuild();
}
private static ColumnFamily makeCf(CFMetaData metadata, String columnValue1, String columnValue2)
{
ColumnFamily cf = ArrayBackedSortedColumns.factory.create(metadata);
if (columnValue1 != null)
cf.addColumn(new BufferCell(getColumnName(metadata, "c1"), bytes(columnValue1)));
if (columnValue2 != null)
cf.addColumn(new BufferCell(getColumnName(metadata, "c2"), bytes(columnValue2)));
return cf;
}
private static CellName getColumnName(CFMetaData metadata, String stringName)
{
return metadata.comparator.makeCellName(stringName);
}
public static class NoOpTrigger implements ITrigger
{
public Collection<Mutation> augment(ByteBuffer key, ColumnFamily update)
{
return null;
}
}
public static class SameKeySameCfTrigger implements ITrigger
{
public Collection<Mutation> augment(ByteBuffer key, ColumnFamily update)
{
ColumnFamily cf = ArrayBackedSortedColumns.factory.create(update.metadata());
cf.addColumn(new BufferCell(getColumnName(update.metadata(), "c2"), bytes("trigger")));
return Collections.singletonList(new Mutation(update.metadata().ksName, key, cf));
}
}
public static class SameKeySameCfPartialTrigger implements ITrigger
{
public Collection<Mutation> augment(ByteBuffer key, ColumnFamily update)
{
if (!key.equals(bytes("k2")))
return null;
ColumnFamily cf = ArrayBackedSortedColumns.factory.create(update.metadata());
cf.addColumn(new BufferCell(getColumnName(update.metadata(), "c2"), bytes("trigger")));
return Collections.singletonList(new Mutation(update.metadata().ksName, key, cf));
}
}
public static class SameKeyDifferentCfTrigger implements ITrigger
{
public Collection<Mutation> augment(ByteBuffer key, ColumnFamily update)
{
ColumnFamily cf = ArrayBackedSortedColumns.factory.create(makeCfMetaData(update.metadata().ksName, "otherCf", null));
cf.addColumn(new BufferCell(getColumnName(update.metadata(), "c2"), bytes("trigger")));
return Collections.singletonList(new Mutation(cf.metadata().ksName, key, cf));
}
}
public static class SameKeyDifferentKsTrigger implements ITrigger
{
public Collection<Mutation> augment(ByteBuffer key, ColumnFamily update)
{
ColumnFamily cf = ArrayBackedSortedColumns.factory.create(makeCfMetaData("otherKs", "otherCf", null));
cf.addColumn(new BufferCell(getColumnName(update.metadata(), "c2"), bytes("trigger")));
return Collections.singletonList(new Mutation(cf.metadata().ksName, key, cf));
}
}
public static class DifferentKeyTrigger implements ITrigger
{
public Collection<Mutation> augment(ByteBuffer key, ColumnFamily update)
{
ColumnFamily cf = ArrayBackedSortedColumns.factory.create(update.metadata());
cf.addColumn(new BufferCell(getColumnName(update.metadata(), "c2"), bytes("trigger")));
return Collections.singletonList(new Mutation(cf.metadata().ksName, bytes("otherKey"), cf));
}
}
private static class RmComparator implements Comparator<IMutation>
{
public int compare(IMutation m1, IMutation m2)
{
int cmp = m1.getKeyspaceName().compareTo(m2.getKeyspaceName());
return cmp != 0 ? cmp : m1.key().compareTo(m2.key());
}
}
private static class CfComparator implements Comparator<ColumnFamily>
{
public int compare(ColumnFamily cf1, ColumnFamily cf2)
{
return cf1.metadata().cfName.compareTo(cf2.metadata().cfName);
}
}
}