blob: d56d8cd345d190e9f33904e6f6cac15a0a2c301a [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.cassandra.db;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import junit.framework.Assert;
import org.apache.cassandra.Util;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.db.transform.FilteredRows;
import org.apache.cassandra.db.transform.MoreRows;
import org.apache.cassandra.db.transform.Transformation;
import org.apache.cassandra.dht.Murmur3Partitioner;
import org.apache.cassandra.utils.AbstractIterator;
import org.apache.cassandra.utils.ByteBufferUtil;
public class TransformerTest
{
static final CFMetaData metadata = metadata();
static final DecoratedKey partitionKey = new BufferDecoratedKey(new Murmur3Partitioner.LongToken(0L), ByteBufferUtil.EMPTY_BYTE_BUFFER);
static final Row staticRow = BTreeRow.singleCellRow(Clustering.STATIC_CLUSTERING, new BufferCell(metadata.partitionColumns().columns(true).getSimple(0), 0L, 0, 0, ByteBufferUtil.bytes(-1), null));
static CFMetaData metadata()
{
CFMetaData.Builder builder = CFMetaData.Builder.create("", "");
builder.addPartitionKey("pk", BytesType.instance);
builder.addClusteringColumn("c", Int32Type.instance);
builder.addStaticColumn("s", Int32Type.instance);
builder.addRegularColumn("v", Int32Type.instance);
return builder.build();
}
// Mock Data
static abstract class AbstractBaseRowIterator<U extends Unfiltered> extends AbstractIterator<U> implements BaseRowIterator<U>
{
private final int i;
private boolean returned;
protected AbstractBaseRowIterator(int i)
{
this.i = i;
}
protected U computeNext()
{
if (returned)
return endOfData();
returned = true;
return (U) row(i);
}
public CFMetaData metadata()
{
return metadata;
}
public boolean isReverseOrder()
{
return false;
}
public PartitionColumns columns()
{
return metadata.partitionColumns();
}
public DecoratedKey partitionKey()
{
return partitionKey;
}
public Row staticRow()
{
return staticRow;
}
public boolean isEmpty()
{
return false;
}
public void close()
{
}
}
private static UnfilteredRowIterator unfiltered(int i)
{
class Iter extends AbstractBaseRowIterator<Unfiltered> implements UnfilteredRowIterator
{
protected Iter(int i)
{
super(i);
}
public DeletionTime partitionLevelDeletion()
{
return DeletionTime.LIVE;
}
public EncodingStats stats()
{
return EncodingStats.NO_STATS;
}
}
return new Iter(i);
}
private static RowIterator filtered(int i)
{
class Iter extends AbstractBaseRowIterator<Row> implements RowIterator
{
protected Iter(int i)
{
super(i);
}
}
return new Iter(i);
}
private static Row row(int i)
{
return BTreeRow.singleCellRow(Util.clustering(metadata.comparator, i),
new BufferCell(metadata.partitionColumns().columns(false).getSimple(0), 1L, BufferCell.NO_TTL, BufferCell.NO_DELETION_TIME, ByteBufferUtil.bytes(i), null));
}
// Transformations that check mock data ranges
private static Transformation expect(int from, int to, List<Check> checks)
{
Expect expect = new Expect(from, to);
checks.add(expect);
return expect;
}
abstract static class Check extends Transformation
{
public abstract void check();
}
static class Expect extends Check
{
final int from, to;
int cur;
boolean closed;
Expect(int from, int to)
{
this.from = from;
this.to = to;
this.cur = from;
}
public Row applyToRow(Row row)
{
Assert.assertEquals(cur++, ByteBufferUtil.toInt(row.clustering().get(0)));
return row;
}
public void onPartitionClose()
{
Assert.assertEquals(to, cur);
closed = true;
}
public void check()
{
Assert.assertTrue(closed);
}
}
// Combinations of mock data and checks for an empty, singleton, and extending (sequential) range
private static enum Filter
{
INIT, APPLY_INNER, APPLY_OUTER, NONE
}
private static BaseRowIterator<?> empty(Filter filter, List<Check> checks)
{
switch (filter)
{
case INIT:
return Transformation.apply(EmptyIterators.row(metadata, partitionKey, false), expect(0, 0, checks));
case APPLY_INNER:
return Transformation.apply(FilteredRows.filter(Transformation.apply(EmptyIterators.unfilteredRow(metadata, partitionKey, false), expect(0, 0, checks)), Integer.MAX_VALUE), expect(0, 0, checks));
case APPLY_OUTER:
case NONE:
return Transformation.apply(EmptyIterators.unfilteredRow(metadata, partitionKey, false), expect(0, 0, checks));
default:
throw new IllegalStateException();
}
}
private static BaseRowIterator<?> singleton(Filter filter, int i, List<Check> checks)
{
switch (filter)
{
case INIT:
return Transformation.apply(filtered(i), expect(i, i + 1, checks));
case APPLY_INNER:
return FilteredRows.filter(Transformation.apply(unfiltered(i), expect(i, i + 1, checks)), Integer.MAX_VALUE);
case APPLY_OUTER:
case NONE:
return Transformation.apply(unfiltered(i), expect(i, i + 1, checks));
default:
throw new IllegalStateException();
}
}
private static BaseRowIterator<?> extendingIterator(int count, Filter filter, List<Check> checks)
{
class RefillNested extends Expect implements MoreRows<BaseRowIterator<?>>
{
boolean returnedEmpty, returnedSingleton, returnedNested;
RefillNested(int from)
{
super(from, count);
}
public BaseRowIterator<?> moreContents()
{
// first call return an empty iterator,
// second call return a singleton iterator (with a function that expects to be around to receive just that item)
// third call return a nested version of ourselves, with a function that expects to receive all future values
// fourth call, return null, indicating no more iterators to return
if (!returnedEmpty)
{
returnedEmpty = true;
return empty(filter, checks);
}
if (!returnedSingleton)
{
returnedSingleton = true;
return singleton(filter, from, checks);
}
if (from + 1 >= to)
return null;
if (!returnedNested)
{
returnedNested = true;
RefillNested refill = new RefillNested(from + 1);
checks.add(refill);
return refill.applyTo(empty(filter, checks));
}
return null;
}
BaseRowIterator<?> applyTo(BaseRowIterator<?> iter)
{
if (iter instanceof UnfilteredRowIterator)
return Transformation.apply(MoreRows.extend((UnfilteredRowIterator) iter, this), this);
else
return Transformation.apply(MoreRows.extend((RowIterator) iter, this), this);
}
}
RefillNested refill = new RefillNested(0);
checks.add(refill);
BaseRowIterator<?> iter = empty(filter, checks);
switch (filter)
{
case APPLY_OUTER:
return FilteredRows.filter((UnfilteredRowIterator) refill.applyTo(iter), Integer.MAX_VALUE);
case APPLY_INNER:
case INIT:
case NONE:
return refill.applyTo(iter);
default:
throw new IllegalStateException();
}
}
@Test
public void testRowExtension()
{
for (Filter filter : Filter.values())
{
List<Check> checks = new ArrayList<>();
BaseRowIterator<?> iter = extendingIterator(5, filter, checks);
for (int i = 0 ; i < 5 ; i++)
{
Unfiltered u = iter.next();
assert u instanceof Row;
Assert.assertEquals(i, ByteBufferUtil.toInt(u.clustering().get(0)));
}
iter.close();
for (Check check : checks)
check.check();
}
}
}