Avoid unnecessary deserialization of terminal arguments when executing CQL functions

patch by Benjamin Lerer and Andrés de la Peña; reviewed by Benjamin Lerer for CASSANDRA-18566

Co-authored-by: Benjamin Lerer <b.lerer@gmail.com>
Co-authored-by: Andrés de la Peña <a.penya.garcia@gmail.com>
This commit is contained in:
Andrés de la Peña 2023-06-03 17:14:45 +01:00
parent ba3ad7487a
commit 4f3cb5de37
107 changed files with 2700 additions and 2084 deletions

View File

@ -1,4 +1,5 @@
5.0
* Avoid unnecessary deserialization of terminal arguments when executing CQL functions (CASSANDRA-18566)
* Remove dependency on pytz library for setting CQLSH timezones on Python version >= 3.9 (CASSANDRA-17433)
* Extend maximum expiration date (CASSANDRA-14227)
* Add guardrail for partition tombstones and deprecate compaction_tombstone_warning_threshold (CASSANDRA-17194)

View File

@ -522,7 +522,7 @@ public abstract class Constants
ByteBuffer current = getCurrentCellBuffer(partitionKey, params);
if (current == null)
return;
ByteBuffer newValue = type.add(type, current, type, increment);
ByteBuffer newValue = type.add(type.compose(current), type.compose(increment));
params.addCell(column, newValue);
}
else if (column.type instanceof StringType)

View File

@ -114,7 +114,8 @@ public abstract class AggregateFcts
return LongType.instance.decompose(count);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
count++;
}
@ -157,14 +158,14 @@ public abstract class AggregateFcts
return ((DecimalType) returnType()).decompose(sum);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
BigDecimal number = arguments.get(0);
if (value == null)
if (number == null)
return;
BigDecimal number = DecimalType.instance.compose(value);
sum = sum.add(number);
}
};
@ -198,15 +199,15 @@ public abstract class AggregateFcts
return DecimalType.instance.decompose(avg);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
BigDecimal number = arguments.get(0);
if (value == null)
if (number == null)
return;
count++;
BigDecimal number = DecimalType.instance.compose(value);
// avg = avg + (value - sum) / count.
avg = avg.add(number.subtract(avg).divide(BigDecimal.valueOf(count), RoundingMode.HALF_EVEN));
@ -238,14 +239,14 @@ public abstract class AggregateFcts
return ((IntegerType) returnType()).decompose(sum);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
BigInteger number = arguments.get(0);
if (value == null)
if (number == null)
return;
BigInteger number = IntegerType.instance.compose(value);
sum = sum.add(number);
}
};
@ -283,15 +284,15 @@ public abstract class AggregateFcts
return IntegerType.instance.decompose(sum.divide(BigInteger.valueOf(count)));
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
BigInteger number = arguments.get(0);
if (value == null)
if (number == null)
return;
count++;
BigInteger number = ((BigInteger) argTypes().get(0).compose(value));
sum = sum.add(number);
}
};
@ -323,14 +324,14 @@ public abstract class AggregateFcts
return ((ByteType) returnType()).decompose(sum);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
Number number = ((Number) argTypes().get(0).compose(value));
sum += number.byteValue();
}
};
@ -348,7 +349,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new AvgAggregate(ByteType.instance)
return new AvgAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException
{
@ -383,14 +384,14 @@ public abstract class AggregateFcts
return ((ShortType) returnType()).decompose(sum);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
Number number = ((Number) argTypes().get(0).compose(value));
sum += number.shortValue();
}
};
@ -408,7 +409,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new AvgAggregate(ShortType.instance)
return new AvgAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion)
{
@ -443,14 +444,14 @@ public abstract class AggregateFcts
return ((Int32Type) returnType()).decompose(sum);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
Number number = ((Number) argTypes().get(0).compose(value));
sum += number.intValue();
}
};
@ -468,7 +469,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new AvgAggregate(Int32Type.instance)
return new AvgAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion)
{
@ -504,7 +505,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new AvgAggregate(LongType.instance)
return new AvgAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion)
{
@ -525,7 +526,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new FloatSumAggregate(FloatType.instance)
return new FloatSumAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException
{
@ -545,7 +546,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new FloatAvgAggregate(FloatType.instance)
return new FloatAvgAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException
{
@ -566,7 +567,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new FloatSumAggregate(DoubleType.instance)
return new FloatSumAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException
{
@ -586,13 +587,6 @@ public abstract class AggregateFcts
private double compensation;
private double simpleSum;
private final AbstractType<?> numberType;
public FloatSumAggregate(AbstractType<?> numberType)
{
this.numberType = numberType;
}
public void reset()
{
sum = 0;
@ -600,16 +594,18 @@ public abstract class AggregateFcts
simpleSum = 0;
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
double number = ((Number) numberType.compose(value)).doubleValue();
simpleSum += number;
double tmp = number - compensation;
double d = number.doubleValue();
simpleSum += d;
double tmp = d - compensation;
double rounded = sum + tmp;
compensation = (rounded - sum) - tmp;
sum = rounded;
@ -644,13 +640,6 @@ public abstract class AggregateFcts
private BigDecimal bigSum = null;
private boolean overflow = false;
private final AbstractType<?> numberType;
public FloatAvgAggregate(AbstractType<?> numberType)
{
this.numberType = numberType;
}
public void reset()
{
sum = 0;
@ -685,34 +674,35 @@ public abstract class AggregateFcts
}
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
count++;
double number = ((Number) numberType.compose(value)).doubleValue();
double d = number.doubleValue();
if (overflow)
{
bigSum = bigSum.add(BigDecimal.valueOf(number));
bigSum = bigSum.add(BigDecimal.valueOf(d));
}
else
{
simpleSum += number;
simpleSum += d;
double prev = sum;
double tmp = number - compensation;
double tmp = d - compensation;
double rounded = sum + tmp;
compensation = (rounded - sum) - tmp;
sum = rounded;
if (Double.isInfinite(sum) && !Double.isInfinite(number))
if (Double.isInfinite(sum) && !Double.isInfinite(d))
{
overflow = true;
bigSum = BigDecimal.valueOf(prev).add(BigDecimal.valueOf(number));
bigSum = BigDecimal.valueOf(prev).add(BigDecimal.valueOf(d));
}
}
}
@ -728,7 +718,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new FloatAvgAggregate(DoubleType.instance)
return new FloatAvgAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException
{
@ -758,7 +748,7 @@ public abstract class AggregateFcts
{
public Aggregate newAggregate()
{
return new AvgAggregate(LongType.instance)
return new AvgAggregate()
{
public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException
{
@ -790,14 +780,15 @@ public abstract class AggregateFcts
return min != null ? LongType.instance.decompose(min) : null;
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
long lval = LongType.instance.compose(value);
long lval = number.longValue();
if (min == null || lval < min)
min = lval;
@ -828,14 +819,15 @@ public abstract class AggregateFcts
return max != null ? LongType.instance.decompose(max) : null;
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
long lval = LongType.instance.compose(value);
long lval = number.longValue();
if (max == null || lval > max)
max = lval;
@ -854,6 +846,13 @@ public abstract class AggregateFcts
{
return new NativeAggregateFunction("max", inputType, inputType)
{
@Override
public Arguments newArguments(ProtocolVersion version)
{
return FunctionArguments.newNoopInstance(version, 1);
}
@Override
public Aggregate newAggregate()
{
return new Aggregate()
@ -870,9 +869,10 @@ public abstract class AggregateFcts
return max;
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
ByteBuffer value = arguments.get(0);
if (value == null)
return;
@ -895,6 +895,13 @@ public abstract class AggregateFcts
{
return new NativeAggregateFunction("min", inputType, inputType)
{
@Override
public Arguments newArguments(ProtocolVersion version)
{
return FunctionArguments.newNoopInstance(version, 1);
}
@Override
public Aggregate newAggregate()
{
return new Aggregate()
@ -911,9 +918,10 @@ public abstract class AggregateFcts
return min;
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
ByteBuffer value = arguments.get(0);
if (value == null)
return;
@ -936,6 +944,13 @@ public abstract class AggregateFcts
{
return new NativeAggregateFunction("count", LongType.instance, inputType)
{
@Override
public Arguments newArguments(ProtocolVersion version)
{
return FunctionArguments.newNoopInstance(version, 1);
}
@Override
public Aggregate newAggregate()
{
return new Aggregate()
@ -952,11 +967,10 @@ public abstract class AggregateFcts
return ((LongType) returnType()).decompose(count);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
if (value == null)
if (arguments.get(0) == null)
return;
count++;
@ -980,14 +994,14 @@ public abstract class AggregateFcts
return LongType.instance.decompose(sum);
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
Number number = LongType.instance.compose(value);
sum += number.longValue();
}
}
@ -1004,13 +1018,6 @@ public abstract class AggregateFcts
private BigInteger bigSum = null;
private boolean overflow = false;
private final AbstractType<?> numberType;
public AvgAggregate(AbstractType<?> type)
{
this.numberType = type;
}
public void reset()
{
count = 0;
@ -1031,28 +1038,30 @@ public abstract class AggregateFcts
}
}
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values)
@Override
public void addInput(Arguments arguments)
{
ByteBuffer value = values.get(0);
Number number = arguments.get(0);
if (value == null)
if (number == null)
return;
count++;
long number = ((Number) numberType.compose(value)).longValue();
long l = number.longValue();
if (overflow)
{
bigSum = bigSum.add(BigInteger.valueOf(number));
bigSum = bigSum.add(BigInteger.valueOf(l));
}
else
{
long prev = sum;
sum += number;
sum += l;
if (((prev ^ sum) & (number ^ sum)) < 0)
if (((prev ^ sum) & (l ^ sum)) < 0)
{
overflow = true;
bigSum = BigInteger.valueOf(prev).add(BigInteger.valueOf(number));
bigSum = BigInteger.valueOf(prev).add(BigInteger.valueOf(l));
}
}
}

View File

@ -18,7 +18,6 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.ProtocolVersion;
@ -42,10 +41,10 @@ public interface AggregateFunction extends Function
{
/**
* Adds the specified input to this aggregate.
* @param protocolVersion native protocol version
* @param values the values to add to the aggregate.
*
* @param arguments the values to add to the aggregate.
*/
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values) throws InvalidRequestException;
public void addInput(Arguments arguments) throws InvalidRequestException;
/**
* Computes and returns the aggregate current value.

View File

@ -0,0 +1,43 @@
/*
* 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.cql3.functions;
import java.nio.ByteBuffer;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* Utility used to deserialize function arguments.
*/
public interface ArgumentDeserializer
{
/**
* An {@link ArgumentDeserializer} that do not deserialize data.
*/
ArgumentDeserializer NOOP_DESERIALIZER = (protocolVersion, buffer) -> buffer;
/**
* Deserializes the specified function argument.
*
* @param protocolVersion the protocol version
* @param buffer the serialized argument
* @return the deserialized argument
*/
Object deserialize(ProtocolVersion protocolVersion, ByteBuffer buffer);
}

View File

@ -0,0 +1,145 @@
/*
* 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.cql3.functions;
import java.nio.ByteBuffer;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* The input arguments to a function.
* <p>The class can be reused for calling the same function multiple times.</p>
*/
public interface Arguments
{
/**
* Sets the specified value to the arguments
*
* @param i the argument position
* @param buffer the serialized argument
*/
void set(int i, ByteBuffer buffer);
/**
* Checks if at least one of the arguments is null.
*
* @return {@code true} if at least one of the arguments is null, {@code false} otherwise.
*/
boolean containsNulls();
/**
* Returns the specified argument
*
* @param i the argument index
* @return the specified argument
*/
<T> T get(int i);
/**
* Returns the protocol version
*
* @return the protocol version
*/
ProtocolVersion getProtocolVersion();
/**
* Returns the specified argument as a {@code boolean}.
*
* @param i the argument index
* @return the specified argument as a {@code boolean}
*/
default boolean getAsBoolean(int i)
{
return this.<Boolean>get(i);
}
/**
* Returns the specified argument as a {@code byte}.
*
* @param i the argument index
* @return the specified argument as a {@code byte}
*/
default byte getAsByte(int i)
{
return this.<Number>get(i).byteValue();
}
/**
* Returns the specified argument as a {@code short}.
*
* @param i the argument index
* @return the specified argument as a {@code short}
*/
default short getAsShort(int i)
{
return this.<Number>get(i).shortValue();
}
/**
* Returns the specified argument as a {@code int}.
*
* @param i the argument index
* @return the specified argument as a {@code int}
*/
default int getAsInt(int i)
{
return this.<Number>get(i).intValue();
}
/**
* Returns the specified argument as a {@code long}.
*
* @param i the argument index
* @return the specified argument as a {@code long}
*/
default long getAsLong(int i)
{
return this.<Number>get(i).longValue();
}
/**
* Returns the specified argument as a {@code float}.
*
* @param i the argument index
* @return the specified argument as a {@code float}
*/
default float getAsFloat(int i)
{
return this.<Number>get(i).floatValue();
}
/**
* Returns the specified argument as a {@code double}.
*
* @param i the argument index
* @return the specified argument as a {@code double}
*/
default double getAsDouble(int i)
{
return this.<Number>get(i).doubleValue();
}
/**
* Returns the current number of arguments.
*
* @return the current number of arguments.
*/
int size();
}

View File

@ -18,9 +18,9 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.serializers.MarshalException;
@ -41,9 +41,23 @@ public abstract class BytesConversionFcts
}
}
private static abstract class BytesConversionFct extends NativeScalarFunction
{
public BytesConversionFct(String name, AbstractType<?> returnType, AbstractType<?>... argsType)
{
super(name, returnType, argsType);
}
@Override
public Arguments newArguments(ProtocolVersion version)
{
return FunctionArguments.newNoopInstance(version, 1);
}
}
// Most of the X_as_blob and blob_as_X functions are basically no-op since everything is
// bytes internally. They only "trick" the type system.
public static class ToBlobFunction extends NativeScalarFunction
public static class ToBlobFunction extends BytesConversionFct
{
private final CQL3Type fromType;
@ -61,9 +75,9 @@ public abstract class BytesConversionFcts
}
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
public ByteBuffer execute(Arguments arguments)
{
return parameters.get(0);
return arguments.get(0);
}
@Override
@ -73,7 +87,7 @@ public abstract class BytesConversionFcts
}
}
public static class FromBlobFunction extends NativeScalarFunction
public static class FromBlobFunction extends BytesConversionFct
{
private final CQL3Type toType;
@ -91,9 +105,9 @@ public abstract class BytesConversionFcts
}
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
public ByteBuffer execute(Arguments arguments)
{
ByteBuffer val = parameters.get(0);
ByteBuffer val = arguments.get(0);
if (val != null)
{

View File

@ -263,13 +263,13 @@ public final class CastFcts
return new JavaFunctionWrapper<>(inputType(), outputType(), converter, true);
}
public final ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public final ByteBuffer execute(Arguments arguments)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
if (arguments.containsNulls())
return null;
return outputType().decompose(converter.apply(compose(bb)));
return outputType().decompose(converter.apply(arguments.get(0)));
}
protected I compose(ByteBuffer bb)
@ -350,9 +350,10 @@ public final class CastFcts
return new CassandraFunctionWrapper<>(inputType(), outputType(), delegate, true);
}
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public ByteBuffer execute(Arguments arguments)
{
return delegate.execute(protocolVersion, parameters);
return delegate.execute(arguments);
}
}
@ -381,13 +382,25 @@ public final class CastFcts
return new CastAsTextFunction<>(inputType(), outputType(), true);
}
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public Arguments newArguments(ProtocolVersion version)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return new FunctionArguments(version, (protocolVersion, buffer) -> {
AbstractType<?> argType = argTypes.get(0);
if (buffer == null || (!buffer.hasRemaining() && argType.isEmptyValueMeaningless()))
return null;
return argType.getSerializer().toCQLLiteral(buffer);
});
}
@Override
public ByteBuffer execute(Arguments arguments)
{
if (arguments.containsNulls())
return null;
return outputType().decompose(inputType().getSerializer().toCQLLiteral(bb));
return outputType().decompose(arguments.get(0));
}
}

View File

@ -19,7 +19,6 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -127,13 +126,12 @@ public class CollectionFcts
return new NativeScalarFunction(name, outputType, inputType)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
public ByteBuffer execute(Arguments arguments)
{
ByteBuffer value = parameters.get(0);
if (value == null)
if (arguments.containsNulls())
return null;
Map<K, V> map = inputType.compose(value);
Map<K, V> map = arguments.get(0);
Set<K> keys = map.keySet();
return outputType.decompose(keys);
}
@ -156,13 +154,12 @@ public class CollectionFcts
return new NativeScalarFunction(name, outputType, inputType)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
public ByteBuffer execute(Arguments arguments)
{
ByteBuffer value = parameters.get(0);
if (value == null)
if (arguments.containsNulls())
return null;
Map<K, V> map = inputType.compose(value);
Map<K, V> map = arguments.get(0);
List<V> values = ImmutableList.copyOf(map.values());
return outputType.decompose(values);
}
@ -182,13 +179,19 @@ public class CollectionFcts
return new NativeScalarFunction(name, Int32Type.instance, inputType)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
public Arguments newArguments(ProtocolVersion version)
{
ByteBuffer value = parameters.get(0);
if (value == null)
return FunctionArguments.newNoopInstance(version, 1);
}
@Override
public ByteBuffer execute(Arguments arguments)
{
if (arguments.containsNulls())
return null;
int size = inputType.size(value);
int size = inputType.size(arguments.get(0));
return Int32Type.instance.decompose(size);
}
};
@ -356,15 +359,26 @@ public class CollectionFcts
}
@Override
public ByteBuffer execute(ProtocolVersion version, List<ByteBuffer> parameters)
public Arguments newArguments(ProtocolVersion version)
{
ByteBuffer value = parameters.get(0);
if (value == null)
return FunctionArguments.newNoopInstance(version, 1);
}
@Override
public ByteBuffer execute(Arguments arguments)
{
if (arguments.containsNulls())
return null;
Arguments args = aggregateFunction.newArguments(arguments.getProtocolVersion());
AggregateFunction.Aggregate aggregate = aggregateFunction.newAggregate();
inputType.forEach(value, element -> aggregate.addInput(version, Collections.singletonList(element)));
return aggregate.compute(version);
inputType.forEach(arguments.get(0), element -> {
args.set(0, element);
aggregate.addInput(args);
});
return aggregate.compute(arguments.getProtocolVersion());
}
}
}

View File

@ -29,7 +29,6 @@ import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.FunctionExecutionException;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.JsonUtils;
import static java.lang.String.format;
@ -54,20 +53,23 @@ public class FromJsonFct extends NativeScalarFunction
super(name.name, returnType, UTF8Type.instance);
}
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public ByteBuffer execute(Arguments arguments)
{
assert parameters.size() == 1 : format("Unexpectedly got %d arguments for %s()", parameters.size(), name.name);
ByteBuffer argument = parameters.get(0);
if (argument == null)
assert arguments.size() == 1 : format("Unexpectedly got %d arguments for %s()", arguments.size(), name.name);
if (arguments.containsNulls())
return null;
String jsonArg = UTF8Type.instance.getSerializer().deserialize(argument);
String jsonArg = arguments.get(0);
try
{
Object object = JsonUtils.JSON_OBJECT_MAPPER.readValue(jsonArg, Object.class);
if (object == null)
return null;
return returnType.fromJSONObject(object).bindAndGet(QueryOptions.forProtocolVersion(protocolVersion));
return returnType.fromJSONObject(object)
.bindAndGet(QueryOptions.forProtocolVersion(arguments.getProtocolVersion()));
}
catch (IOException exc)
{

View File

@ -24,6 +24,7 @@ import java.util.Optional;
import org.apache.cassandra.cql3.AssignmentTestable;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.schema.Difference;
import org.apache.cassandra.transport.ProtocolVersion;
import org.github.jamm.Unmetered;
@Unmetered
@ -72,6 +73,14 @@ public interface Function extends AssignmentTestable
*/
public String columnName(List<String> columnNames);
/**
* Creates some new input arguments for this function.
*
* @param version the protocol version
* @return some new input arguments for this function
*/
Arguments newArguments(ProtocolVersion version);
public default Optional<Difference> compare(Function other)
{
throw new UnsupportedOperationException();

View File

@ -0,0 +1,168 @@
/*
* 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.cql3.functions;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* Default {@link Arguments} implementation.
*/
public final class FunctionArguments implements Arguments
{
/**
* An empty {@link FunctionArguments} for the current protocol.
*/
private static final FunctionArguments EMPTY = new FunctionArguments(ProtocolVersion.CURRENT);
/**
* The deserializer used to deserialize the columns.
*/
private final ArgumentDeserializer[] deserializers;
/**
* The protocol version.
*/
private final ProtocolVersion version;
/**
* The deserialized arguments.
*/
private final Object[] arguments;
/**
* Creates a new {@link FunctionArguments} for the specified types.
*
* @param version the protocol version
* @param argTypes the argument types
* @return a new {@link FunctionArguments} for the specified types.
*/
public static FunctionArguments newInstanceForUdf(ProtocolVersion version, List<UDFDataType> argTypes)
{
int size = argTypes.size();
if (size == 0)
return emptyInstance(version);
ArgumentDeserializer[] deserializers = new ArgumentDeserializer[size];
for (int i = 0; i < size; i++)
deserializers[i] = argTypes.get(i).getArgumentDeserializer();
return new FunctionArguments(version, deserializers);
}
@Override
public ProtocolVersion getProtocolVersion()
{
return version;
}
/**
* Creates a new {@link FunctionArguments} that does not deserialize the arguments.
*
* @param version the protocol version
* @param numberOfArguments the number of argument
* @return a new {@link FunctionArguments} for the specified types.
*/
public static FunctionArguments newNoopInstance(ProtocolVersion version, int numberOfArguments)
{
ArgumentDeserializer[] deserializers = new ArgumentDeserializer[numberOfArguments];
Arrays.fill(deserializers, ArgumentDeserializer.NOOP_DESERIALIZER);
return new FunctionArguments(version, deserializers);
}
/**
* Creates an empty {@link FunctionArguments}.
*
* @param version the protocol version
* @return an empty {@link FunctionArguments}
*/
public static FunctionArguments emptyInstance(ProtocolVersion version)
{
if (version == ProtocolVersion.CURRENT)
return EMPTY;
return new FunctionArguments(version);
}
/**
* Creates a new {@link FunctionArguments} for a native function.
* <p>Native functions can use different {@link ArgumentDeserializer} to avoid instanciating primitive wrappers.</p>
*
* @param version the protocol version
* @param argTypes the argument types
* @return a new {@link FunctionArguments} for the specified types.
*/
public static FunctionArguments newInstanceForNativeFunction(ProtocolVersion version, List<AbstractType<?>> argTypes)
{
int size = argTypes.size();
if (size == 0)
return emptyInstance(version);
ArgumentDeserializer[] deserializers = new ArgumentDeserializer[size];
for (int i = 0; i < size; i++)
deserializers[i] = argTypes.get(i).getArgumentDeserializer();
return new FunctionArguments(version, deserializers);
}
public FunctionArguments(ProtocolVersion version, ArgumentDeserializer... deserializers)
{
this.version = version;
this.deserializers = deserializers;
this.arguments = new Object[deserializers.length];
}
public void set(int i, ByteBuffer buffer)
{
arguments[i] = deserializers[i].deserialize(version, buffer);
}
@Override
public boolean containsNulls()
{
for (int i = 0; i < arguments.length; i++)
{
if (arguments[i] == null)
return true;
}
return false;
}
@SuppressWarnings("unchecked")
@Override
public <T> T get(int i)
{
return (T) arguments[i];
}
@Override
public int size()
{
return arguments.length;
}
}

View File

@ -29,7 +29,6 @@ import org.apache.cassandra.cql3.statements.RequestValidations;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.ByteBufferUtil;
import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
@ -57,31 +56,35 @@ public class FunctionCall extends Term.NonTerminal
t.collectMarkerSpecification(boundNames);
}
@Override
public Term.Terminal bind(QueryOptions options) throws InvalidRequestException
{
return makeTerminal(fun, bindAndGet(options), options.getProtocolVersion());
return makeTerminal(fun, bindAndGet(options));
}
@Override
public ByteBuffer bindAndGet(QueryOptions options) throws InvalidRequestException
{
List<ByteBuffer> buffers = new ArrayList<>(terms.size());
for (Term t : terms)
Arguments arguments = fun.newArguments(options.getProtocolVersion());
for (int i = 0, m = terms.size(); i < m; i++)
{
ByteBuffer functionArg = t.bindAndGet(options);
RequestValidations.checkBindValueSet(functionArg, "Invalid unset value for argument in call to function %s", fun.name().name);
buffers.add(functionArg);
Term t = terms.get(i);
ByteBuffer argument = t.bindAndGet(options);
RequestValidations.checkBindValueSet(argument, "Invalid unset value for argument in call to function %s", fun.name().name);
arguments.set(i, argument);
}
return executeInternal(options.getProtocolVersion(), fun, buffers);
return executeInternal(fun, arguments);
}
private static ByteBuffer executeInternal(ProtocolVersion protocolVersion, ScalarFunction fun, List<ByteBuffer> params) throws InvalidRequestException
private static ByteBuffer executeInternal(ScalarFunction fun, Arguments arguments) throws InvalidRequestException
{
ByteBuffer result = fun.execute(protocolVersion, params);
ByteBuffer result = fun.execute(arguments);
try
{
// Check the method didn't lied on it's declared return type
// Check the method didn't lie on it's declared return type
if (result != null)
fun.returnType().validate(result);
return result;
}
catch (MarshalException e)
@ -101,7 +104,7 @@ public class FunctionCall extends Term.NonTerminal
return false;
}
private static Term.Terminal makeTerminal(Function fun, ByteBuffer result, ProtocolVersion version) throws InvalidRequestException
private static Term.Terminal makeTerminal(Function fun, ByteBuffer result) throws InvalidRequestException
{
if (result == null)
return null;

View File

@ -37,19 +37,16 @@ import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.ByteStreams;
import com.google.common.reflect.TypeToken;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.concurrent.NamedThreadFactory;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.security.SecurityThreadGroup;
import org.apache.cassandra.security.ThreadAwareSecurityManager;
@ -68,8 +65,6 @@ public final class JavaBasedUDFunction extends UDFunction
{
private static final String BASE_PACKAGE = "org.apache.cassandra.cql3.udf.gen";
private static final Pattern JAVA_LANG_PREFIX = Pattern.compile("\\bjava\\.lang\\.");
private static final Logger logger = LoggerFactory.getLogger(JavaBasedUDFunction.class);
private static final AtomicInteger classSequence = new AtomicInteger();
@ -192,13 +187,7 @@ public final class JavaBasedUDFunction extends UDFunction
JavaBasedUDFunction(FunctionName name, List<ColumnIdentifier> argNames, List<AbstractType<?>> argTypes,
AbstractType<?> returnType, boolean calledOnNullInput, String body)
{
super(name, argNames, argTypes, UDHelper.driverTypes(argTypes),
returnType, UDHelper.driverType(returnType), calledOnNullInput, "java", body);
// javaParamTypes is just the Java representation for argTypes resp. argDataTypes
TypeToken<?>[] javaParamTypes = UDHelper.typeTokens(argCodecs, calledOnNullInput);
// javaReturnType is just the Java representation for returnType resp. returnTypeCodec
TypeToken<?> javaReturnType = returnCodec.getJavaType();
super(name, argNames, argTypes, returnType, calledOnNullInput, "java", body);
// put each UDF in a separate package to prevent cross-UDF code access
String pkgName = BASE_PACKAGE + '.' + generateClassName(name, 'p');
@ -228,16 +217,16 @@ public final class JavaBasedUDFunction extends UDFunction
s = patternJavaDriver.matcher(body).replaceAll("org.apache.cassandra.cql3.functions.types.");
break;
case "arguments":
s = generateArguments(javaParamTypes, argNames, false);
s = generateArguments(argumentTypes, argNames, false);
break;
case "arguments_aggregate":
s = generateArguments(javaParamTypes, argNames, true);
s = generateArguments(argumentTypes, argNames, true);
break;
case "argument_list":
s = generateArgumentList(javaParamTypes, argNames);
s = generateArgumentList(argumentTypes, argNames);
break;
case "return_type":
s = javaSourceName(javaReturnType);
s = resultType.getJavaTypeName();
break;
case "execute_internal_name":
s = executeInternalName;
@ -338,9 +327,9 @@ public final class JavaBasedUDFunction extends UDFunction
if (nonSyntheticMethodCount != 3 || cls.getDeclaredConstructors().length != 1)
throw new InvalidRequestException("Check your source to not define additional Java methods or constructors");
MethodType methodType = MethodType.methodType(void.class)
.appendParameterTypes(TypeCodec.class, TypeCodec[].class, UDFContext.class);
.appendParameterTypes(UDFDataType.class, UDFContext.class);
MethodHandle ctor = MethodHandles.lookup().findConstructor(cls, methodType);
this.javaUDF = (JavaUDF) ctor.invokeWithArguments(returnCodec, argCodecs, udfContext);
this.javaUDF = (JavaUDF) ctor.invokeWithArguments(resultType, udfContext);
}
finally
{
@ -364,19 +353,22 @@ public final class JavaBasedUDFunction extends UDFunction
}
}
@Override
protected ExecutorService executor()
{
return executor;
}
protected ByteBuffer executeUserDefined(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeUserDefined(Arguments arguments)
{
return javaUDF.executeImpl(protocolVersion, params);
return javaUDF.executeImpl(arguments);
}
protected Object executeAggregateUserDefined(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateUserDefined(Object state, Arguments arguments)
{
return javaUDF.executeAggregateImpl(protocolVersion, firstParam, params);
return javaUDF.executeAggregateImpl(state, arguments);
}
private static int countNewlines(StringBuilder javaSource)
@ -409,22 +401,15 @@ public final class JavaBasedUDFunction extends UDFunction
return sb.toString();
}
@VisibleForTesting
public static String javaSourceName(TypeToken<?> type)
{
String n = type.toString();
return JAVA_LANG_PREFIX.matcher(n).replaceAll("");
}
private static String generateArgumentList(TypeToken<?>[] paramTypes, List<ColumnIdentifier> argNames)
private static String generateArgumentList(List<UDFDataType> argTypes, List<ColumnIdentifier> argNames)
{
// initial builder size can just be a guess (prevent temp object allocations)
StringBuilder code = new StringBuilder(32 * paramTypes.length);
for (int i = 0; i < paramTypes.length; i++)
StringBuilder code = new StringBuilder(32 * argTypes.size());
for (int i = 0; i < argTypes.size(); i++)
{
if (i > 0)
code.append(", ");
code.append(javaSourceName(paramTypes[i]))
code.append(argTypes.get(i).getJavaTypeName())
.append(' ')
.append(argNames.get(i));
}
@ -450,36 +435,56 @@ public final class JavaBasedUDFunction extends UDFunction
* {@code firstParam, (double) super.compose_double(protocolVersion, 1, params.get(1))}
* </p>
*/
private static String generateArguments(TypeToken<?>[] paramTypes, List<ColumnIdentifier> argNames, boolean forAggregate)
private static String generateArguments(List<UDFDataType> argTypes, List<ColumnIdentifier> argNames, boolean forAggregate)
{
StringBuilder code = new StringBuilder(64 * paramTypes.length);
for (int i = 0; i < paramTypes.length; i++)
int size = argTypes.size();
StringBuilder code = new StringBuilder(64 * size);
for (int i = 0; i < size; i++)
{
UDFDataType argType = argTypes.get(i);
if (i > 0)
// add separator, if not the first argument
code.append(",\n");
// add comment only if trace is enabled
if (logger.isTraceEnabled())
code.append(" /* parameter '").append(argNames.get(i)).append("' */\n");
code.append(" /* argument '").append(argNames.get(i)).append("' */\n");
code.append(" ");
// cast to Java type
code.append(" (").append(javaSourceName(paramTypes[i])).append(") ");
code.append('(').append(argType.getJavaTypeName()).append(") ");
if (forAggregate && i == 0)
{
// special case for aggregations where the state variable (1st arg to state + final function and
// return value from state function) is not re-serialized
code.append("firstParam");
code.append("state");
}
else
// generate object representation of input parameter (call UDFunction.compose)
code.append(composeMethod(paramTypes[i])).append("(protocolVersion, ").append(i).append(", params.get(").append(forAggregate ? i - 1 : i).append("))");
{
// generate object representation of input parameter
code.append("arguments.");
appendGetMethodName(code, argType).append('(').append(forAggregate ? i - 1 : i).append(')');
}
}
return code.toString();
}
private static String composeMethod(TypeToken<?> type)
/**
* Appends the get method name for the specified type to avoid boxing.
* @param code the builder to append to
* @param type the data type
* @return the builder
*/
private static StringBuilder appendGetMethodName(StringBuilder code, UDFDataType type)
{
return (type.isPrimitive()) ? ("super.compose_" + type.getRawType().getName()) : "super.compose";
code.append("get");
if (!type.isPrimitive())
return code;
return code.append("As").append(StringUtils.capitalize(type.getJavaTypeName()));
}
// Java source UDFs are a very simple compilation task, which allows us to let one class implement
@ -696,4 +701,5 @@ public final class JavaBasedUDFunction extends UDFunction
{
return ThreadAwareSecurityManager.noPermissions;
}
}}
}
}

View File

@ -19,9 +19,7 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.transport.ProtocolVersion;
/**
@ -32,78 +30,62 @@ import org.apache.cassandra.transport.ProtocolVersion;
*/
public abstract class JavaUDF
{
private final TypeCodec<Object> returnCodec;
private final TypeCodec<Object>[] argCodecs;
private final UDFDataType returnType;
protected final UDFContext udfContext;
protected JavaUDF(TypeCodec<Object> returnCodec, TypeCodec<Object>[] argCodecs, UDFContext udfContext)
protected JavaUDF(UDFDataType returnType, UDFContext udfContext)
{
this.returnCodec = returnCodec;
this.argCodecs = argCodecs;
this.returnType = returnType;
this.udfContext = udfContext;
}
protected abstract ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params);
protected abstract ByteBuffer executeImpl(Arguments arguments);
protected abstract Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params);
protected abstract Object executeAggregateImpl(Object state, Arguments arguments);
protected Object compose(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
//~///////////////////////////////////////////////////////////////////////////////////////////
// The decompose method is overloaded to avoid boxing of the result if it is a primitive type.
//~///////////////////////////////////////////////////////////////////////////////////////////
// do not remove - used by generated Java UDFs
protected final ByteBuffer decompose(ProtocolVersion protocolVersion, Object value)
{
return UDFunction.compose(argCodecs, protocolVersion, argIndex, value);
}
protected ByteBuffer decompose(ProtocolVersion protocolVersion, Object value)
{
return UDFunction.decompose(returnCodec, protocolVersion, value);
return returnType.decompose(protocolVersion, value);
}
// do not remove - used by generated Java UDFs
protected float compose_float(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
protected final ByteBuffer decompose(ProtocolVersion protocolVersion, byte value)
{
assert value != null && value.remaining() > 0;
return (float) UDHelper.deserialize(TypeCodec.cfloat(), protocolVersion, value);
return returnType.decompose(protocolVersion, value);
}
// do not remove - used by generated Java UDFs
protected double compose_double(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
protected final ByteBuffer decompose(ProtocolVersion protocolVersion, short value)
{
assert value != null && value.remaining() > 0;
return (double) UDHelper.deserialize(TypeCodec.cdouble(), protocolVersion, value);
return returnType.decompose(protocolVersion, value);
}
// do not remove - used by generated Java UDFs
protected byte compose_byte(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
protected final ByteBuffer decompose(ProtocolVersion protocolVersion, int value)
{
assert value != null && value.remaining() > 0;
return (byte) UDHelper.deserialize(TypeCodec.tinyInt(), protocolVersion, value);
return returnType.decompose(protocolVersion, value);
}
// do not remove - used by generated Java UDFs
protected short compose_short(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
protected final ByteBuffer decompose(ProtocolVersion protocolVersion, long value)
{
assert value != null && value.remaining() > 0;
return (short) UDHelper.deserialize(TypeCodec.smallInt(), protocolVersion, value);
return returnType.decompose(protocolVersion, value);
}
// do not remove - used by generated Java UDFs
protected int compose_int(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
protected final ByteBuffer decompose(ProtocolVersion protocolVersion, float value)
{
assert value != null && value.remaining() > 0;
return (int) UDHelper.deserialize(TypeCodec.cint(), protocolVersion, value);
return returnType.decompose(protocolVersion, value);
}
// do not remove - used by generated Java UDFs
protected long compose_long(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
protected final ByteBuffer decompose(ProtocolVersion protocolVersion, double value)
{
assert value != null && value.remaining() > 0;
return (long) UDHelper.deserialize(TypeCodec.bigint(), protocolVersion, value);
}
// do not remove - used by generated Java UDFs
protected boolean compose_boolean(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
{
assert value != null && value.remaining() > 0;
return (boolean) UDHelper.deserialize(TypeCodec.cboolean(), protocolVersion, value);
return returnType.decompose(protocolVersion, value);
}
}

View File

@ -21,10 +21,11 @@ package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import com.google.common.collect.ImmutableList;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.exceptions.InvalidRequestException;
public final class MathFcts
{
@ -47,83 +48,49 @@ public final class MathFcts
log10Fct(t),
roundFct(t)))
.flatMap(Collection::stream)
.forEach(f -> functions.add(f));
.forEach(functions::add);
}
public static NativeFunction absFct(final NumberType<?> type)
{
return new NativeScalarFunction("abs", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.abs(bb);
}
};
return mathFct("abs", type, NumberType::abs);
}
public static NativeFunction expFct(final NumberType<?> type)
{
return new NativeScalarFunction("exp", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.exp(bb);
}
};
return mathFct("exp", type, NumberType::exp);
}
public static NativeFunction logFct(final NumberType<?> type)
{
return new NativeScalarFunction("log", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.log(bb);
}
};
return mathFct("log", type, NumberType::log);
}
public static NativeFunction log10Fct(final NumberType<?> type)
{
return new NativeScalarFunction("log10", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.log10(bb);
}
};
return mathFct("log10", type, NumberType::log10);
}
public static NativeFunction roundFct(final NumberType<?> type)
{
return new NativeScalarFunction("round", type, type)
return mathFct("round", type, NumberType::round);
}
private static NativeFunction mathFct(String name,
NumberType<?> type,
BiFunction<NumberType<?>, Number, ByteBuffer> f)
{
return new NativeScalarFunction(name, type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.round(bb);
Number number = arguments.get(0);
if (number == null)
return null;
return f.apply(type, number);
}
};
}

View File

@ -22,6 +22,7 @@ import java.util.Arrays;
import javax.annotation.Nullable;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* Base class for our native/hardcoded functions.
@ -59,4 +60,11 @@ public abstract class NativeFunction extends AbstractFunction
{
return null;
}
@Override
public Arguments newArguments(ProtocolVersion version)
{
return FunctionArguments.newInstanceForNativeFunction(version, argTypes);
}
}

View File

@ -20,10 +20,10 @@ package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.Duration;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.OperationExecutionException;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* Operation functions (Mathematics).
@ -31,87 +31,59 @@ import org.apache.cassandra.transport.ProtocolVersion;
*/
public final class OperationFcts
{
private static enum OPERATION
private enum OPERATION
{
ADDITION('+', "_add")
{
protected ByteBuffer executeOnNumerics(NumberType<?> resultType,
NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right)
protected ByteBuffer executeOnNumerics(NumberType<?> resultType, Number left, Number right)
{
return resultType.add(leftType, left, rightType, right);
return resultType.add(left, right);
}
@Override
protected ByteBuffer executeOnTemporals(TemporalType<?> type,
ByteBuffer temporal,
ByteBuffer duration)
protected ByteBuffer executeOnTemporals(TemporalType<?> type, Number temporal, Duration duration)
{
return type.addDuration(temporal, duration);
}
@Override
protected ByteBuffer excuteOnStrings(StringType resultType,
StringType leftType,
ByteBuffer left,
StringType rightType,
ByteBuffer right)
protected ByteBuffer excuteOnStrings(StringType resultType, String left, String right)
{
return resultType.concat(leftType, left, rightType, right);
return resultType.concat(left, right);
}
},
SUBSTRACTION('-', "_substract")
{
protected ByteBuffer executeOnNumerics(NumberType<?> resultType,
NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right)
protected ByteBuffer executeOnNumerics(NumberType<?> resultType, Number left, Number right)
{
return resultType.substract(leftType, left, rightType, right);
return resultType.substract(left, right);
}
@Override
protected ByteBuffer executeOnTemporals(TemporalType<?> type,
ByteBuffer temporal,
ByteBuffer duration)
protected ByteBuffer executeOnTemporals(TemporalType<?> type, Number temporal, Duration duration)
{
return type.substractDuration(temporal, duration);
}
},
MULTIPLICATION('*', "_multiply")
{
protected ByteBuffer executeOnNumerics(NumberType<?> resultType,
NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right)
protected ByteBuffer executeOnNumerics(NumberType<?> resultType, Number left, Number right)
{
return resultType.multiply(leftType, left, rightType, right);
return resultType.multiply(left, right);
}
},
DIVISION('/', "_divide")
{
protected ByteBuffer executeOnNumerics(NumberType<?> resultType,
NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right)
protected ByteBuffer executeOnNumerics(NumberType<?> resultType, Number left, Number right)
{
return resultType.divide(leftType, left, rightType, right);
return resultType.divide(left, right);
}
},
MODULO('%', "_modulo")
{
protected ByteBuffer executeOnNumerics(NumberType<?> resultType,
NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right)
protected ByteBuffer executeOnNumerics(NumberType<?> resultType, Number left, Number right)
{
return resultType.mod(leftType, left, rightType, right);
return resultType.mod(left, right);
}
};
@ -135,17 +107,11 @@ public final class OperationFcts
* Executes the operation between the specified numeric operand.
*
* @param resultType the result ype of the operation
* @param leftType the type of the left operand
* @param left the left operand
* @param rightType the type of the right operand
* @param right the right operand
* @return the operation result
*/
protected abstract ByteBuffer executeOnNumerics(NumberType<?> resultType,
NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right);
protected abstract ByteBuffer executeOnNumerics(NumberType<?> resultType, Number left, Number right);
/**
* Executes the operation on the specified temporal operand.
@ -155,9 +121,7 @@ public final class OperationFcts
* @param duration the duration
* @return the operation result
*/
protected ByteBuffer executeOnTemporals(TemporalType<?> type,
ByteBuffer temporal,
ByteBuffer duration)
protected ByteBuffer executeOnTemporals(TemporalType<?> type, Number temporal, Duration duration)
{
throw new UnsupportedOperationException();
}
@ -166,17 +130,11 @@ public final class OperationFcts
* Executes the operation between the specified string operand.
*
* @param resultType the result type of the operation
* @param leftType the type of the left operand
* @param left the left operand
* @param rightType the type of the right operand
* @param right the right operand
* @return the operation result
*/
protected ByteBuffer excuteOnStrings(StringType resultType,
StringType leftType,
ByteBuffer left,
StringType rightType,
ByteBuffer right)
protected ByteBuffer excuteOnStrings(StringType resultType, String left, String right)
{
throw new UnsupportedOperationException();
}
@ -198,7 +156,8 @@ public final class OperationFcts
/**
* Returns the {@code OPERATOR} with the specified symbol.
* @param functionName the function name
*
* @param symbol a symbol
* @return the {@code OPERATOR} with the specified symbol
*/
public static OPERATION fromSymbol(char symbol)
@ -395,16 +354,15 @@ public final class OperationFcts
return String.format("%s %s %s", columnNames.get(0), getOperator(), columnNames.get(1));
}
public final ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public final ByteBuffer execute(Arguments arguments)
{
ByteBuffer left = parameters.get(0);
ByteBuffer right = parameters.get(1);
if (left == null || !left.hasRemaining() || right == null || !right.hasRemaining())
if (arguments.containsNulls())
return null;
try
{
return doExecute(left, operation, right);
return doExecute(arguments.get(0), operation, arguments.get(1));
}
catch (Exception e)
{
@ -412,13 +370,13 @@ public final class OperationFcts
}
}
protected abstract ByteBuffer doExecute(ByteBuffer left, OPERATION operation, ByteBuffer right);
protected abstract ByteBuffer doExecute(Object left, OPERATION operation, Object right);
/**
* Returns the operator symbol.
* @return the operator symbol
*/
private final char getOperator()
private char getOperator()
{
return operation.symbol;
}
@ -438,13 +396,11 @@ public final class OperationFcts
}
@Override
protected ByteBuffer doExecute(ByteBuffer left, OPERATION operation, ByteBuffer right)
protected ByteBuffer doExecute(Object left, OPERATION operation, Object right)
{
NumberType<?> leftType = (NumberType<?>) argTypes().get(0);
NumberType<?> rightType = (NumberType<?>) argTypes().get(1);
NumberType<?> resultType = (NumberType<?>) returnType();
return operation.executeOnNumerics(resultType, leftType, left, rightType, right);
return operation.executeOnNumerics(resultType, (Number) left, (Number) right);
}
}
@ -459,13 +415,11 @@ public final class OperationFcts
}
@Override
protected ByteBuffer doExecute(ByteBuffer left, OPERATION operation, ByteBuffer right)
protected ByteBuffer doExecute(Object left, OPERATION operation, Object right)
{
StringType leftType = (StringType) argTypes().get(0);
StringType rightType = (StringType) argTypes().get(1);
StringType resultType = (StringType) returnType();
return operation.excuteOnStrings(resultType, leftType, left, rightType, right);
return operation.excuteOnStrings(resultType, (String) left, (String) right);
}
}
@ -474,17 +428,16 @@ public final class OperationFcts
*/
private static class TemporalOperationFunction extends OperationFunction
{
public TemporalOperationFunction(TemporalType<?> type,
OPERATION operation)
public TemporalOperationFunction(TemporalType<?> type, OPERATION operation)
{
super(type, type, operation, DurationType.instance);
}
@Override
protected ByteBuffer doExecute(ByteBuffer left, OPERATION operation, ByteBuffer right)
protected ByteBuffer doExecute(Object left, OPERATION operation, Object right)
{
TemporalType<?> resultType = (TemporalType<?>) returnType();
return operation.executeOnTemporals(resultType, left, right);
return operation.executeOnTemporals(resultType, (Number) left, (Duration) right);
}
}
@ -504,15 +457,15 @@ public final class OperationFcts
return String.format("-%s", columnNames.get(0));
}
public final ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public final ByteBuffer execute(Arguments arguments)
{
ByteBuffer input = parameters.get(0);
if (input == null)
if (arguments.containsNulls())
return null;
NumberType<?> inputType = (NumberType<?>) argTypes().get(0);
return inputType.negate(input);
return inputType.negate(arguments.get(0));
}
}
}

View File

@ -41,5 +41,5 @@ public interface PartialScalarFunction extends ScalarFunction
*
* @return the list of input parameters for the function
*/
public List<ByteBuffer> getPartialParameters();
public List<ByteBuffer> getPartialArguments();
}

View File

@ -18,7 +18,6 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.cql3.CqlBuilder;
@ -64,7 +63,13 @@ final class PartiallyAppliedScalarFunction extends NativeScalarFunction implemen
}
@Override
public List<ByteBuffer> getPartialParameters()
public Arguments newArguments(ProtocolVersion version)
{
return new PartialFunctionArguments(version, function, partialParameters, argTypes.size());
}
@Override
public List<ByteBuffer> getPartialArguments()
{
return partialParameters;
}
@ -81,16 +86,10 @@ final class PartiallyAppliedScalarFunction extends NativeScalarFunction implemen
return argTypes;
}
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters) throws InvalidRequestException
@Override
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
List<ByteBuffer> fullParameters = new ArrayList<>(partialParameters);
int arg = 0;
for (int i = 0; i < fullParameters.size(); i++)
{
if (fullParameters.get(i) == UNRESOLVED)
fullParameters.set(i, parameters.get(arg++));
}
return function.execute(protocolVersion, fullParameters);
return function.execute(arguments);
}
@Override
@ -110,4 +109,70 @@ final class PartiallyAppliedScalarFunction extends NativeScalarFunction implemen
b.append(") -> ").append(returnType);
return b.toString();
}
/**
* The arguments for a {@code PartiallyAppliedScalarFunction}.
*
*/
private static final class PartialFunctionArguments implements Arguments
{
/**
* The decorated arguments.
*/
private final Arguments arguments;
/**
* The position of the unresolved arguments.
*/
private final int[] mapping;
public PartialFunctionArguments(ProtocolVersion version, ScalarFunction function, List<ByteBuffer> partialArguments, int unresolvedCount)
{
arguments = function.newArguments(version);
mapping = new int[unresolvedCount];
int mappingIndex = 0;
for (int i = 0, m = partialArguments.size(); i < m; i++)
{
ByteBuffer argument = partialArguments.get(i);
if (argument != Function.UNRESOLVED)
{
arguments.set(i, argument);
}
else
{
mapping[mappingIndex++] = i;
}
}
}
@Override
public ProtocolVersion getProtocolVersion()
{
return arguments.getProtocolVersion();
}
@Override
public void set(int i, ByteBuffer buffer)
{
arguments.set(mapping[i], buffer);
}
@Override
public boolean containsNulls()
{
return arguments.containsNulls();
}
@Override
public <T> T get(int i)
{
return arguments.get(i);
}
@Override
public int size()
{
return arguments.size();
}
}
}

View File

@ -40,20 +40,20 @@ class PreComputedScalarFunction extends NativeScalarFunction implements PartialS
private final ProtocolVersion valueVersion;
private final ScalarFunction function;
private final List<ByteBuffer> parameters;
private final List<ByteBuffer> arguments;
PreComputedScalarFunction(AbstractType<?> returnType,
ByteBuffer value,
ProtocolVersion valueVersion,
ScalarFunction function,
List<ByteBuffer> parameters)
List<ByteBuffer> arguments)
{
// Note that we never register those function, there are just used internally, so the name doesn't matter much
super("__constant__", returnType);
this.value = value;
this.valueVersion = valueVersion;
this.function = function;
this.parameters = parameters;
this.arguments = arguments;
}
@Override
@ -63,17 +63,24 @@ class PreComputedScalarFunction extends NativeScalarFunction implements PartialS
}
@Override
public List<ByteBuffer> getPartialParameters()
public List<ByteBuffer> getPartialArguments()
{
return parameters;
return arguments;
}
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> nothing) throws InvalidRequestException
@Override
public ByteBuffer execute(Arguments nothing) throws InvalidRequestException
{
if (protocolVersion == valueVersion)
if (nothing.getProtocolVersion() == valueVersion)
return value;
return function.execute(protocolVersion, parameters);
Arguments args = function.newArguments(nothing.getProtocolVersion());
for (int i = 0, m = arguments.size() ; i < m; i++)
{
args.set(i, arguments.get(i));;
}
return function.execute(args);
}
public ScalarFunction partialApplication(ProtocolVersion protocolVersion, List<ByteBuffer> nothing) throws InvalidRequestException

View File

@ -43,18 +43,17 @@ public interface ScalarFunction extends Function
}
/**
* Applies this function to the specified parameter.
* Applies this function to the specified arguments.
*
* @param protocolVersion protocol version used for parameters and return value
* @param parameters the input parameters
* @return the result of applying this function to the parameter
* @throws InvalidRequestException if this function cannot not be applied to the parameter
* @param arguments the input arguments for the function
* @return the result of applying this function to the arguments
* @throws InvalidRequestException if this function cannot not be applied to the arguments
*/
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters) throws InvalidRequestException;
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException;
/**
* Does a partial application of the function. That is, given only some of the parameters of the function provided,
* return a new function that only expect the parameters not provided.
* Does a partial application of the function. That is, given only some of the arguments of the function provided,
* return a new function that only expect the arguments not provided.
* <p>
* To take an example, if you consider the function
* <pre>
@ -66,21 +65,21 @@ public interface ScalarFunction extends Function
* </pre>
* and such that for any value of {@code b} and {@code d}, {@code bar(b, d) == foo(3, b, 'bar', d)}.
*
* @param protocolVersion protocol version used for parameters
* @param partialParameters a list of input parameters for the function where some parameters can be {@link #UNRESOLVED}.
* The input <b>must</b> be of size {@code this.argsType().size()}. For convenience, it is
* allowed both to pass a list with all parameters being {@link #UNRESOLVED} (the function is
* then returned directly) and with none of them unresolved (in which case, if the function is pure,
* it is computed and a dummy no-arg function returning the result is returned).
* @return a function corresponding to the partial application of this function to the parameters of
* {@code partialParameters} that are not {@link #UNRESOLVED}.
* @param protocolVersion protocol version used for arguments
* @param partialArguments a list of input arguments for the function where some arguments can be {@link #UNRESOLVED}.
* The input <b>must</b> be of size {@code this.argsType().size()}. For convenience, it is
* allowed both to pass a list with all arguments being {@link #UNRESOLVED} (the function is
* then returned directly) and with none of them unresolved (in which case, if the function is pure,
* it is computed and a dummy no-arg function returning the result is returned).
* @return a function corresponding to the partial application of this function to the arguments of
* {@code partialArguments} that are not {@link #UNRESOLVED}.
*/
public default ScalarFunction partialApplication(ProtocolVersion protocolVersion, List<ByteBuffer> partialParameters)
default ScalarFunction partialApplication(ProtocolVersion protocolVersion, List<ByteBuffer> partialArguments)
{
int unresolvedCount = 0;
for (ByteBuffer parameter : partialParameters)
for (ByteBuffer argument : partialArguments)
{
if (parameter == UNRESOLVED)
if (argument == UNRESOLVED)
++unresolvedCount;
}
@ -88,12 +87,20 @@ public interface ScalarFunction extends Function
return this;
if (isPure() && unresolvedCount == 0)
return new PreComputedScalarFunction(returnType(),
execute(protocolVersion, partialParameters),
protocolVersion,
this,
partialParameters);
{
Arguments arguments = newArguments(protocolVersion);
for (int i = 0, m = partialArguments.size(); i < m; i++)
{
arguments.set(i, partialArguments.get(i));
}
return new PartiallyAppliedScalarFunction(this, partialParameters, unresolvedCount);
return new PreComputedScalarFunction(returnType(),
execute(arguments),
protocolVersion,
this,
partialArguments);
}
return new PartiallyAppliedScalarFunction(this, partialArguments, unresolvedCount);
}
}

View File

@ -26,10 +26,8 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.cql3.Duration;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.utils.UUIDGen;
import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
@ -73,7 +71,7 @@ public abstract class TimeFcts
}
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
public ByteBuffer execute(Arguments arguments)
{
return type.now();
}
@ -92,9 +90,33 @@ public abstract class TimeFcts
}
}
public static abstract class TemporalConversionFunction extends NativeScalarFunction
{
protected TemporalConversionFunction(String name, AbstractType<?> returnType, AbstractType<?>... argsType)
{
super(name, returnType, argsType);
}
public ByteBuffer execute(Arguments arguments)
{
beforeExecution();
if (arguments.containsNulls())
return null;
return convertArgument(arguments.getAsLong(0));
}
protected void beforeExecution()
{
}
protected abstract ByteBuffer convertArgument(long timeInMillis);
}
public static final NativeFunction minTimeuuidFct = new MinTimeuuidFunction(false);
private static final class MinTimeuuidFunction extends NativeScalarFunction
private static final class MinTimeuuidFunction extends TemporalConversionFunction
{
public MinTimeuuidFunction(boolean legacy)
{
@ -102,13 +124,9 @@ public abstract class TimeFcts
}
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
protected ByteBuffer convertArgument(long timeInMillis)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return TimeUUID.minAtUnixMillis(TimestampType.instance.compose(bb).getTime()).toBytes();
return TimeUUID.minAtUnixMillis(timeInMillis).toBytes();
}
@Override
@ -120,7 +138,7 @@ public abstract class TimeFcts
public static final NativeFunction maxTimeuuidFct = new MaxTimeuuidFunction(false);
private static final class MaxTimeuuidFunction extends NativeScalarFunction
private static final class MaxTimeuuidFunction extends TemporalConversionFunction
{
public MaxTimeuuidFunction(boolean legacy)
{
@ -128,13 +146,9 @@ public abstract class TimeFcts
}
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
protected ByteBuffer convertArgument(long timeInMillis)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return TimeUUID.maxAtUnixMillis(TimestampType.instance.compose(bb).getTime()).toBytes();
return TimeUUID.maxAtUnixMillis(timeInMillis).toBytes();
}
@Override
@ -155,7 +169,7 @@ public abstract class TimeFcts
return new ToDateFunction(type, false);
}
private static class ToDateFunction extends NativeScalarFunction
private static class ToDateFunction extends TemporalConversionFunction
{
private final TemporalType<?> type;
@ -166,14 +180,9 @@ public abstract class TimeFcts
}
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
protected ByteBuffer convertArgument(long timeInMillis)
{
ByteBuffer bb = parameters.get(0);
if (bb == null || !bb.hasRemaining())
return null;
long millis = type.toTimeInMillis(bb);
return SimpleDateType.instance.fromTimeInMillis(millis);
return SimpleDateType.instance.fromTimeInMillis(timeInMillis);
}
@Override
@ -200,7 +209,7 @@ public abstract class TimeFcts
return new ToTimestampFunction(type, false);
}
private static class ToTimestampFunction extends NativeScalarFunction
private static class ToTimestampFunction extends TemporalConversionFunction
{
private final TemporalType<?> type;
@ -211,14 +220,9 @@ public abstract class TimeFcts
}
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
protected ByteBuffer convertArgument(long timeInMillis)
{
ByteBuffer bb = parameters.get(0);
if (bb == null || !bb.hasRemaining())
return null;
long millis = type.toTimeInMillis(bb);
return TimestampType.instance.fromTimeInMillis(millis);
return TimestampType.instance.fromTimeInMillis(timeInMillis);
}
@Override
@ -245,7 +249,7 @@ public abstract class TimeFcts
return new ToUnixTimestampFunction(type, false);
}
private static class ToUnixTimestampFunction extends NativeScalarFunction
private static class ToUnixTimestampFunction extends TemporalConversionFunction
{
private final TemporalType<?> type;
@ -256,13 +260,9 @@ public abstract class TimeFcts
}
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
protected ByteBuffer convertArgument(long timeInMillis)
{
ByteBuffer bb = parameters.get(0);
if (bb == null || !bb.hasRemaining())
return null;
return ByteBufferUtil.bytes(type.toTimeInMillis(bb));
return ByteBufferUtil.bytes(timeInMillis);
}
@Override
@ -301,25 +301,17 @@ public abstract class TimeFcts
&& (partialParameters.size() == 2 || partialParameters.get(2) != UNRESOLVED);
}
public final ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public final ByteBuffer execute(Arguments arguments)
{
ByteBuffer timeBuffer = parameters.get(0);
ByteBuffer durationBuffer = parameters.get(1);
Long startingTime = getStartingTime(parameters);
if (timeBuffer == null || durationBuffer == null || startingTime == null)
if (arguments.containsNulls())
return null;
Long time = toTimeInMillis(timeBuffer);
Duration duration = DurationType.instance.compose(durationBuffer);
if (time == null || duration == null)
return null;
long time = arguments.getAsLong(0);
Duration duration = arguments.get(1);
long startingTime = getStartingTime(arguments);
validateDuration(duration);
long floor = Duration.floorTimestamp(time, duration, startingTime);
return fromTimeInMillis(floor);
@ -328,20 +320,13 @@ public abstract class TimeFcts
/**
* Returns the time to use as the starting time.
*
* @param parameters the function parameters
* @param arguments the function arguments
* @return the time to use as the starting time
*/
private Long getStartingTime(List<ByteBuffer> parameters)
private long getStartingTime(Arguments arguments)
{
if (parameters.size() == 3)
{
ByteBuffer startingTimeBuffer = parameters.get(2);
if (startingTimeBuffer == null)
return null;
return toStartingTimeInMillis(startingTimeBuffer);
}
if (arguments.size() == 3)
return arguments.getAsLong(2);
return ZERO;
}
@ -363,22 +348,6 @@ public abstract class TimeFcts
* @return the serialized time
*/
protected abstract ByteBuffer fromTimeInMillis(long timeInMillis);
/**
* Deserializes the specified input time.
*
* @param bytes the serialized time
* @return the time in milliseconds
*/
protected abstract Long toTimeInMillis(ByteBuffer bytes);
/**
* Deserializes the specified starting time.
*
* @param bytes the serialized starting time
* @return the starting time in milliseconds
*/
protected abstract Long toStartingTimeInMillis(ByteBuffer bytes);
}
/**
@ -411,16 +380,6 @@ public abstract class TimeFcts
{
return TimestampType.instance.fromTimeInMillis(timeInMillis);
}
protected Long toStartingTimeInMillis(ByteBuffer bytes)
{
return TimestampType.instance.toTimeInMillis(bytes);
}
protected Long toTimeInMillis(ByteBuffer bytes)
{
return TimestampType.instance.toTimeInMillis(bytes);
}
}
/**
@ -453,16 +412,6 @@ public abstract class TimeFcts
{
return TimestampType.instance.fromTimeInMillis(timeInMillis);
}
protected Long toStartingTimeInMillis(ByteBuffer bytes)
{
return TimestampType.instance.toTimeInMillis(bytes);
}
protected Long toTimeInMillis(ByteBuffer bytes)
{
return UUIDGen.getAdjustedTimestamp(UUIDGen.getUUID(bytes));
}
}
/**
@ -496,16 +445,6 @@ public abstract class TimeFcts
return SimpleDateType.instance.fromTimeInMillis(timeInMillis);
}
protected Long toStartingTimeInMillis(ByteBuffer bytes)
{
return SimpleDateType.instance.toTimeInMillis(bytes);
}
protected Long toTimeInMillis(ByteBuffer bytes)
{
return SimpleDateType.instance.toTimeInMillis(bytes);
}
@Override
protected void validateDuration(Duration duration)
{
@ -527,19 +466,14 @@ public abstract class TimeFcts
return partialParameters.get(0) == UNRESOLVED && partialParameters.get(1) != UNRESOLVED;
}
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public ByteBuffer execute(Arguments arguments)
{
ByteBuffer timeBuffer = parameters.get(0);
ByteBuffer durationBuffer = parameters.get(1);
if (timeBuffer == null || durationBuffer == null)
if (arguments.containsNulls())
return null;
Long time = TimeType.instance.compose(timeBuffer);
Duration duration = DurationType.instance.compose(durationBuffer);
if (time == null || duration == null)
return null;
long time = arguments.getAsLong(0);
Duration duration = arguments.get(1);
long floor = Duration.floorTime(time, duration);

View File

@ -54,14 +54,26 @@ public class ToJsonFct extends NativeScalarFunction
super(name, UTF8Type.instance, argType);
}
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters) throws InvalidRequestException
@Override
public Arguments newArguments(ProtocolVersion version)
{
assert parameters.size() == 1 : format("Expected 1 argument for %s(), but got %d", name.name, parameters.size());
ByteBuffer parameter = parameters.get(0);
if (parameter == null)
return new FunctionArguments(version, (protocolVersion, buffer) -> {
AbstractType<?> argType = argTypes.get(0);
if (buffer == null || (!buffer.hasRemaining() && argType.isEmptyValueMeaningless()))
return null;
return argTypes.get(0).toJSONString(buffer, protocolVersion);
});
}
@Override
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
if (arguments.containsNulls())
return ByteBufferUtil.bytes("null");
return ByteBufferUtil.bytes(argTypes.get(0).toJSONString(parameter, protocolVersion));
return ByteBufferUtil.bytes(arguments.<String>get(0));
}
public static void addFunctionsTo(NativeFunctions functions)

View File

@ -18,6 +18,7 @@
package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import org.apache.cassandra.cql3.AssignmentTestable;
@ -39,6 +40,14 @@ public class TokenFct extends NativeScalarFunction
this.metadata = metadata;
}
@Override
public Arguments newArguments(ProtocolVersion version)
{
ArgumentDeserializer[] deserializers = new ArgumentDeserializer[argTypes.size()];
Arrays.fill(deserializers, ArgumentDeserializer.NOOP_DESERIALIZER);
return new FunctionArguments(version, deserializers);
}
private static AbstractType<?>[] getKeyTypes(TableMetadata metadata)
{
AbstractType<?>[] types = new AbstractType[metadata.partitionKeyColumns().size()];
@ -48,12 +57,12 @@ public class TokenFct extends NativeScalarFunction
return types;
}
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters) throws InvalidRequestException
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
CBuilder builder = CBuilder.create(metadata.partitionKeyAsClusteringComparator());
for (int i = 0; i < parameters.size(); i++)
for (int i = 0; i < arguments.size(); i++)
{
ByteBuffer bb = parameters.get(i);
ByteBuffer bb = arguments.get(i);
if (bb == null)
return null;
builder.add(bb);

View File

@ -26,7 +26,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.cql3.CqlBuilder;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.ConfigurationException;
@ -47,9 +46,9 @@ public class UDAggregate extends UserFunction implements AggregateFunction
{
protected static final Logger logger = LoggerFactory.getLogger(UDAggregate.class);
private final AbstractType<?> stateType;
private final TypeCodec stateTypeCodec;
private final TypeCodec returnTypeCodec;
private final UDFDataType stateType;
private final List<UDFDataType> argumentTypes;
private final UDFDataType resultType;
protected final ByteBuffer initcond;
private final ScalarFunction stateFunction;
private final ScalarFunction finalFunction;
@ -64,9 +63,9 @@ public class UDAggregate extends UserFunction implements AggregateFunction
super(name, argTypes, returnType);
this.stateFunction = stateFunc;
this.finalFunction = finalFunc;
this.stateType = stateFunc.returnType();
this.stateTypeCodec = UDHelper.codecFor(UDHelper.driverType(stateType));
this.returnTypeCodec = UDHelper.codecFor(UDHelper.driverType(returnType));
this.argumentTypes = UDFDataType.wrap(argTypes, false);
this.resultType = UDFDataType.wrap(returnType, false);
this.stateType = stateFunc != null ? UDFDataType.wrap(stateFunc.returnType(), false) : null;
this.initcond = initcond;
}
@ -105,6 +104,12 @@ public class UDAggregate extends UserFunction implements AggregateFunction
return false;
}
@Override
public Arguments newArguments(ProtocolVersion version)
{
return FunctionArguments.newInstanceForUdf(version, argumentTypes);
}
public boolean hasReferenceTo(Function function)
{
return stateFunction == function || finalFunction == function;
@ -115,7 +120,7 @@ public class UDAggregate extends UserFunction implements AggregateFunction
{
return any(argTypes(), t -> t.referencesUserType(name))
|| returnType.referencesUserType(name)
|| (null != stateType && stateType.referencesUserType(name))
|| (null != stateType && stateType.toAbstractType().referencesUserType(name))
|| stateFunction.referencesUserType(name)
|| (null != finalFunction && finalFunction.referencesUserType(name));
}
@ -166,7 +171,7 @@ public class UDAggregate extends UserFunction implements AggregateFunction
public AbstractType<?> stateType()
{
return stateType;
return stateType == null ? null : stateType.toAbstractType();
}
public Aggregate newAggregate() throws InvalidRequestException
@ -179,17 +184,18 @@ public class UDAggregate extends UserFunction implements AggregateFunction
private Object state;
private boolean needsInit = true;
public void addInput(ProtocolVersion protocolVersion, List<ByteBuffer> values) throws InvalidRequestException
@Override
public void addInput(Arguments arguments) throws InvalidRequestException
{
maybeInit(protocolVersion);
maybeInit(arguments.getProtocolVersion());
long startTime = nanoTime();
stateFunctionCount++;
if (stateFunction instanceof UDFunction)
{
UDFunction udf = (UDFunction)stateFunction;
if (udf.isCallableWrtNullable(values))
state = udf.executeForAggregate(protocolVersion, state, values);
if (udf.isCallableWrtNullable(arguments))
state = udf.executeForAggregate(state, arguments);
}
else
{
@ -202,7 +208,7 @@ public class UDAggregate extends UserFunction implements AggregateFunction
{
if (needsInit)
{
state = initcond != null ? UDHelper.deserialize(stateTypeCodec, protocolVersion, initcond.duplicate()) : null;
state = initcond != null ? stateType.compose(protocolVersion, initcond.duplicate()) : null;
stateFunctionDuration = 0;
stateFunctionCount = 0;
needsInit = false;
@ -216,13 +222,13 @@ public class UDAggregate extends UserFunction implements AggregateFunction
// final function is traced in UDFunction
Tracing.trace("Executed UDA {}: {} call(s) to state function {} in {}\u03bcs", name(), stateFunctionCount, stateFunction.name(), stateFunctionDuration);
if (finalFunction == null)
return UDFunction.decompose(stateTypeCodec, protocolVersion, state);
return stateType.decompose(protocolVersion, state);
if (finalFunction instanceof UDFunction)
{
UDFunction udf = (UDFunction)finalFunction;
Object result = udf.executeForAggregate(protocolVersion, state, Collections.emptyList());
return UDFunction.decompose(returnTypeCodec, protocolVersion, result);
Object result = udf.executeForAggregate(state, FunctionArguments.emptyInstance(protocolVersion));
return resultType.decompose(protocolVersion, result);
}
throw new UnsupportedOperationException("UDAs only support UDFs");
}
@ -281,7 +287,8 @@ public class UDAggregate extends UserFunction implements AggregateFunction
if (null != stateType && !stateType.equals(other.stateType))
{
if (stateType.asCQL3Type().toString().equals(other.stateType.asCQL3Type().toString()))
if (stateType.toAbstractType().asCQL3Type().toString()
.equals(other.stateType.toAbstractType().asCQL3Type().toString()))
differsDeeply = true;
else
return Optional.of(Difference.SHALLOW);

View File

@ -49,7 +49,7 @@ public final class UDFByteCodeVerifier
public static final String JAVA_UDF_NAME = JavaUDF.class.getName().replace('.', '/');
public static final String OBJECT_NAME = Object.class.getName().replace('.', '/');
public static final String CTOR_SIG = "(Lorg/apache/cassandra/cql3/functions/types/TypeCodec;[Lorg/apache/cassandra/cql3/functions/types/TypeCodec;Lorg/apache/cassandra/cql3/functions/UDFContext;)V";
public static final String CTOR_SIG = "(Lorg/apache/cassandra/cql3/functions/UDFDataType;Lorg/apache/cassandra/cql3/functions/UDFContext;)V";
private final Set<String> disallowedClasses = new HashSet<>();
private final Multimap<String, String> disallowedMethodCalls = HashMultimap.create();
@ -100,21 +100,21 @@ public final class UDFByteCodeVerifier
{
if (Opcodes.ACC_PUBLIC != access)
errors.add("constructor not public");
// allowed constructor - JavaUDF(TypeCodec returnCodec, TypeCodec[] argCodecs)
// allowed constructor - JavaUDF(UDFDataType returnType, UDFContext udfContext)
return new ConstructorVisitor(errors);
}
if ("executeImpl".equals(name) && "(Lorg/apache/cassandra/transport/ProtocolVersion;Ljava/util/List;)Ljava/nio/ByteBuffer;".equals(desc))
if ("executeImpl".equals(name) && "(Lorg/apache/cassandra/cql3/functions/Arguments;)Ljava/nio/ByteBuffer;".equals(desc))
{
if (Opcodes.ACC_PROTECTED != access)
errors.add("executeImpl not protected");
// the executeImpl method - ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
// the executeImpl method - ByteBuffer executeImpl(Arguments arguments)
return new ExecuteImplVisitor(errors);
}
if ("executeAggregateImpl".equals(name) && "(Lorg/apache/cassandra/transport/ProtocolVersion;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object;".equals(desc))
if ("executeAggregateImpl".equals(name) && "(Ljava/lang/Object;Lorg/apache/cassandra/cql3/functions/Arguments;)Ljava/lang/Object;".equals(desc))
{
if (Opcodes.ACC_PROTECTED != access)
errors.add("executeAggregateImpl not protected");
// the executeImpl method - ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
// the executeImpl method - ByteBuffer executeImpl(Object state, Arguments arguments)
return new ExecuteImplVisitor(errors);
}
if ("<clinit>".equals(name))

View File

@ -23,12 +23,18 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.cassandra.cql3.functions.types.DataType;
import org.apache.cassandra.cql3.functions.types.TupleType;
import org.apache.cassandra.cql3.functions.types.TupleValue;
import org.apache.cassandra.cql3.functions.types.UDTValue;
import org.apache.cassandra.cql3.functions.types.UserType;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.schema.CQLTypeParser;
import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.cql3.functions.types.*;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.JavaDriverUtils;
/**
* Package private implementation of {@link UDFContext}
@ -36,87 +42,68 @@ import org.apache.cassandra.utils.ByteBufferUtil;
public final class UDFContextImpl implements UDFContext
{
private final KeyspaceMetadata keyspaceMetadata;
private final Map<String, TypeCodec<Object>> byName = new HashMap<>();
private final TypeCodec<Object>[] argCodecs;
private final TypeCodec<Object> returnCodec;
private final Map<String, UDFDataType> byName = new HashMap<>();
private final List<UDFDataType> argTypes;
private final UDFDataType returnType;
UDFContextImpl(List<ColumnIdentifier> argNames, TypeCodec<Object>[] argCodecs, TypeCodec<Object> returnCodec,
UDFContextImpl(List<ColumnIdentifier> argNames,
List<UDFDataType> argTypes,
UDFDataType returnType,
KeyspaceMetadata keyspaceMetadata)
{
for (int i = 0; i < argNames.size(); i++)
byName.put(argNames.get(i).toString(), argCodecs[i]);
this.argCodecs = argCodecs;
this.returnCodec = returnCodec;
byName.put(argNames.get(i).toString(), argTypes.get(i));
this.argTypes = argTypes;
this.returnType = returnType;
this.keyspaceMetadata = keyspaceMetadata;
}
public UDTValue newArgUDTValue(String argName)
{
return newUDTValue(codecFor(argName));
return newUDTValue(getArgumentTypeByName(argName).toDataType());
}
public UDTValue newArgUDTValue(int argNum)
{
return newUDTValue(codecFor(argNum));
return newUDTValue(getArgumentTypeByIndex(argNum).toDataType());
}
public UDTValue newReturnUDTValue()
{
return newUDTValue(returnCodec);
return newUDTValue(returnType.toDataType());
}
public UDTValue newUDTValue(String udtName)
{
Optional<org.apache.cassandra.db.marshal.UserType> udtType = keyspaceMetadata.types.get(ByteBufferUtil.bytes(udtName));
DataType dataType = UDHelper.driverType(udtType.orElseThrow(
DataType dataType = JavaDriverUtils.driverType(udtType.orElseThrow(
() -> new IllegalArgumentException("No UDT named " + udtName + " in keyspace " + keyspaceMetadata.name)
));
));
return newUDTValue(dataType);
}
public TupleValue newArgTupleValue(String argName)
{
return newTupleValue(codecFor(argName));
return newTupleValue(getArgumentTypeByName(argName).toDataType());
}
public TupleValue newArgTupleValue(int argNum)
{
return newTupleValue(codecFor(argNum));
return newTupleValue(getArgumentTypeByIndex(argNum).toDataType());
}
public TupleValue newReturnTupleValue()
{
return newTupleValue(returnCodec);
return newTupleValue(returnType.toDataType());
}
public TupleValue newTupleValue(String cqlDefinition)
{
AbstractType<?> abstractType = CQLTypeParser.parse(keyspaceMetadata.name, cqlDefinition, keyspaceMetadata.types);
DataType dataType = UDHelper.driverType(abstractType);
DataType dataType = JavaDriverUtils.driverType(abstractType);
return newTupleValue(dataType);
}
private TypeCodec<Object> codecFor(int argNum)
{
if (argNum < 0 || argNum >= argCodecs.length)
throw new IllegalArgumentException("Function does not declare an argument with index " + argNum);
return argCodecs[argNum];
}
private TypeCodec<Object> codecFor(String argName)
{
TypeCodec<Object> codec = byName.get(argName);
if (codec == null)
throw new IllegalArgumentException("Function does not declare an argument named '" + argName + '\'');
return codec;
}
private static UDTValue newUDTValue(TypeCodec<Object> codec)
{
DataType dataType = codec.getCqlType();
return newUDTValue(dataType);
}
private static UDTValue newUDTValue(DataType dataType)
{
if (!(dataType instanceof UserType))
@ -125,12 +112,6 @@ public final class UDFContextImpl implements UDFContext
return userType.newValue();
}
private static TupleValue newTupleValue(TypeCodec<Object> codec)
{
DataType dataType = codec.getCqlType();
return newTupleValue(dataType);
}
private static TupleValue newTupleValue(DataType dataType)
{
if (!(dataType instanceof TupleType))
@ -138,4 +119,19 @@ public final class UDFContextImpl implements UDFContext
TupleType tupleType = (TupleType) dataType;
return tupleType.newValue();
}
private UDFDataType getArgumentTypeByIndex(int index)
{
if (index < 0 || index >= argTypes.size())
throw new IllegalArgumentException("Function does not declare an argument with index " + index);
return argTypes.get(index);
}
private UDFDataType getArgumentTypeByName(String argName)
{
UDFDataType arg = byName.get(argName);
if (arg == null)
throw new IllegalArgumentException("Function does not declare an argument named '" + argName + '\'');
return arg;
}
}

View File

@ -0,0 +1,300 @@
/*
* 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.cql3.functions;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import com.google.common.base.Objects;
import com.google.common.reflect.TypeToken;
import org.apache.cassandra.cql3.functions.types.DataType;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.types.TypeCodec.*;
import org.apache.cassandra.cql3.functions.types.exceptions.InvalidTypeException;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.JavaDriverUtils;
/**
* Represents a data type used within the UDF/UDA.
* </p>
* Internaly, UDFs and UDAs use the Java driver types. This class maintains the mapping between the C* type
* and the corresponding java driver type.
*/
public final class UDFDataType
{
private static final Pattern JAVA_LANG_PREFIX = Pattern.compile("\\bjava\\.lang\\.");
/**
* The Cassandra type.
*/
private final AbstractType<?> abstractType;
/**
* The Java driver type used to serialize and deserialize the UDF/UDA data.
*/
private final TypeCodec<?> typeCodec;
/**
* The java type corresponding to this data type.
*/
private final TypeToken<?> javaType;
/**
* Creates a new {@code UDFDataType} corresponding to the specified Cassandra type.
*
* @param abstractType the Cassandra type
* @param usePrimitive {@code true} if the primitive type can be used.
*/
private UDFDataType(AbstractType<?> abstractType, boolean usePrimitive)
{
this.abstractType = abstractType;
this.typeCodec = JavaDriverUtils.codecFor(abstractType);
TypeToken<?> token = typeCodec.getJavaType();
this.javaType = usePrimitive ? token.unwrap() : token;
}
/**
* Converts this type into the corresponding Cassandra type.
* @return the corresponding Cassandra type
*/
public AbstractType<?> toAbstractType()
{
return abstractType;
}
/**
* Converts this type into the corresponding Java class.
* @return the corresponding Java class.
*/
public Class<?> toJavaClass()
{
return typeCodec.getJavaType().getRawType();
}
/**
* @return the name of the java type corresponding to this class.
*/
public String getJavaTypeName()
{
String n = javaType.toString();
return JAVA_LANG_PREFIX.matcher(n).replaceAll("");
}
/**
* Checks if this type is corresponding to a Java primitive type.
* @return {@code true} if this type is corresponding to a Java primitive type, {@code false} otherwise.
*/
public boolean isPrimitive()
{
return javaType.isPrimitive();
}
/**
* Returns the {@code UDFDataType} corresponding to the specified type.
*
* @param abstractType the Cassandra type
* @param usePrimitive {@code true} if the primitive type can be used.
* @return the {@code UDFDataType} corresponding to the specified type.
*/
public static UDFDataType wrap(AbstractType<?> abstractType, boolean usePrimitive)
{
return new UDFDataType(abstractType, usePrimitive);
}
/**
* Returns the {@code UDFDataType}s corresponding to the specified types.
*
* @param argTypes the Cassandra types
* @param usePrimitive {@code true} if the primitive types can be used.
* @return the {@code UDFDataType}s corresponding to the specified types.
*/
public static List<UDFDataType> wrap(List<AbstractType<?>> argTypes, boolean usePrimitive)
{
List<UDFDataType> types = new ArrayList<>(argTypes.size());
for (AbstractType<?> argType : argTypes)
{
types.add(UDFDataType.wrap(argType, usePrimitive));
}
return types;
}
/**
* Converts this type into the corresponding Java driver type.
* @return the corresponding Java driver type.
*/
public DataType toDataType()
{
return typeCodec.getCqlType();
}
@Override
public int hashCode()
{
return Objects.hashCode(abstractType, javaType);
}
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof UDFDataType))
return false;
UDFDataType that = (UDFDataType) obj;
return abstractType.equals(that.abstractType) && javaType.equals(that.javaType);
}
/**
* Deserializes the specified oject.
*
* @param protocolVersion the protocol version
* @param buffer the serialized object
* @return the deserialized object
*/
public Object compose(ProtocolVersion protocolVersion, ByteBuffer buffer)
{
if (buffer == null || (buffer.remaining() == 0 && abstractType.isEmptyValueMeaningless()))
return null;
return typeCodec.deserialize(buffer, protocolVersion);
}
/**
* Serialized the specified oject.
*
* @param protocolVersion the protocol version
* @param value the value to serialize
* @return the serialized object
*/
@SuppressWarnings("unchecked")
public ByteBuffer decompose(ProtocolVersion protocolVersion, Object value)
{
if (value == null)
return null;
if (!toJavaClass().isAssignableFrom(value.getClass()))
throw new InvalidTypeException("Invalid value for CQL type " + toDataType().getName());
return ((TypeCodec<Object>) typeCodec).serialize(value, protocolVersion);
}
/**
* Serialized the specified byte.
*
* @param protocolVersion the protocol version
* @param value the value to serialize
* @return the serialized byte
*/
public ByteBuffer decompose(ProtocolVersion protocolVersion, byte value)
{
if (!(typeCodec instanceof PrimitiveByteCodec))
throw new InvalidTypeException("Invalid value for CQL type " + toDataType().getName());
return ((PrimitiveByteCodec) typeCodec).serializeNoBoxing(value, protocolVersion);
}
/**
* Serialized the specified byte.
*
* @param protocolVersion the protocol version
* @param value the value to serialize
* @return the serialized byte
*/
public ByteBuffer decompose(ProtocolVersion protocolVersion, short value)
{
if (!(typeCodec instanceof PrimitiveShortCodec))
throw new InvalidTypeException("Invalid value for CQL type " + toDataType().getName());
return ((PrimitiveShortCodec) typeCodec).serializeNoBoxing(value, protocolVersion);
}
/**
* Serialized the specified byte.
*
* @param protocolVersion the protocol version
* @param value the value to serialize
* @return the serialized byte
*/
public ByteBuffer decompose(ProtocolVersion protocolVersion, int value)
{
if (!(typeCodec instanceof PrimitiveIntCodec))
throw new InvalidTypeException("Invalid value for CQL type " + toDataType().getName());
return ((PrimitiveIntCodec) typeCodec).serializeNoBoxing(value, protocolVersion);
}
/**
* Serialized the specified byte.
*
* @param protocolVersion the protocol version
* @param value the value to serialize
* @return the serialized byte
*/
public ByteBuffer decompose(ProtocolVersion protocolVersion, long value)
{
if (!(typeCodec instanceof PrimitiveLongCodec))
throw new InvalidTypeException("Invalid value for CQL type " + toDataType().getName());
return ((PrimitiveLongCodec) typeCodec).serializeNoBoxing(value, protocolVersion);
}
/**
* Serialized the specified byte.
*
* @param protocolVersion the protocol version
* @param value the value to serialize
* @return the serialized byte
*/
public ByteBuffer decompose(ProtocolVersion protocolVersion, float value)
{
if (!(typeCodec instanceof PrimitiveFloatCodec))
throw new InvalidTypeException("Invalid value for CQL type " + toDataType().getName());
return ((PrimitiveFloatCodec) typeCodec).serializeNoBoxing(value, protocolVersion);
}
/**
* Serialized the specified byte.
*
* @param protocolVersion the protocol version
* @param value the value to serialize
* @return the serialized byte
*/
public ByteBuffer decompose(ProtocolVersion protocolVersion, double value)
{
if (!(typeCodec instanceof PrimitiveDoubleCodec))
throw new InvalidTypeException("Invalid value for CQL type " + toDataType().getName());
return ((PrimitiveDoubleCodec) typeCodec).serializeNoBoxing(value, protocolVersion);
}
public ArgumentDeserializer getArgumentDeserializer()
{
// If the type is corresponding to a primitive one we can use the ArgumentDeserializer of the AbstractType
if (isPrimitive())
return abstractType.getArgumentDeserializer();
return this::compose;
}
}

View File

@ -22,7 +22,6 @@ import java.lang.management.ThreadMXBean;
import java.net.InetAddress;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
@ -46,7 +45,6 @@ import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.CqlBuilder;
import org.apache.cassandra.cql3.functions.types.DataType;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
@ -77,8 +75,8 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
protected final String language;
protected final String body;
protected final TypeCodec<Object>[] argCodecs;
protected final TypeCodec<Object> returnCodec;
protected final List<UDFDataType> argumentTypes;
protected final UDFDataType resultType;
protected final boolean calledOnNullInput;
protected final UDFContext udfContext;
@ -115,6 +113,8 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
"java/time/",
"java/util/",
"org/apache/cassandra/cql3/functions/types/",
"org/apache/cassandra/cql3/functions/Arguments.class",
"org/apache/cassandra/cql3/functions/UDFDataType.class",
"org/apache/cassandra/cql3/functions/JavaUDF.class",
"org/apache/cassandra/cql3/functions/UDFContext.class",
"org/apache/cassandra/exceptions/",
@ -210,32 +210,23 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
boolean calledOnNullInput,
String language,
String body)
{
this(name, argNames, argTypes, UDHelper.driverTypes(argTypes), returnType,
UDHelper.driverType(returnType), calledOnNullInput, language, body);
}
protected UDFunction(FunctionName name,
List<ColumnIdentifier> argNames,
List<AbstractType<?>> argTypes,
DataType[] argDataTypes,
AbstractType<?> returnType,
DataType returnDataType,
boolean calledOnNullInput,
String language,
String body)
{
super(name, argTypes, returnType);
assert new HashSet<>(argNames).size() == argNames.size() : "duplicate argument names";
this.argNames = argNames;
this.language = language;
this.body = body;
this.argCodecs = UDHelper.codecsFor(argDataTypes);
this.returnCodec = UDHelper.codecFor(returnDataType);
this.argumentTypes = UDFDataType.wrap(argTypes, !calledOnNullInput);
this.resultType = UDFDataType.wrap(returnType, !calledOnNullInput);
this.calledOnNullInput = calledOnNullInput;
KeyspaceMetadata keyspaceMetadata = Schema.instance.getKeyspaceMetadata(name.keyspace);
this.udfContext = new UDFContextImpl(argNames, argCodecs, returnCodec,
keyspaceMetadata);
this.udfContext = new UDFContextImpl(argNames, argumentTypes, resultType, keyspaceMetadata);
}
@Override
public Arguments newArguments(ProtocolVersion version)
{
return FunctionArguments.newInstanceForUdf(version, argumentTypes);
}
public static UDFunction tryCreate(FunctionName name,
@ -294,12 +285,14 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
return ImmediateExecutor.INSTANCE;
}
protected Object executeAggregateUserDefined(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> parameters)
@Override
protected Object executeAggregateUserDefined(Object firstParam, Arguments arguments)
{
throw broken();
}
public ByteBuffer executeUserDefined(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public ByteBuffer executeUserDefined(Arguments arguments)
{
throw broken();
}
@ -361,28 +354,29 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
return builder.toString();
}
@Override
public boolean isPure()
{
// Right now, we have no way to check if an UDF is pure. Due to that we consider them as non pure to avoid any risk.
return false;
}
public final ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public final ByteBuffer execute(Arguments arguments)
{
assertUdfsEnabled(language);
if (!isCallableWrtNullable(parameters))
if (!isCallableWrtNullable(arguments))
return null;
long tStart = nanoTime();
parameters = makeEmptyParametersNull(parameters);
try
{
// Using async UDF execution is expensive (adds about 100us overhead per invocation on a Core-i7 MBPr).
ByteBuffer result = DatabaseDescriptor.enableUserDefinedFunctionsThreads()
? executeAsync(protocolVersion, parameters)
: executeUserDefined(protocolVersion, parameters);
? executeAsync(arguments)
: executeUserDefined(arguments);
Tracing.trace("Executed UDF {} in {}\u03bcs", name(), (nanoTime() - tStart) / 1000);
return result;
@ -400,28 +394,21 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
}
}
/**
* Like {@link ScalarFunction#execute(ProtocolVersion, List)} but the first parameter is already in non-serialized form.
* Remaining parameters (2nd paramters and all others) are in {@code parameters}.
* This is used to prevent superfluous (de)serialization of the state of aggregates.
* Means: scalar functions of aggregates are called using this variant.
*/
public final Object executeForAggregate(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> parameters)
public final Object executeForAggregate(Object state, Arguments arguments)
{
assertUdfsEnabled(language);
if (!calledOnNullInput && firstParam == null || !isCallableWrtNullable(parameters))
if (!calledOnNullInput && state == null || !isCallableWrtNullable(arguments))
return null;
long tStart = nanoTime();
parameters = makeEmptyParametersNull(parameters);
try
{
// Using async UDF execution is expensive (adds about 100us overhead per invocation on a Core-i7 MBPr).
Object result = DatabaseDescriptor.enableUserDefinedFunctionsThreads()
? executeAggregateAsync(protocolVersion, firstParam, parameters)
: executeAggregateUserDefined(protocolVersion, firstParam, parameters);
? executeAggregateAsync(state, arguments)
: executeAggregateUserDefined(state, arguments);
Tracing.trace("Executed UDF {} in {}\u03bcs", name(), (nanoTime() - tStart) / 1000);
return result;
}
@ -477,29 +464,23 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
}
}
private ByteBuffer executeAsync(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
private ByteBuffer executeAsync(Arguments arguments)
{
ThreadIdAndCpuTime threadIdAndCpuTime = new ThreadIdAndCpuTime();
return async(threadIdAndCpuTime, () -> {
threadIdAndCpuTime.setup();
return executeUserDefined(protocolVersion, parameters);
return executeUserDefined(arguments);
});
}
/**
* Like {@link #executeAsync(ProtocolVersion, List)} but the first parameter is already in non-serialized form.
* Remaining parameters (2nd paramters and all others) are in {@code parameters}.
* This is used to prevent superfluous (de)serialization of the state of aggregates.
* Means: scalar functions of aggregates are called using this variant.
*/
private Object executeAggregateAsync(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> parameters)
private Object executeAggregateAsync(Object state, Arguments arguments)
{
ThreadIdAndCpuTime threadIdAndCpuTime = new ThreadIdAndCpuTime();
return async(threadIdAndCpuTime, () -> {
threadIdAndCpuTime.setup();
return executeAggregateUserDefined(protocolVersion, firstParam, parameters);
return executeAggregateUserDefined(state, arguments);
});
}
@ -578,32 +559,16 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
}
}
private List<ByteBuffer> makeEmptyParametersNull(List<ByteBuffer> parameters)
{
List<ByteBuffer> r = new ArrayList<>(parameters.size());
for (int i = 0; i < parameters.size(); i++)
{
ByteBuffer param = parameters.get(i);
r.add(UDHelper.isNullOrEmpty(argTypes.get(i), param)
? null : param);
}
return r;
}
protected abstract ExecutorService executor();
public boolean isCallableWrtNullable(List<ByteBuffer> parameters)
public boolean isCallableWrtNullable(Arguments arguments)
{
if (!calledOnNullInput)
for (int i = 0; i < parameters.size(); i++)
if (UDHelper.isNullOrEmpty(argTypes.get(i), parameters.get(i)))
return false;
return true;
return calledOnNullInput || !arguments.containsNulls();
}
protected abstract ByteBuffer executeUserDefined(ProtocolVersion protocolVersion, List<ByteBuffer> parameters);
protected abstract ByteBuffer executeUserDefined(Arguments arguments);
protected abstract Object executeAggregateUserDefined(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> parameters);
protected abstract Object executeAggregateUserDefined(Object firstParam, Arguments arguments);
public boolean isAggregate()
{
@ -630,23 +595,6 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
return language;
}
/**
* Used by UDF implementations (both Java code generated by {@link JavaBasedUDFunction}) to convert the C*
* serialized representation to the Java object representation.
*
* @param protocolVersion the native protocol version used for serialization
* @param argIndex index of the UDF input argument
*/
protected Object compose(ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
{
return compose(argCodecs, protocolVersion, argIndex, value);
}
protected static Object compose(TypeCodec<Object>[] codecs, ProtocolVersion protocolVersion, int argIndex, ByteBuffer value)
{
return value == null ? null : UDHelper.deserialize(codecs[argIndex], protocolVersion, value);
}
/**
* Used by UDF implementations (both Java code generated by {@link JavaBasedUDFunction}) to convert the Java
* object representation for the return value to the C* serialized representation.
@ -655,12 +603,7 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
*/
protected ByteBuffer decompose(ProtocolVersion protocolVersion, Object value)
{
return decompose(returnCodec, protocolVersion, value);
}
protected static ByteBuffer decompose(TypeCodec<Object> codec, ProtocolVersion protocolVersion, Object value)
{
return value == null ? null : UDHelper.serialize(codec, protocolVersion, value);
return resultType.decompose(protocolVersion, value);
}
@Override
@ -746,7 +689,7 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
@Override
public int hashCode()
{
return Objects.hashCode(name, UserFunctions.typeHashCode(argTypes), UserFunctions.typeHashCode(returnType), returnType, language, body);
return Objects.hashCode(name, UserFunctions.typeHashCode(argTypes), returnType, language, body);
}
private static class UDFClassLoader extends ClassLoader

View File

@ -1,143 +0,0 @@
/*
* 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.cql3.functions;
import java.nio.ByteBuffer;
import java.util.List;
import com.google.common.reflect.TypeToken;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.functions.types.*;
import org.apache.cassandra.cql3.functions.types.exceptions.InvalidTypeException;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* Helper class for User Defined Functions, Types and Aggregates.
*/
public final class UDHelper
{
private static final CodecRegistry codecRegistry = new CodecRegistry();
static TypeCodec<Object>[] codecsFor(DataType[] dataType)
{
TypeCodec<Object>[] codecs = new TypeCodec[dataType.length];
for (int i = 0; i < dataType.length; i++)
codecs[i] = codecFor(dataType[i]);
return codecs;
}
public static TypeCodec<Object> codecFor(DataType dataType)
{
return codecRegistry.codecFor(dataType);
}
/**
* Construct an array containing the Java classes for the given {@link DataType}s.
*
* @param dataTypes array with UDF argument types
* @param calledOnNullInput whether to allow {@code null} as an argument value
* @return array of same size with UDF arguments
*/
public static TypeToken<?>[] typeTokens(TypeCodec<Object>[] dataTypes, boolean calledOnNullInput)
{
TypeToken<?>[] paramTypes = new TypeToken[dataTypes.length];
for (int i = 0; i < paramTypes.length; i++)
{
TypeToken<?> typeToken = dataTypes[i].getJavaType();
if (!calledOnNullInput)
{
// only care about classes that can be used in a data type
Class<?> clazz = typeToken.getRawType();
if (clazz == Integer.class)
typeToken = TypeToken.of(int.class);
else if (clazz == Long.class)
typeToken = TypeToken.of(long.class);
else if (clazz == Byte.class)
typeToken = TypeToken.of(byte.class);
else if (clazz == Short.class)
typeToken = TypeToken.of(short.class);
else if (clazz == Float.class)
typeToken = TypeToken.of(float.class);
else if (clazz == Double.class)
typeToken = TypeToken.of(double.class);
else if (clazz == Boolean.class)
typeToken = TypeToken.of(boolean.class);
}
paramTypes[i] = typeToken;
}
return paramTypes;
}
/**
* Construct an array containing the {@link DataType}s for the
* C* internal types.
*
* @param abstractTypes list with UDF argument types
* @return array with argument types as {@link DataType}
*/
public static DataType[] driverTypes(List<AbstractType<?>> abstractTypes)
{
DataType[] argDataTypes = new DataType[abstractTypes.size()];
for (int i = 0; i < argDataTypes.length; i++)
argDataTypes[i] = driverType(abstractTypes.get(i));
return argDataTypes;
}
/**
* Returns the {@link DataType} for the C* internal type.
*/
public static DataType driverType(AbstractType abstractType)
{
CQL3Type cqlType = abstractType.asCQL3Type();
String abstractTypeDef = cqlType.getType().toString();
return driverTypeFromAbstractType(abstractTypeDef);
}
public static DataType driverTypeFromAbstractType(String abstractTypeDef)
{
return DataTypeClassNameParser.parseOne(abstractTypeDef,
ProtocolVersion.CURRENT,
codecRegistry);
}
public static Object deserialize(TypeCodec<?> codec, ProtocolVersion protocolVersion, ByteBuffer value)
{
return codec.deserialize(value, protocolVersion);
}
public static ByteBuffer serialize(TypeCodec<?> codec, ProtocolVersion protocolVersion, Object value)
{
if (!codec.getJavaType().getRawType().isAssignableFrom(value.getClass()))
throw new InvalidTypeException("Invalid value for CQL type " + codec.getCqlType().getName());
return ((TypeCodec)codec).serialize(value, protocolVersion);
}
public static Class<?> asJavaClass(TypeCodec<?> codec)
{
return codec.getJavaType().getRawType();
}
public static boolean isNullOrEmpty(AbstractType<?> type, ByteBuffer bb)
{
return bb == null ||
(bb.remaining() == 0 && type.isEmptyValueMeaningless());
}
}

View File

@ -22,7 +22,6 @@ import java.util.*;
import org.apache.cassandra.db.marshal.UUIDType;
import org.apache.cassandra.serializers.UUIDSerializer;
import org.apache.cassandra.transport.ProtocolVersion;
public abstract class UuidFcts
{
@ -33,7 +32,8 @@ public abstract class UuidFcts
public static final NativeFunction uuidFct = new NativeScalarFunction("uuid", UUIDType.instance)
{
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
@Override
public ByteBuffer execute(Arguments arguments)
{
return UUIDSerializer.instance.serialize(UUID.randomUUID());
}

View File

@ -35,6 +35,7 @@ import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.CqlBuilder;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.Terms;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.Function;
import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.FunctionResolver;
@ -64,7 +65,7 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.invalidReq
* CQL function actually has three arguments. The first argument is always ommitted when attaching the function to a
* schema column. The value of that first argument is always the value of the masked column, in this case an int.
*/
public abstract class ColumnMask
public class ColumnMask
{
public static final String DISABLED_ERROR_MESSAGE = "Cannot mask columns because dynamic data masking is not " +
"enabled. You can enable it with the " +
@ -76,20 +77,13 @@ public abstract class ColumnMask
/** The values of the arguments of the partially applied masking function. */
protected final ByteBuffer[] partialArgumentValues;
private ColumnMask(ScalarFunction function, ByteBuffer... partialArgumentValues)
public ColumnMask(ScalarFunction function, ByteBuffer... partialArgumentValues)
{
assert function.argTypes().size() == partialArgumentValues.length + 1;
this.function = function;
this.partialArgumentValues = partialArgumentValues;
}
public static ColumnMask build(ScalarFunction function, ByteBuffer... partialArgumentValues)
{
return function.isNative()
? new Native((MaskingFunction) function, partialArgumentValues)
: new Custom(function, partialArgumentValues);
}
/**
* @return The types of the arguments of the partially applied masking function, as an unmodifiable list.
*/
@ -122,23 +116,37 @@ public abstract class ColumnMask
.build();
Function newFunction = FunctionResolver.get(function.name().keyspace, function.name(), args, null, null, null);
assert newFunction != null;
return build((ScalarFunction) newFunction, partialArgumentValues);
return new ColumnMask((ScalarFunction) newFunction, partialArgumentValues);
}
/**
* @param protocolVersion the used version of the transport protocol
* @param value a column value to be masked
* @return the specified value after having been masked by the masked function
* @param version the used version of the transport protocol
* @return a masker instance that caches the terminal masking function arguments
*/
public ByteBuffer mask(ProtocolVersion protocolVersion, ByteBuffer value)
public Masker masker(ProtocolVersion version)
{
if (!DatabaseDescriptor.getDynamicDataMaskingEnabled())
return value;
return maskInternal(protocolVersion, value);
return new Masker(version, function, partialArgumentValues);
}
protected abstract ByteBuffer maskInternal(ProtocolVersion protocolVersion, ByteBuffer value);
public static class Masker
{
private final ScalarFunction function;
private final Arguments arguments;
private Masker(ProtocolVersion version, ScalarFunction function, ByteBuffer[] partialArgumentValues)
{
this.function = function;
arguments = function.newArguments(version);
for (int i = 0; i < partialArgumentValues.length; i++)
arguments.set(i + 1, partialArgumentValues[i]);
}
public ByteBuffer mask(ByteBuffer value)
{
arguments.set(0, value);
return function.execute(arguments);
}
}
public static void ensureEnabled()
{
@ -183,57 +191,6 @@ public abstract class ColumnMask
builder.append(" MASKED WITH ").append(toString());
}
/**
* {@link ColumnMask} for native masking functions.
*/
private static class Native extends ColumnMask
{
private final MaskingFunction.Masker masker;
public Native(MaskingFunction function, ByteBuffer... partialArgumentValues)
{
super(function, partialArgumentValues);
masker = function.masker(partialArgumentValues);
}
@Override
protected ByteBuffer maskInternal(ProtocolVersion protocolVersion, ByteBuffer value)
{
return masker.mask(value);
}
}
/**
* {@link ColumnMask} for user-defined masking functions.
*/
private static class Custom extends ColumnMask
{
public Custom(ScalarFunction function, ByteBuffer... partialArgumentValues)
{
super(function, partialArgumentValues);
}
@Override
protected ByteBuffer maskInternal(ProtocolVersion protocolVersion, ByteBuffer value)
{
List<ByteBuffer> argumentValues;
int numPartialArgs = partialArgumentValues.length;
if (numPartialArgs == 0)
{
argumentValues = Collections.singletonList(value);
}
else
{
ByteBuffer[] args = new ByteBuffer[numPartialArgs + 1];
args[0] = value;
System.arraycopy(partialArgumentValues, 0, args, 1, numPartialArgs);
argumentValues = Arrays.asList(args);
}
return function.execute(protocolVersion, argumentValues);
}
}
/**
* A parsed but not prepared column mask.
*/
@ -252,7 +209,7 @@ public abstract class ColumnMask
{
ScalarFunction function = findMaskingFunction(keyspace, table, column, type);
ByteBuffer[] partialArguments = preparePartialArguments(keyspace, function);
return ColumnMask.build(function, partialArguments);
return new ColumnMask(function, partialArguments);
}
private ScalarFunction findMaskingFunction(String keyspace, String table, ColumnIdentifier column, AbstractType<?> type)

View File

@ -21,11 +21,15 @@ package org.apache.cassandra.cql3.functions.masking;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.FunctionArguments;
import org.apache.cassandra.cql3.functions.FunctionFactory;
import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.FunctionParameter;
import org.apache.cassandra.cql3.functions.NativeFunction;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* A {@link MaskingFunction} that returns a fixed replacement value for the data type of its single argument.
@ -40,34 +44,24 @@ public class DefaultMaskingFunction extends MaskingFunction
{
public static final String NAME = "default";
private final Masker masker;
AbstractType<?> inputType;
private <T> DefaultMaskingFunction(FunctionName name, AbstractType<T> inputType)
private DefaultMaskingFunction(FunctionName name, AbstractType<?> inputType)
{
super(name, inputType, inputType);
masker = new Masker(inputType);
this.inputType = inputType;
}
@Override
public Masker masker(ByteBuffer... parameters)
public Arguments newArguments(ProtocolVersion version)
{
return masker;
return FunctionArguments.newNoopInstance(version, 1);
}
private static class Masker implements MaskingFunction.Masker
@Override
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
private final ByteBuffer defaultValue;
private Masker(AbstractType<?> inputType)
{
defaultValue = inputType.getMaskedValue();
}
@Override
public ByteBuffer mask(ByteBuffer value)
{
return defaultValue;
}
return inputType.getMaskedValue();
}
/** @return a {@link FunctionFactory} to build new {@link DefaultMaskingFunction}s. */

View File

@ -22,22 +22,25 @@ import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.function.Supplier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Suppliers;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.FunctionArguments;
import org.apache.cassandra.cql3.functions.FunctionFactory;
import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.FunctionParameter;
import org.apache.cassandra.cql3.functions.NativeFunction;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.StringType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.ByteBufferUtil;
/**
@ -50,61 +53,42 @@ public class HashMaskingFunction extends MaskingFunction
public static final String NAME = "hash";
/** The default hashing algorithm to be used if no other algorithm is specified in the call to the function. */
public static final String DEFAULT_ALGORITHM = "SHA-256";
public static final ByteBuffer DEFAULT_ALGORITHM = UTF8Type.instance.decompose("SHA-256");
/**
* All the created digests that we use to generate the hash, so we don't need to get them on every call.
* We use the serialized algorithm as key, so we don't have to deserialize it for getting a cache hit.
*/
private static final Map<ByteBuffer, MessageDigest> DIGESTS = new ConcurrentHashMap<>();
// The default message digest is lazily built to prevent a failure during server startup if the algorithm is not
// available. That way, if the algorithm is not found only the calls to the function will fail.
private static final Supplier<MessageDigest> DEFAULT_DIGEST = Suppliers.memoize(() -> messageDigest(DEFAULT_ALGORITHM));
private static final AbstractType<?>[] DEFAULT_ARGUMENTS = {};
private static final AbstractType<?>[] ARGUMENTS_WITH_ALGORITHM = new AbstractType<?>[]{ UTF8Type.instance };
/** The type of the supplied algorithm argument, {@code null} if that argument isn't supplied. */
@Nullable
private final StringType algorithmArgumentType;
private HashMaskingFunction(FunctionName name, AbstractType<?> inputType, @Nullable StringType algorithmArgumentType)
private HashMaskingFunction(FunctionName name, AbstractType<?> inputType, boolean hasAlgorithmArgument)
{
super(name, BytesType.instance, inputType, argumentsType(algorithmArgumentType));
this.algorithmArgumentType = algorithmArgumentType;
}
private static AbstractType<?>[] argumentsType(@Nullable StringType algorithmArgumentType)
{
// the algorithm argument is optional, so we will have different signatures depending on whether that argument
// is supplied or not
return algorithmArgumentType == null
? DEFAULT_ARGUMENTS
: new AbstractType<?>[]{ algorithmArgumentType };
super(name, BytesType.instance, inputType, hasAlgorithmArgument ? ARGUMENTS_WITH_ALGORITHM : DEFAULT_ARGUMENTS);
}
@Override
public Masker masker(ByteBuffer... parameters)
public Arguments newArguments(ProtocolVersion version)
{
return new Masker(algorithmArgumentType, parameters);
return new FunctionArguments(version,
ArgumentDeserializer.NOOP_DESERIALIZER, // the value to be masked
(v, b) -> messageDigest(b)); // the algorithm, if any
}
private static class Masker implements MaskingFunction.Masker
@Override
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
private final MessageDigest digest;
ByteBuffer value = arguments.get(0);
if (value == null)
return null;
private Masker(StringType algorithmArgumentType, ByteBuffer... parameters)
{
if (algorithmArgumentType == null || parameters[0] == null)
{
digest = DEFAULT_DIGEST.get();
}
else
{
String algorithm = algorithmArgumentType.compose(parameters[0]);
digest = messageDigest(algorithm);
}
}
@Override
public ByteBuffer mask(ByteBuffer value)
{
return HashMaskingFunction.hash(digest, value);
}
MessageDigest digest = arguments.get(1);
if (digest == null)
digest = messageDigest(DEFAULT_ALGORITHM);
return HashMaskingFunction.hash(digest, value);
}
@VisibleForTesting
@ -118,6 +102,19 @@ public class HashMaskingFunction extends MaskingFunction
return BytesType.instance.compose(ByteBuffer.wrap(hash));
}
@VisibleForTesting
static MessageDigest messageDigest(@Nullable ByteBuffer algorithm)
{
ByteBuffer cacheKey = algorithm == null ? DEFAULT_ALGORITHM : algorithm;
MessageDigest digest = DIGESTS.get(cacheKey);
if (digest == null)
{
digest = messageDigest(UTF8Type.instance.compose(cacheKey));
DIGESTS.put(cacheKey.duplicate(), digest);
}
return digest;
}
@VisibleForTesting
static MessageDigest messageDigest(String algorithm)
{
@ -144,9 +141,9 @@ public class HashMaskingFunction extends MaskingFunction
switch (argTypes.size())
{
case 1:
return new HashMaskingFunction(name, argTypes.get(0), null);
return new HashMaskingFunction(name, argTypes.get(0), false);
case 2:
return new HashMaskingFunction(name, argTypes.get(0), UTF8Type.instance);
return new HashMaskingFunction(name, argTypes.get(0), true);
default:
throw invalidNumberOfArgumentsException();
}

View File

@ -18,9 +18,6 @@
package org.apache.cassandra.cql3.functions.masking;
import java.nio.ByteBuffer;
import java.util.List;
import com.google.common.collect.ObjectArrays;
import org.apache.cassandra.cql3.functions.FunctionFactory;
@ -28,7 +25,6 @@ import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.FunctionParameter;
import org.apache.cassandra.cql3.functions.NativeScalarFunction;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* A {@link NativeScalarFunction} that totally or partially replaces the original value of a column value,
@ -56,33 +52,6 @@ public abstract class MaskingFunction extends NativeScalarFunction
super(name.name, outputType, ObjectArrays.concat(inputType, argsType));
}
@Override
public final ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer[] partialParameters = new ByteBuffer[parameters.size() - 1];
for (int i = 0; i < partialParameters.length; i++)
partialParameters[i] = parameters.get(i + 1);
return masker(partialParameters).mask(parameters.get(0));
}
/**
* Returns a new {@link Masker} for the specified masking parameters.
* This is meant to be used by {@link ColumnMask}, so it doesn't need to evaluate the arguments on every call.
*
* @param parameters the masking parameters in the function call.
* @return a new {@link Masker} using the specified masking arguments
*/
public abstract Masker masker(ByteBuffer... parameters);
/**
* Class that actually makes the masking of the first function parameter according to the masking arguments.
*/
public interface Masker
{
public ByteBuffer mask(ByteBuffer value);
}
protected static abstract class Factory extends FunctionFactory
{
public Factory(String name, FunctionParameter... parameters)

View File

@ -21,11 +21,15 @@ package org.apache.cassandra.cql3.functions.masking;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.FunctionArguments;
import org.apache.cassandra.cql3.functions.FunctionFactory;
import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.FunctionParameter;
import org.apache.cassandra.cql3.functions.NativeFunction;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* A {@link MaskingFunction} that always returns a {@code null} column. The returned value is always an absent column,
@ -37,7 +41,6 @@ import org.apache.cassandra.db.marshal.AbstractType;
public class NullMaskingFunction extends MaskingFunction
{
public static final String NAME = "null";
private static final Masker MASKER = new Masker();
private NullMaskingFunction(FunctionName name, AbstractType<?> inputType)
{
@ -45,18 +48,15 @@ public class NullMaskingFunction extends MaskingFunction
}
@Override
public Masker masker(ByteBuffer... parameters)
public Arguments newArguments(ProtocolVersion version)
{
return MASKER;
return FunctionArguments.newNoopInstance(version, 1);
}
private static class Masker implements MaskingFunction.Masker
@Override
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
@Override
public ByteBuffer mask(ByteBuffer value)
{
return null;
}
return null;
}
/** @return a {@link FunctionFactory} to build new {@link NullMaskingFunction}s. */

View File

@ -23,12 +23,14 @@ import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.FunctionArguments;
import org.apache.cassandra.cql3.functions.FunctionFactory;
import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.FunctionParameter;
@ -37,10 +39,13 @@ import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.ProtocolVersion;
import static java.lang.String.format;
/**
* A {@link MaskingFunction} applied to a {@link org.apache.cassandra.db.marshal.StringType} value that,
* depending on {@link Type}:
* depending on {@link Kind}:
* <ul>
* <li>Replaces each character between the supplied positions by the supplied padding character. In other words,
* it will mask all the characters except the first m and last n.</li>
@ -55,25 +60,38 @@ public class PartialMaskingFunction extends MaskingFunction
public static final char DEFAULT_PADDING_CHAR = '*';
/** The type of partial masking to perform, inner or outer. */
private final Type type;
private final Kind kind;
/** The original type of the masked value. */
private final AbstractType<String> inputType;
/** Whether a padding argument hab been supplied. */
@Nullable
private final boolean hasPaddingArgument;
/** The custom argument deserializer for the padding character. */
private final ArgumentDeserializer paddingArgumentDeserializer;
private PartialMaskingFunction(FunctionName name,
Type type,
Kind kind,
AbstractType<String> inputType,
boolean hasPaddingArgument)
{
super(name, inputType, inputType, argumentsType(hasPaddingArgument));
this.type = type;
this.kind = kind;
this.inputType = inputType;
this.hasPaddingArgument = hasPaddingArgument;
paddingArgumentDeserializer = (version, buffer) -> {
if (buffer == null || !hasPaddingArgument)
return null;
String arg = UTF8Type.instance.compose(buffer);
if (arg.length() != 1)
{
throw new InvalidRequestException(format("The padding argument for function %s should " +
"be single-character, but '%s' has %d characters.",
name, arg, arg.length()));
}
return arg.charAt(0);
};
}
private static AbstractType<?>[] argumentsType(boolean hasPaddingArgument)
@ -87,56 +105,31 @@ public class PartialMaskingFunction extends MaskingFunction
}
@Override
public Masker masker(ByteBuffer... parameters)
public Arguments newArguments(ProtocolVersion version)
{
return new Masker(parameters);
return new FunctionArguments(version,
inputType.getArgumentDeserializer(),
Int32Type.instance.getArgumentDeserializer(),
Int32Type.instance.getArgumentDeserializer(),
paddingArgumentDeserializer);
}
private class Masker implements MaskingFunction.Masker
@Override
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
private final int begin, end;
private final char padding;
String value = arguments.get(0);
if (value == null)
return null;
private Masker(ByteBuffer... parameters)
{
// Parse the beginning and end positions. No validation is needed since the masker accepts negatives,
// but we should consider that the arguments migh be null.
begin = parameters[0] == null ? 0 : Int32Type.instance.compose(parameters[0]);
end = parameters[1] == null ? 0 : Int32Type.instance.compose(parameters[1]);
int begin = arguments.get(1) != null ? arguments.getAsInt(1) : 0;
int end = arguments.get(2) != null ? arguments.getAsInt(2) : 0;
char padding = arguments.get(3) == null ? DEFAULT_PADDING_CHAR : arguments.get(3);
// Parse the padding character. The type of the argument is a string of any length because we don't have a
// character type in CQL, so we should verify that the passed string argument is single-character.
if (hasPaddingArgument && parameters[2] != null)
{
String parameter = UTF8Type.instance.compose(parameters[2]);
if (parameter.length() != 1)
{
throw new InvalidRequestException(String.format("The padding argument for function %s should " +
"be single-character, but '%s' has %d characters.",
name(), parameter, parameter.length()));
}
padding = parameter.charAt(0);
}
else
{
padding = DEFAULT_PADDING_CHAR;
}
}
@Override
public ByteBuffer mask(ByteBuffer value)
{
// Null column values aren't masked
if (value == null)
return null;
String stringValue = inputType.compose(value);
String maskedValue = type.mask(stringValue, begin, end, padding);
return inputType.decompose(maskedValue);
}
String maskedValue = kind.mask(value, begin, end, padding);
return inputType.decompose(maskedValue);
}
public enum Type
public enum Kind
{
/** Masks everything except the first {@code begin} and last {@code end} characters. */
INNER
@ -181,14 +174,14 @@ public class PartialMaskingFunction extends MaskingFunction
/** @return a collection of function factories to build new {@code PartialMaskingFunction} functions. */
public static Collection<FunctionFactory> factories()
{
return Stream.of(Type.values())
return Stream.of(Kind.values())
.map(PartialMaskingFunction::factory)
.collect(Collectors.toSet());
}
private static FunctionFactory factory(Type type)
private static FunctionFactory factory(Kind kind)
{
return new MaskingFunction.Factory(type.name(),
return new MaskingFunction.Factory(kind.name(),
FunctionParameter.string(),
FunctionParameter.fixed(CQL3Type.Native.INT),
FunctionParameter.fixed(CQL3Type.Native.INT),
@ -199,7 +192,7 @@ public class PartialMaskingFunction extends MaskingFunction
protected NativeFunction doGetOrCreateFunction(List<AbstractType<?>> argTypes, AbstractType<?> receiverType)
{
AbstractType<String> inputType = (AbstractType<String>) argTypes.get(0);
return new PartialMaskingFunction(name, type, inputType, argTypes.size() == 4);
return new PartialMaskingFunction(name, kind, inputType, argTypes.size() == 4);
}
};
}

View File

@ -21,11 +21,15 @@ package org.apache.cassandra.cql3.functions.masking;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.FunctionArguments;
import org.apache.cassandra.cql3.functions.FunctionFactory;
import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.FunctionParameter;
import org.apache.cassandra.cql3.functions.NativeFunction;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* A {@link MaskingFunction} that replaces the specified column value by a certain replacement value.
@ -44,25 +48,15 @@ public class ReplaceMaskingFunction extends MaskingFunction
}
@Override
public Masker masker(ByteBuffer... parameters)
public Arguments newArguments(ProtocolVersion version)
{
return new Masker(parameters[0]);
return FunctionArguments.newNoopInstance(version, 2);
}
private static class Masker implements MaskingFunction.Masker
@Override
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
private final ByteBuffer replacement;
private Masker(ByteBuffer replacement)
{
this.replacement = replacement;
}
@Override
public ByteBuffer mask(ByteBuffer value)
{
return replacement;
}
return arguments.get(1);
}
/** @return a {@link FunctionFactory} to build new {@link ReplaceMaskingFunction}s. */

View File

@ -20,7 +20,6 @@ package org.apache.cassandra.cql3.selection;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.google.common.base.Objects;
@ -28,6 +27,7 @@ import com.google.common.collect.Iterables;
import org.apache.commons.lang3.text.StrBuilder;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.FunctionResolver;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
@ -55,6 +55,10 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
{
protected Selector deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
{
// The selector will only be deserialized on the replicas for GROUP BY queries. Due to that we
// can safely use the current protocol version.
final ProtocolVersion protocolVersion = ProtocolVersion.CURRENT;
FunctionName name = new FunctionName(in.readUTF(), in.readUTF());
int numberOfArguments = in.readUnsignedVInt32();
@ -74,19 +78,27 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
.collect(joining(", "))));
boolean isPartial = in.readBoolean();
// if the function is partial we need to retrieve the resolved arguments.
// The resolved arguments are encoded as follow: [vint]([vint][bytes])*
// The first vint contains the bitset used to determine which arguments were resolved.
// A bit equals to one meaning a resolved argument.
// The arguments are encoded as [vint][bytes] where the vint contains the size in bytes of the
// argument.
if (isPartial)
{
// We use a bitset to track the position of the unresolved arguments
int bitset = in.readUnsignedVInt32();
List<ByteBuffer> partialParameters = new ArrayList<>(numberOfArguments);
List<ByteBuffer> partialArguments = new ArrayList<>(numberOfArguments);
for (int i = 0; i < numberOfArguments; i++)
{
ByteBuffer parameter = ((bitset & 1) == 1) ? ByteBufferUtil.readWithVIntLength(in)
: Function.UNRESOLVED;
partialParameters.add(parameter);
boolean isArgumentResolved = getRightMostBit(bitset) == 1;
ByteBuffer argument = isArgumentResolved ? ByteBufferUtil.readWithVIntLength(in)
: Function.UNRESOLVED;
partialArguments.add(argument);
bitset >>= 1;
}
function = ((ScalarFunction) function).partialApplication(ProtocolVersion.CURRENT, partialParameters);
function = ((ScalarFunction) function).partialApplication(protocolVersion, partialArguments);
}
int numberOfRemainingArguments = in.readUnsignedVInt32();
@ -96,10 +108,22 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
argSelectors.add(Selector.serializer.deserialize(in, version, metadata));
}
return newFunctionSelector(function, argSelectors);
return newFunctionSelector(protocolVersion, function, argSelectors);
}
protected abstract Selector newFunctionSelector(Function function, List<Selector> argSelectors);
/**
* Returns the value of the right most bit.
* @param bitset the bitset
* @return the value of the right most bit
*/
private int getRightMostBit(int bitset)
{
return bitset & 1;
}
protected abstract Selector newFunctionSelector(ProtocolVersion version,
Function function,
List<Selector> argSelectors);
}
protected final T fun;
@ -108,7 +132,7 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
* The list used to pass the function arguments is recycled to avoid the cost of instantiating a new list
* with each function call.
*/
private final List<ByteBuffer> args;
private final Arguments args;
protected final List<Selector> argSelectors;
public static Factory newFactory(final Function fun, final SelectorFactories factories) throws InvalidRequestException
@ -154,7 +178,7 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
public Selector newInstance(QueryOptions options) throws InvalidRequestException
{
return fun.isAggregate() ? new AggregateFunctionSelector(fun, factories.newInstances(options))
return fun.isAggregate() ? new AggregateFunctionSelector(options.getProtocolVersion(), fun, factories.newInstances(options))
: createScalarSelector(options, (ScalarFunction) fun, factories.newInstances(options));
}
@ -179,21 +203,25 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
}
if (terminalCount == 0)
return new ScalarFunctionSelector(fun, argSelectors);
return new ScalarFunctionSelector(version, fun, argSelectors);
// All terminal, reduce to a simple value if the function is pure
if (terminalCount == argSelectors.size() && function.isPure())
return new TermSelector(function.execute(version, terminalArgs), function.returnType());
// We have some terminal arguments but not all, do a partial application
// We have some terminal arguments, do a partial application
ScalarFunction partialFunction = function.partialApplication(version, terminalArgs);
// If all the arguments are terminal and the function is pure we can reduce to a simple value.
if (terminalCount == argSelectors.size() && fun.isPure())
{
Arguments arguments = partialFunction.newArguments(version);
return new TermSelector(partialFunction.execute(arguments), partialFunction.returnType());
}
List<Selector> remainingSelectors = new ArrayList<>(argSelectors.size() - terminalCount);
for (Selector selector : argSelectors)
{
if (!selector.isTerminal())
remainingSelectors.add(selector);
}
return new ScalarFunctionSelector(partialFunction, remainingSelectors);
return new ScalarFunctionSelector(version, partialFunction, remainingSelectors);
}
public boolean isWritetimeSelectorFactory()
@ -226,12 +254,12 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
};
}
protected AbstractFunctionSelector(Kind kind, T fun, List<Selector> argSelectors)
protected AbstractFunctionSelector(Kind kind, ProtocolVersion version, T fun, List<Selector> argSelectors)
{
super(kind);
this.fun = fun;
this.argSelectors = argSelectors;
this.args = Arrays.asList(new ByteBuffer[argSelectors.size()]);
this.args = fun.newArguments(version);
}
@Override
@ -249,7 +277,7 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
args.set(i, value);
}
protected List<ByteBuffer> args()
protected Arguments args()
{
return args;
}
@ -311,14 +339,14 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
if (isPartial)
{
List<ByteBuffer> partialParameters = ((PartialScalarFunction) fun).getPartialParameters();
List<ByteBuffer> partialArguments = ((PartialScalarFunction) fun).getPartialArguments();
// We use a bitset to track the position of the unresolved arguments
size += TypeSizes.sizeofUnsignedVInt(computeBitSet(partialParameters));
size += TypeSizes.sizeofUnsignedVInt(computeBitSet(partialArguments));
for (int i = 0, m = partialParameters.size(); i < m; i++)
for (int i = 0, m = partialArguments.size(); i < m; i++)
{
ByteBuffer buffer = partialParameters.get(i);
ByteBuffer buffer = partialArguments.get(i);
if (buffer != Function.UNRESOLVED)
size += ByteBufferUtil.serializedSizeWithVIntLength(buffer);
}
@ -353,14 +381,14 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
if (isPartial)
{
List<ByteBuffer> partialParameters = ((PartialScalarFunction) fun).getPartialParameters();
List<ByteBuffer> partialArguments = ((PartialScalarFunction) fun).getPartialArguments();
// We use a bitset to track the position of the unresolved arguments
out.writeUnsignedVInt32(computeBitSet(partialParameters));
out.writeUnsignedVInt32(computeBitSet(partialArguments));
for (int i = 0, m = partialParameters.size(); i < m; i++)
for (int i = 0, m = partialArguments.size(); i < m; i++)
{
ByteBuffer buffer = partialParameters.get(i);
ByteBuffer buffer = partialArguments.get(i);
if (buffer != Function.UNRESOLVED)
ByteBufferUtil.writeWithVIntLength(buffer, out);
}
@ -372,13 +400,13 @@ abstract class AbstractFunctionSelector<T extends Function> extends Selector
serializer.serialize(argSelectors.get(i), out, version);
}
private int computeBitSet(List<ByteBuffer> partialParameters)
private int computeBitSet(List<ByteBuffer> partialArguments)
{
assert partialParameters.size() <= 32 : "cannot serialize partial function with more than 32 parameters";
assert partialArguments.size() <= 32 : "cannot serialize partial function with more than 32 arguments";
int bitset = 0;
for (int i = 0, m = partialParameters.size(); i < m; i++)
for (int i = 0, m = partialArguments.size(); i < m; i++)
{
if (partialParameters.get(i) != Function.UNRESOLVED)
if (partialArguments.get(i) != Function.UNRESOLVED)
bitset |= 1 << i;
}
return bitset;

View File

@ -27,12 +27,12 @@ import org.apache.cassandra.transport.ProtocolVersion;
final class AggregateFunctionSelector extends AbstractFunctionSelector<AggregateFunction>
{
protected static final SelectorDeserializer deserializer = new AbstractFunctionSelectorDeserializer()
static final SelectorDeserializer deserializer = new AbstractFunctionSelectorDeserializer()
{
@Override
protected Selector newFunctionSelector(Function function, List<Selector> argSelectors)
protected Selector newFunctionSelector(ProtocolVersion version, Function function, List<Selector> argSelectors)
{
return new AggregateFunctionSelector(function, argSelectors);
return new AggregateFunctionSelector(version, function, argSelectors);
}
};
@ -55,7 +55,7 @@ final class AggregateFunctionSelector extends AbstractFunctionSelector<Aggregate
setArg(i, s.getOutput(protocolVersion));
s.reset();
}
this.aggregate.addInput(protocolVersion, args());
aggregate.addInput(args());
}
public ByteBuffer getOutput(ProtocolVersion protocolVersion) throws InvalidRequestException
@ -68,9 +68,9 @@ final class AggregateFunctionSelector extends AbstractFunctionSelector<Aggregate
aggregate.reset();
}
AggregateFunctionSelector(Function fun, List<Selector> argSelectors) throws InvalidRequestException
AggregateFunctionSelector(ProtocolVersion version, Function fun, List<Selector> argSelectors) throws InvalidRequestException
{
super(Kind.AGGREGATE_FUNCTION_SELECTOR, (AggregateFunction) fun, argSelectors);
super(Kind.AGGREGATE_FUNCTION_SELECTOR, version, (AggregateFunction) fun, argSelectors);
this.aggregate = this.fun.newAggregate();
}

View File

@ -28,12 +28,12 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
final class ScalarFunctionSelector extends AbstractFunctionSelector<ScalarFunction>
{
protected static final SelectorDeserializer deserializer = new AbstractFunctionSelectorDeserializer()
static final SelectorDeserializer deserializer = new AbstractFunctionSelectorDeserializer()
{
@Override
protected Selector newFunctionSelector(Function function, List<Selector> argSelectors)
protected Selector newFunctionSelector(ProtocolVersion version, Function function, List<Selector> argSelectors)
{
return new ScalarFunctionSelector(function, argSelectors);
return new ScalarFunctionSelector(version, function, argSelectors);
}
};
@ -58,7 +58,7 @@ final class ScalarFunctionSelector extends AbstractFunctionSelector<ScalarFuncti
setArg(i, s.getOutput(protocolVersion));
s.reset();
}
return fun.execute(protocolVersion, args());
return fun.execute(args());
}
@Override
@ -69,8 +69,8 @@ final class ScalarFunctionSelector extends AbstractFunctionSelector<ScalarFuncti
argSelectors.get(i).validateForGroupBy();
}
ScalarFunctionSelector(Function fun, List<Selector> argSelectors)
ScalarFunctionSelector(ProtocolVersion version, Function fun, List<Selector> argSelectors)
{
super(Kind.SCALAR_FUNCTION_SELECTOR, (ScalarFunction) fun, argSelectors);
super(Kind.SCALAR_FUNCTION_SELECTOR, version, (ScalarFunction) fun, argSelectors);
}
}

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import com.google.common.base.Objects;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.functions.masking.ColumnMask;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
@ -46,7 +47,7 @@ public final class SimpleSelector extends Selector
ByteBuffer columnName = ByteBufferUtil.readWithVIntLength(in);
ColumnMetadata column = metadata.getColumn(columnName);
int idx = in.readInt();
return new SimpleSelector(column, idx, false);
return new SimpleSelector(column, idx, false, ProtocolVersion.CURRENT);
}
};
@ -86,7 +87,7 @@ public final class SimpleSelector extends Selector
@Override
public Selector newInstance(QueryOptions options)
{
return new SimpleSelector(column, idx, useForPostOrdering);
return new SimpleSelector(column, idx, useForPostOrdering, options.getProtocolVersion());
}
@Override
@ -119,7 +120,7 @@ public final class SimpleSelector extends Selector
public final ColumnMetadata column;
private final int idx;
private final boolean useForPostOrdering;
private final ColumnMask.Masker masker;
private ByteBuffer current;
private ColumnTimestamps writetimes;
private ColumnTimestamps ttls;
@ -146,14 +147,14 @@ public final class SimpleSelector extends Selector
ttls = input.getTtls(idx);
/*
We apply the column mask of the column unless:
We apply the column masker of the column unless:
- The column doesn't have a mask
- This selector is for a query with ORDER BY post-ordering, indicated by this.useForPostOrdering
- The input row is for a user with UNMASK permission, indicated by input.unmask()
- Dynamic data masking is globally disabled
*/
ColumnMask mask = useForPostOrdering || input.unmask() ? null : column.getMask();
ByteBuffer value = input.getValue(idx);
current = mask == null ? value : mask.mask(input.getProtocolVersion(), value);
current = masker == null || input.unmask() || !DatabaseDescriptor.getDynamicDataMaskingEnabled()
? value : masker.mask(value);
}
}
@ -196,12 +197,19 @@ public final class SimpleSelector extends Selector
return column.name.toString();
}
private SimpleSelector(ColumnMetadata column, int idx, boolean useForPostOrdering)
private SimpleSelector(ColumnMetadata column, int idx, boolean useForPostOrdering, ProtocolVersion version)
{
super(Kind.SIMPLE_SELECTOR);
this.column = column;
this.idx = idx;
this.useForPostOrdering = useForPostOrdering;
/*
We apply the column mask of the column unless:
- The column doesn't have a mask
- This selector is for a query with ORDER BY post-ordering
*/
this.masker = useForPostOrdering || column.getMask() == null
? null
: column.getMask().masker(version);
}
@Override

View File

@ -35,7 +35,6 @@ import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.ScalarFunction;
import org.apache.cassandra.cql3.functions.UDAggregate;
import org.apache.cassandra.cql3.functions.UDFunction;
import org.apache.cassandra.cql3.functions.UDHelper;
import org.apache.cassandra.cql3.functions.UserFunction;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.schema.UserFunctions.FunctionsDiff;
@ -181,7 +180,7 @@ public final class CreateAggregateStatement extends AlterSchemaStatement
String initialValueString = stateType.asCQL3Type().toCQLLiteral(initialValue, ProtocolVersion.CURRENT);
assert Objects.equal(initialValue, Terms.asBytes(keyspaceName, initialValueString, stateType));
if (Constants.NULL_LITERAL != rawInitialValue && UDHelper.isNullOrEmpty(stateType, initialValue))
if (Constants.NULL_LITERAL != rawInitialValue && isNullOrEmpty(stateType, initialValue))
throw ire("INITCOND must not be empty for all types except TEXT, ASCII, BLOB");
}
@ -228,6 +227,12 @@ public final class CreateAggregateStatement extends AlterSchemaStatement
return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.userFunctions.withAddedOrUpdated(aggregate)));
}
private static boolean isNullOrEmpty(AbstractType<?> type, ByteBuffer bb)
{
return bb == null ||
(bb.remaining() == 0 && type.isEmptyValueMeaningless());
}
SchemaChange schemaChangeEvent(KeyspacesDiff diff)
{
assert diff.altered.size() == 1;

View File

@ -22,6 +22,7 @@ import java.util.UUID;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Duration;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.UUIDSerializer;
@ -198,13 +199,13 @@ public abstract class AbstractTimeUUIDType<T> extends TemporalType<T>
}
@Override
public ByteBuffer addDuration(ByteBuffer temporal, ByteBuffer duration)
public ByteBuffer addDuration(Number temporal, Duration duration)
{
throw new UnsupportedOperationException();
}
@Override
public ByteBuffer substractDuration(ByteBuffer temporal, ByteBuffer duration)
public ByteBuffer substractDuration(Number temporal, Duration duration)
{
throw new UnsupportedOperationException();
}

View File

@ -32,6 +32,7 @@ import org.apache.cassandra.cql3.AssignmentTestable;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.ColumnSpecification;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.db.rows.Cell;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.io.util.DataInputPlus;
@ -268,6 +269,14 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>, Assignm
public abstract TypeSerializer<T> getSerializer();
/**
* @return the deserializer used to deserialize the function arguments of this type.
*/
public ArgumentDeserializer getArgumentDeserializer()
{
return new DefaultArgumentDeserializer(this);
}
/* convenience method */
public String getString(Collection<ByteBuffer> names)
{
@ -723,4 +732,26 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>, Assignm
{
throw new UnsupportedOperationException("There isn't a defined masked value for type " + asCQL3Type());
}
/**
* {@link ArgumentDeserializer} that uses the type deserialization.
*/
protected static class DefaultArgumentDeserializer implements ArgumentDeserializer
{
private final AbstractType<?> type;
public DefaultArgumentDeserializer(AbstractType<?> type)
{
this.type = type;
}
@Override
public Object deserialize(ProtocolVersion protocolVersion, ByteBuffer buffer)
{
if (buffer == null || (!buffer.hasRemaining() && type.isEmptyValueMeaningless()))
return null;
return type.compose(buffer);
}
}
}

View File

@ -28,6 +28,7 @@ import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.AsciiSerializer;
@ -38,6 +39,7 @@ import org.apache.cassandra.utils.JsonUtils;
public class AsciiType extends StringType
{
public static final AsciiType instance = new AsciiType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose("****");
AsciiType() {super(ComparisonType.BYTE_ORDER);} // singleton
@ -104,6 +106,12 @@ public class AsciiType extends StringType
return AsciiSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
@Override
public ByteBuffer getMaskedValue()
{

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.BooleanSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -32,7 +33,7 @@ import org.apache.cassandra.utils.bytecomparable.ByteSource;
public class BooleanType extends AbstractType<Boolean>
{
public static final BooleanType instance = new BooleanType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose(false);
BooleanType() {super(ComparisonType.CUSTOM);} // singleton
@ -113,6 +114,12 @@ public class BooleanType extends AbstractType<Boolean>
return BooleanSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
@Override
public int valueLengthIfFixed()
{

View File

@ -19,9 +19,12 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import org.apache.commons.lang3.mutable.MutableByte;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.ByteSerializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.TypeSerializer;
@ -109,81 +112,81 @@ public class ByteType extends NumberType<Byte>
}
@Override
public byte toByte(ByteBuffer value)
public ArgumentDeserializer getArgumentDeserializer()
{
return ByteBufferUtil.toByte(value);
return new NumberArgumentDeserializer<MutableByte>(new MutableByte())
{
@Override
protected void setMutableValue(MutableByte mutable, ByteBuffer buffer)
{
mutable.setValue(ByteBufferUtil.toByte(buffer));
}
};
}
@Override
public short toShort(ByteBuffer value)
public ByteBuffer add(Number left, Number right)
{
return toByte(value);
return ByteBufferUtil.bytes((byte) (left.byteValue() + right.byteValue()));
}
@Override
protected int toInt(ByteBuffer value)
public ByteBuffer substract(Number left, Number right)
{
return toByte(value);
}
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((byte) (leftType.toByte(left) + rightType.toByte(right)));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((byte) (leftType.toByte(left) - rightType.toByte(right)));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((byte) (leftType.toByte(left) * rightType.toByte(right)));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((byte) (leftType.toByte(left) / rightType.toByte(right)));
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((byte) (leftType.toByte(left) % rightType.toByte(right)));
}
public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes((byte) -toByte(input));
return ByteBufferUtil.bytes((byte) (left.byteValue() - right.byteValue()));
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer multiply(Number left, Number right)
{
return ByteBufferUtil.bytes((byte) Math.abs(toByte(input)));
return ByteBufferUtil.bytes((byte) (left.byteValue() * right.byteValue()));
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer divide(Number left, Number right)
{
return ByteBufferUtil.bytes((byte) Math.exp(toByte(input)));
return ByteBufferUtil.bytes((byte) (left.byteValue() / right.byteValue()));
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer mod(Number left, Number right)
{
return ByteBufferUtil.bytes((byte) Math.log(toByte(input)));
return ByteBufferUtil.bytes((byte) (left.byteValue() % right.byteValue()));
}
public ByteBuffer negate(Number input)
{
return ByteBufferUtil.bytes((byte) -input.byteValue());
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer abs(Number input)
{
return ByteBufferUtil.bytes((byte) Math.log10(toByte(input)));
return ByteBufferUtil.bytes((byte) Math.abs(input.byteValue()));
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer exp(Number input)
{
return ByteBufferUtil.clone(input);
return ByteBufferUtil.bytes((byte) Math.exp(input.byteValue()));
}
@Override
public ByteBuffer log(Number input)
{
return ByteBufferUtil.bytes((byte) Math.log(input.byteValue()));
}
@Override
public ByteBuffer log10(Number input)
{
return ByteBufferUtil.bytes((byte) Math.log10(input.byteValue()));
}
@Override
public ByteBuffer round(Number input)
{
return decompose(input.byteValue());
}
@Override

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.BytesSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -97,6 +98,12 @@ public class BytesType extends AbstractType<ByteBuffer>
return BytesSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ArgumentDeserializer.NOOP_DESERIALIZER;
}
@Override
public ByteBuffer getMaskedValue()
{

View File

@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.serializers.CounterSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -96,69 +97,74 @@ public class CounterColumnType extends NumberType<Long>
}
@Override
protected long toLong(ByteBuffer value)
public ArgumentDeserializer getArgumentDeserializer()
{
return ByteBufferUtil.toLong(value);
}
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) + rightType.toLong(right));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) - rightType.toLong(right));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) * rightType.toLong(right));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) / rightType.toLong(right));
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) % rightType.toLong(right));
}
public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes(-toLong(input));
return LongType.instance.getArgumentDeserializer();
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer add(Number left, Number right)
{
return ByteBufferUtil.bytes(Math.abs(toLong(input)));
return ByteBufferUtil.bytes(left.longValue() + right.longValue());
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer substract(Number left, Number right)
{
return ByteBufferUtil.bytes((long) Math.exp(toLong(input)));
return ByteBufferUtil.bytes(left.longValue() - right.longValue());
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer multiply(Number left, Number right)
{
return ByteBufferUtil.bytes((long) Math.log(toLong(input)));
return ByteBufferUtil.bytes(left.longValue() * right.longValue());
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer divide(Number left, Number right)
{
return ByteBufferUtil.bytes((long) Math.log10(toLong(input)));
return ByteBufferUtil.bytes(left.longValue() / right.longValue());
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer mod(Number left, Number right)
{
return ByteBufferUtil.clone(input);
return ByteBufferUtil.bytes(left.longValue() % right.longValue());
}
public ByteBuffer negate(Number input)
{
return ByteBufferUtil.bytes(-input.longValue());
}
@Override
public ByteBuffer abs(Number input)
{
return ByteBufferUtil.bytes(Math.abs(input.longValue()));
}
@Override
public ByteBuffer exp(Number input)
{
return ByteBufferUtil.bytes((long) Math.exp(input.longValue()));
}
@Override
public ByteBuffer log(Number input)
{
return ByteBufferUtil.bytes((long) Math.log(input.longValue()));
}
@Override
public ByteBuffer log10(Number input)
{
return ByteBufferUtil.bytes((long) Math.log10(input.longValue()));
}
@Override
public ByteBuffer round(Number input)
{
return decompose(input.longValue());
}
@Override

View File

@ -26,6 +26,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.TimestampSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -45,7 +46,7 @@ public class DateType extends AbstractType<Date>
private static final Logger logger = LoggerFactory.getLogger(DateType.class);
public static final DateType instance = new DateType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose(new Date(0));
DateType() {super(ComparisonType.BYTE_ORDER);} // singleton
@ -136,6 +137,12 @@ public class DateType extends AbstractType<Date>
return TimestampSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
@Override
public int valueLengthIfFixed()
{

View File

@ -29,6 +29,7 @@ import com.google.common.primitives.Ints;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.DecimalSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -43,7 +44,10 @@ public class DecimalType extends NumberType<BigDecimal>
{
public static final DecimalType instance = new DecimalType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose(BigDecimal.ZERO);
private static final int MIN_SCALE = 32;
private static final int MIN_SIGNIFICANT_DIGITS = MIN_SCALE;
private static final int MAX_SCALE = 1000;
@ -316,60 +320,59 @@ public class DecimalType extends NumberType<BigDecimal>
}
@Override
protected int toInt(ByteBuffer value)
public ArgumentDeserializer getArgumentDeserializer()
{
throw new UnsupportedOperationException();
return ARGUMENT_DESERIALIZER;
}
/**
* Converts the specified number into a {@link BigDecimal}.
*
* @param number the value to convert
* @return the converted value
*/
protected BigDecimal toBigDecimal(Number number)
{
if (number instanceof BigDecimal)
return (BigDecimal) number;
if (number instanceof BigInteger)
return new BigDecimal((BigInteger) number);
double d = number.doubleValue();
if (Double.isNaN(d))
throw new NumberFormatException("A NaN cannot be converted into a decimal");
if (Double.isInfinite(d))
throw new NumberFormatException("An infinite number cannot be converted into a decimal");
return BigDecimal.valueOf(d);
}
@Override
protected float toFloat(ByteBuffer value)
public ByteBuffer add(Number left, Number right)
{
throw new UnsupportedOperationException();
return decompose(toBigDecimal(left).add(toBigDecimal(right), MAX_PRECISION));
}
@Override
protected long toLong(ByteBuffer value)
public ByteBuffer substract(Number left, Number right)
{
throw new UnsupportedOperationException();
return decompose(toBigDecimal(left).subtract(toBigDecimal(right), MAX_PRECISION));
}
@Override
protected double toDouble(ByteBuffer value)
public ByteBuffer multiply(Number left, Number right)
{
throw new UnsupportedOperationException();
return decompose(toBigDecimal(left).multiply(toBigDecimal(right), MAX_PRECISION));
}
@Override
protected BigInteger toBigInteger(ByteBuffer value)
public ByteBuffer divide(Number left, Number right)
{
throw new UnsupportedOperationException();
}
@Override
protected BigDecimal toBigDecimal(ByteBuffer value)
{
return compose(value);
}
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return decompose(leftType.toBigDecimal(left).add(rightType.toBigDecimal(right), MAX_PRECISION));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return decompose(leftType.toBigDecimal(left).subtract(rightType.toBigDecimal(right), MAX_PRECISION));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return decompose(leftType.toBigDecimal(left).multiply(rightType.toBigDecimal(right), MAX_PRECISION));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
BigDecimal leftOperand = leftType.toBigDecimal(left);
BigDecimal rightOperand = rightType.toBigDecimal(right);
BigDecimal leftOperand = toBigDecimal(left);
BigDecimal rightOperand = toBigDecimal(right);
// Predict position of first significant digit in the quotient.
// Note: it is possible to improve prediction accuracy by comparing first significant digits in operands
@ -385,24 +388,26 @@ public class DecimalType extends NumberType<BigDecimal>
return decompose(leftOperand.divide(rightOperand, scale, RoundingMode.HALF_UP).stripTrailingZeros());
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
@Override
public ByteBuffer mod(Number left, Number right)
{
return decompose(leftType.toBigDecimal(left).remainder(rightType.toBigDecimal(right)));
return decompose(toBigDecimal(left).remainder(toBigDecimal(right)));
}
public ByteBuffer negate(ByteBuffer input)
@Override
public ByteBuffer negate(Number input)
{
return decompose(toBigDecimal(input).negate());
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer abs(Number input)
{
return decompose(toBigDecimal(input).abs());
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer exp(Number input)
{
return decompose(exp(toBigDecimal(input)));
}
@ -416,7 +421,7 @@ public class DecimalType extends NumberType<BigDecimal>
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer log(Number input)
{
return decompose(log(toBigDecimal(input)));
}
@ -431,7 +436,7 @@ public class DecimalType extends NumberType<BigDecimal>
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer log10(Number input)
{
return decompose(log10(toBigDecimal(input)));
}
@ -446,7 +451,7 @@ public class DecimalType extends NumberType<BigDecimal>
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer round(Number input)
{
return DecimalType.instance.decompose(
toBigDecimal(input).setScale(0, RoundingMode.HALF_UP));

View File

@ -19,9 +19,12 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.DoubleSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -122,6 +125,19 @@ public class DoubleType extends NumberType<Double>
return DoubleSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return new NumberArgumentDeserializer<MutableDouble>(new MutableDouble())
{
@Override
protected void setMutableValue(MutableDouble mutable, ByteBuffer buffer)
{
mutable.setValue(ByteBufferUtil.toDouble(buffer));
}
};
}
@Override
public int valueLengthIfFixed()
{
@ -129,87 +145,69 @@ public class DoubleType extends NumberType<Double>
}
@Override
protected int toInt(ByteBuffer value)
public ByteBuffer add(Number left, Number right)
{
throw new UnsupportedOperationException();
return ByteBufferUtil.bytes(left.doubleValue() + right.doubleValue());
}
@Override
protected float toFloat(ByteBuffer value)
public ByteBuffer substract(Number left, Number right)
{
throw new UnsupportedOperationException();
return ByteBufferUtil.bytes(left.doubleValue() - right.doubleValue());
}
@Override
protected long toLong(ByteBuffer value)
public ByteBuffer multiply(Number left, Number right)
{
throw new UnsupportedOperationException();
return ByteBufferUtil.bytes(left.doubleValue() * right.doubleValue());
}
@Override
protected double toDouble(ByteBuffer value)
public ByteBuffer divide(Number left, Number right)
{
return ByteBufferUtil.toDouble(value);
}
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toDouble(left) + rightType.toDouble(right));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toDouble(left) - rightType.toDouble(right));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toDouble(left) * rightType.toDouble(right));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toDouble(left) / rightType.toDouble(right));
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toDouble(left) % rightType.toDouble(right));
}
public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes(-toDouble(input));
return ByteBufferUtil.bytes(left.doubleValue() / right.doubleValue());
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer mod(Number left, Number right)
{
return ByteBufferUtil.bytes(Math.abs(toDouble(input)));
return ByteBufferUtil.bytes(left.doubleValue() % right.doubleValue());
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer negate(Number input)
{
return ByteBufferUtil.bytes(Math.exp(toDouble(input)));
return ByteBufferUtil.bytes(-input.doubleValue());
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer abs(Number input)
{
return ByteBufferUtil.bytes(Math.log(toDouble(input)));
return ByteBufferUtil.bytes(Math.abs(input.doubleValue()));
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer exp(Number input)
{
return ByteBufferUtil.bytes(Math.log10(toDouble(input)));
return ByteBufferUtil.bytes(Math.exp(input.doubleValue()));
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer log(Number input)
{
return ByteBufferUtil.bytes((double) Math.round(toDouble(input)));
return ByteBufferUtil.bytes(Math.log(input.doubleValue()));
}
@Override
public ByteBuffer log10(Number input)
{
return ByteBufferUtil.bytes(Math.log10(input.doubleValue()));
}
@Override
public ByteBuffer round(Number input)
{
return ByteBufferUtil.bytes((double) Math.round(input.doubleValue()));
}
@Override

View File

@ -23,6 +23,7 @@ import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Duration;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.DurationSerializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.TypeSerializer;
@ -36,6 +37,8 @@ public class DurationType extends AbstractType<Duration>
{
public static final DurationType instance = new DurationType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose(Duration.newInstance(0, 0, 0));
DurationType()
@ -77,6 +80,12 @@ public class DurationType extends AbstractType<Duration>
return DurationSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
@Override
public CQL3Type asCQL3Type()
{

View File

@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.serializers.EmptySerializer;
@ -129,6 +130,12 @@ public class EmptyType extends AbstractType<Void>
return EmptySerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
throw new UnsupportedOperationException();
}
@Override
public int valueLengthIfFixed()
{

View File

@ -19,9 +19,12 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import org.apache.commons.lang3.mutable.MutableFloat;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.FloatSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -123,6 +126,19 @@ public class FloatType extends NumberType<Float>
return FloatSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return new NumberArgumentDeserializer<MutableFloat>(new MutableFloat())
{
@Override
protected void setMutableValue(MutableFloat mutable, ByteBuffer buffer)
{
mutable.setValue(ByteBufferUtil.toFloat(buffer));
}
};
}
@Override
public int valueLengthIfFixed()
{
@ -130,81 +146,69 @@ public class FloatType extends NumberType<Float>
}
@Override
protected int toInt(ByteBuffer value)
public ByteBuffer add(Number left, Number right)
{
throw new UnsupportedOperationException();
return ByteBufferUtil.bytes(left.floatValue() + right.floatValue());
}
@Override
protected float toFloat(ByteBuffer value)
public ByteBuffer substract(Number left, Number right)
{
return ByteBufferUtil.toFloat(value);
return ByteBufferUtil.bytes(left.floatValue() - right.floatValue());
}
@Override
protected double toDouble(ByteBuffer value)
public ByteBuffer multiply(Number left, Number right)
{
return toFloat(value);
}
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toFloat(left) + rightType.toFloat(right));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toFloat(left) - rightType.toFloat(right));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toFloat(left) * rightType.toFloat(right));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toFloat(left) / rightType.toFloat(right));
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toFloat(left) % rightType.toFloat(right));
}
public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes(-toFloat(input));
return ByteBufferUtil.bytes(left.floatValue() * right.floatValue());
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer divide(Number left, Number right)
{
return ByteBufferUtil.bytes(Math.abs(toFloat(input)));
return ByteBufferUtil.bytes(left.floatValue() / right.floatValue());
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer mod(Number left, Number right)
{
return ByteBufferUtil.bytes((float) Math.exp(toFloat(input)));
return ByteBufferUtil.bytes(left.floatValue() % right.floatValue());
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer negate(Number input)
{
return ByteBufferUtil.bytes((float) Math.log(toFloat(input)));
return ByteBufferUtil.bytes(-input.floatValue());
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer abs(Number input)
{
return ByteBufferUtil.bytes((float) Math.log10(toFloat(input)));
return ByteBufferUtil.bytes(Math.abs(input.floatValue()));
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer exp(Number input)
{
return ByteBufferUtil.bytes((float) Math.round(toFloat(input)));
return ByteBufferUtil.bytes((float) Math.exp(input.floatValue()));
}
@Override
public ByteBuffer log(Number input)
{
return ByteBufferUtil.bytes((float) Math.log(input.floatValue()));
}
@Override
public ByteBuffer log10(Number input)
{
return ByteBufferUtil.bytes((float) Math.log10(input.floatValue()));
}
@Override
public ByteBuffer round(Number input)
{
return ByteBufferUtil.bytes((float) Math.round(input.floatValue()));
}
@Override

View File

@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.serializers.TypeSerializer;
@ -71,4 +72,10 @@ public class FrozenType extends AbstractType<Void>
{
throw new UnsupportedOperationException();
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
throw new UnsupportedOperationException();
}
}

View File

@ -24,6 +24,7 @@ import java.nio.ByteBuffer;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.InetAddressSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -34,6 +35,8 @@ public class InetAddressType extends AbstractType<InetAddress>
{
public static final InetAddressType instance = new InetAddressType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose(new InetSocketAddress(0).getAddress());
InetAddressType() {super(ComparisonType.BYTE_ORDER);} // singleton
@ -98,6 +101,12 @@ public class InetAddressType extends AbstractType<InetAddress>
return InetAddressSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
@Override
public ByteBuffer getMaskedValue()
{

View File

@ -20,9 +20,12 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import java.util.Objects;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.Int32Serializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.transport.ProtocolVersion;
@ -129,6 +132,19 @@ public class Int32Type extends NumberType<Integer>
return Int32Serializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return new NumberArgumentDeserializer<MutableInt>(new MutableInt())
{
@Override
protected void setMutableValue(MutableInt mutable, ByteBuffer buffer)
{
mutable.setValue(ByteBufferUtil.toInt(buffer));
}
};
}
@Override
public int valueLengthIfFixed()
{
@ -136,75 +152,69 @@ public class Int32Type extends NumberType<Integer>
}
@Override
protected int toInt(ByteBuffer value)
public ByteBuffer add(Number left, Number right)
{
return ByteBufferUtil.toInt(value);
return ByteBufferUtil.bytes(left.intValue() + right.intValue());
}
@Override
protected float toFloat(ByteBuffer value)
public ByteBuffer substract(Number left, Number right)
{
return toInt(value);
}
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toInt(left) + rightType.toInt(right));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toInt(left) - rightType.toInt(right));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toInt(left) * rightType.toInt(right));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toInt(left) / rightType.toInt(right));
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toInt(left) % rightType.toInt(right));
}
public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes(-toInt(input));
return ByteBufferUtil.bytes(left.intValue() - right.intValue());
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer multiply(Number left, Number right)
{
return ByteBufferUtil.bytes(Math.abs(toInt(input)));
return ByteBufferUtil.bytes(left.intValue() * right.intValue());
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer divide(Number left, Number right)
{
return ByteBufferUtil.bytes((int) Math.exp(toInt(input)));
return ByteBufferUtil.bytes(left.intValue() / right.intValue());
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer mod(Number left, Number right)
{
return ByteBufferUtil.bytes((int) Math.log(toInt(input)));
return ByteBufferUtil.bytes(left.intValue() % right.intValue());
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer negate(Number input)
{
return ByteBufferUtil.bytes((int) Math.log10(toInt(input)));
return ByteBufferUtil.bytes(-input.intValue());
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer abs(Number input)
{
return ByteBufferUtil.clone(input);
return ByteBufferUtil.bytes(Math.abs(input.intValue()));
}
@Override
public ByteBuffer exp(Number input)
{
return ByteBufferUtil.bytes((int) Math.exp(input.intValue()));
}
@Override
public ByteBuffer log(Number input)
{
return ByteBufferUtil.bytes((int) Math.log(input.intValue()));
}
@Override
public ByteBuffer log10(Number input)
{
return ByteBufferUtil.bytes((int) Math.log10(input.intValue()));
}
@Override
public ByteBuffer round(Number input)
{
return ByteBufferUtil.bytes(input.intValue());
}
@Override

View File

@ -25,6 +25,7 @@ import java.util.Objects;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.IntegerSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -38,6 +39,8 @@ public final class IntegerType extends NumberType<BigInteger>
{
public static final IntegerType instance = new IntegerType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose(BigInteger.ZERO);
// Constants or escaping values needed to encode/decode variable-length integers in our custom byte-ordered
@ -497,79 +500,57 @@ public final class IntegerType extends NumberType<BigInteger>
}
@Override
protected int toInt(ByteBuffer value)
public ArgumentDeserializer getArgumentDeserializer()
{
throw new UnsupportedOperationException();
return ARGUMENT_DESERIALIZER;
}
@Override
protected float toFloat(ByteBuffer value)
private BigInteger toBigInteger(Number number)
{
throw new UnsupportedOperationException();
if (number instanceof BigInteger)
return (BigInteger) number;
return BigInteger.valueOf(number.longValue());
}
@Override
protected long toLong(ByteBuffer value)
public ByteBuffer add(Number left, Number right)
{
throw new UnsupportedOperationException();
return decompose(toBigInteger(left).add(toBigInteger(right)));
}
@Override
protected double toDouble(ByteBuffer value)
public ByteBuffer substract(Number left, Number right)
{
throw new UnsupportedOperationException();
return decompose(toBigInteger(left).subtract(toBigInteger(right)));
}
@Override
protected BigInteger toBigInteger(ByteBuffer value)
public ByteBuffer multiply(Number left, Number right)
{
return compose(value);
return decompose(toBigInteger(left).multiply(toBigInteger(right)));
}
@Override
protected BigDecimal toBigDecimal(ByteBuffer value)
public ByteBuffer divide(Number left, Number right)
{
return new BigDecimal(compose(value));
return decompose(toBigInteger(left).divide(toBigInteger(right)));
}
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
public ByteBuffer mod(Number left, Number right)
{
return decompose(leftType.toBigInteger(left).add(rightType.toBigInteger(right)));
return decompose(toBigInteger(left).remainder(toBigInteger(right)));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return decompose(leftType.toBigInteger(left).subtract(rightType.toBigInteger(right)));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return decompose(leftType.toBigInteger(left).multiply(rightType.toBigInteger(right)));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return decompose(leftType.toBigInteger(left).divide(rightType.toBigInteger(right)));
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return decompose(leftType.toBigInteger(left).remainder(rightType.toBigInteger(right)));
}
public ByteBuffer negate(ByteBuffer input)
public ByteBuffer negate(Number input)
{
return decompose(toBigInteger(input).negate());
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer abs(Number input)
{
return decompose(toBigInteger(input).abs());
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer exp(Number input)
{
BigInteger bi = toBigInteger(input);
BigDecimal bd = new BigDecimal(bi);
@ -579,7 +560,7 @@ public final class IntegerType extends NumberType<BigInteger>
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer log(Number input)
{
BigInteger bi = toBigInteger(input);
if (bi.compareTo(BigInteger.ZERO) <= 0) throw new ArithmeticException("Natural log of number zero or less");
@ -590,7 +571,7 @@ public final class IntegerType extends NumberType<BigInteger>
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer log10(Number input)
{
BigInteger bi = toBigInteger(input);
if (bi.compareTo(BigInteger.ZERO) <= 0) throw new ArithmeticException("Log10 of number zero or less");
@ -601,9 +582,9 @@ public final class IntegerType extends NumberType<BigInteger>
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer round(Number input)
{
return ByteBufferUtil.clone(input);
return decompose(toBigInteger(input));
}
@Override

View File

@ -22,6 +22,7 @@ import java.util.UUID;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.UUIDSerializer;
@ -34,6 +35,8 @@ public class LexicalUUIDType extends AbstractType<UUID>
{
public static final LexicalUUIDType instance = new LexicalUUIDType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose(UUID.fromString("00000000-0000-0000-0000-000000000000"));
LexicalUUIDType()
@ -123,11 +126,18 @@ public class LexicalUUIDType extends AbstractType<UUID>
}
}
@Override
public TypeSerializer<UUID> getSerializer()
{
return UUIDSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
@Override
public int valueLengthIfFixed()
{

View File

@ -20,9 +20,12 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import java.util.Objects;
import org.apache.commons.lang3.mutable.MutableLong;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.LongSerializer;
import org.apache.cassandra.serializers.MarshalException;
@ -147,6 +150,19 @@ public class LongType extends NumberType<Long>
return LongSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return new NumberArgumentDeserializer<MutableLong>(new MutableLong())
{
@Override
protected void setMutableValue(MutableLong mutable, ByteBuffer buffer)
{
mutable.setValue(ByteBufferUtil.toLong(buffer));
}
};
}
@Override
public int valueLengthIfFixed()
{
@ -154,81 +170,69 @@ public class LongType extends NumberType<Long>
}
@Override
protected int toInt(ByteBuffer value)
public ByteBuffer add(Number left, Number right)
{
throw new UnsupportedOperationException();
return ByteBufferUtil.bytes(left.longValue() + right.longValue());
}
@Override
protected float toFloat(ByteBuffer value)
public ByteBuffer substract(Number left, Number right)
{
throw new UnsupportedOperationException();
return ByteBufferUtil.bytes(left.longValue() - right.longValue());
}
@Override
protected long toLong(ByteBuffer value)
public ByteBuffer multiply(Number left, Number right)
{
return ByteBufferUtil.toLong(value);
}
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) + rightType.toLong(right));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) - rightType.toLong(right));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) * rightType.toLong(right));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) / rightType.toLong(right));
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes(leftType.toLong(left) % rightType.toLong(right));
}
public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes(-toLong(input));
return ByteBufferUtil.bytes(left.longValue() * right.longValue());
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer divide(Number left, Number right)
{
return ByteBufferUtil.bytes(Math.abs(toLong(input)));
return ByteBufferUtil.bytes(left.longValue() / right.longValue());
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer mod(Number left, Number right)
{
return ByteBufferUtil.bytes((long) Math.exp(toLong(input)));
return ByteBufferUtil.bytes(left.longValue() % right.longValue());
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer negate(Number input)
{
return ByteBufferUtil.bytes((long) Math.log(toLong(input)));
return ByteBufferUtil.bytes(-input.longValue());
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer abs(Number input)
{
return ByteBufferUtil.bytes((long) Math.log10(toLong(input)));
return ByteBufferUtil.bytes(Math.abs(input.longValue()));
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer exp(Number input)
{
return ByteBufferUtil.clone(input);
return ByteBufferUtil.bytes((long) Math.exp(input.longValue()));
}
@Override
public ByteBuffer log(Number input)
{
return ByteBufferUtil.bytes((long) Math.log(input.longValue()));
}
@Override
public ByteBuffer log10(Number input)
{
return ByteBufferUtil.bytes((long) Math.log10(input.longValue()));
}
@Override
public ByteBuffer round(Number input)
{
return decompose(input.longValue());
}
@Override

View File

@ -17,10 +17,13 @@
*/
package org.apache.cassandra.db.marshal;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import org.apache.commons.lang3.mutable.Mutable;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* Base type for the numeric types.
*/
@ -40,178 +43,50 @@ public abstract class NumberType<T extends Number> extends AbstractType<T>
return false;
}
/**
* Converts the specified value into a <code>BigInteger</code> if allowed.
*
* @param value the value to convert
* @return the converted value
* @throws UnsupportedOperationException if the value cannot be converted without losing precision
*/
protected BigInteger toBigInteger(ByteBuffer value)
{
return BigInteger.valueOf(toLong(value));
}
/**
* Converts the specified value into a <code>BigDecimal</code>.
*
* @param value the value to convert
* @return the converted value
*/
protected BigDecimal toBigDecimal(ByteBuffer value)
{
double d = toDouble(value);
if (Double.isNaN(d))
throw new NumberFormatException("A NaN cannot be converted into a decimal");
if (Double.isInfinite(d))
throw new NumberFormatException("An infinite number cannot be converted into a decimal");
return BigDecimal.valueOf(d);
}
/**
* Converts the specified value into a <code>byte</code> if allowed.
*
* @param value the value to convert
* @return the converted value
* @throws UnsupportedOperationException if the value cannot be converted without losing precision
*/
protected byte toByte(ByteBuffer value)
{
throw new UnsupportedOperationException();
}
/**
* Converts the specified value into a <code>short</code> if allowed.
*
* @param value the value to convert
* @return the converted value
* @throws UnsupportedOperationException if the value cannot be converted without losing precision
*/
protected short toShort(ByteBuffer value)
{
throw new UnsupportedOperationException();
}
/**
* Converts the specified value into an <code>int</code> if allowed.
*
* @param value the value to convert
* @return the converted value
* @throws UnsupportedOperationException if the value cannot be converted without losing precision
*/
protected int toInt(ByteBuffer value)
{
throw new UnsupportedOperationException();
}
/**
* Converts the specified value into a <code>long</code> if allowed.
*
* @param value the value to convert
* @return the converted value
* @throws UnsupportedOperationException if the value cannot be converted without losing precision
*/
protected long toLong(ByteBuffer value)
{
return toInt(value);
}
/**
* Converts the specified value into a <code>float</code> if allowed.
*
* @param value the value to convert
* @return the converted value
* @throws UnsupportedOperationException if the value cannot be converted without losing precision
*/
protected float toFloat(ByteBuffer value)
{
return toInt(value);
}
/**
* Converts the specified value into a <code>double</code> if allowed.
*
* @param value the value to convert
* @return the converted value
* @throws UnsupportedOperationException if the value cannot be converted without losing precision
*/
protected double toDouble(ByteBuffer value)
{
return toLong(value);
}
/**
* Adds the left argument to the right one.
*
* @param leftType the type associated to the left argument
* @param left the left argument
* @param rightType the type associated to the right argument
* @param right the right argument
* @return the addition result
*/
public abstract ByteBuffer add(NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right);
public abstract ByteBuffer add(Number left, Number right);
/**
* Substracts the left argument from the right one.
*
* @param leftType the type associated to the left argument
* @param left the left argument
* @param rightType the type associated to the right argument
* @param right the right argument
* @return the substraction result
*/
public abstract ByteBuffer substract(NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right);
public abstract ByteBuffer substract(Number left, Number right);
/**
* Multiplies the left argument with the right one.
*
* @param leftType the type associated to the left argument
* @param left the left argument
* @param rightType the type associated to the right argument
* @param right the right argument
* @return the multiplication result
*/
public abstract ByteBuffer multiply(NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right);
public abstract ByteBuffer multiply(Number left, Number right);
/**
* Divides the left argument by the right one.
*
* @param leftType the type associated to the left argument
* @param left the left argument
* @param rightType the type associated to the right argument
* @param right the right argument
* @return the division result
*/
public abstract ByteBuffer divide(NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right);
public abstract ByteBuffer divide(Number left, Number right);
/**
* Return the remainder.
*
* @param leftType the type associated to the left argument
* @param left the left argument
* @param rightType the type associated to the right argument
* @param right the right argument
* @return the remainder
*/
public abstract ByteBuffer mod(NumberType<?> leftType,
ByteBuffer left,
NumberType<?> rightType,
ByteBuffer right);
public abstract ByteBuffer mod(Number left, Number right);
/**
* Negates the argument.
@ -219,7 +94,7 @@ public abstract class NumberType<T extends Number> extends AbstractType<T>
* @param input the argument to negate
* @return the negated argument
*/
public abstract ByteBuffer negate(ByteBuffer input);
public abstract ByteBuffer negate(Number input);
/**
* Takes the absolute value of the argument.
@ -228,7 +103,7 @@ public abstract class NumberType<T extends Number> extends AbstractType<T>
* @return a ByteBuffer containing the absolute value of the argument. The type of the contents of the ByteBuffer
* will match that of the input.
*/
public abstract ByteBuffer abs(ByteBuffer input);
public abstract ByteBuffer abs(Number input);
/**
* Raises e to the power of the argument.
@ -238,7 +113,7 @@ public abstract class NumberType<T extends Number> extends AbstractType<T>
* argument. The type of the contents of the ByteBuffer will be a double, unless the input is a DecimalType or
* IntegerType, in which the ByteBuffer will contain a DecimalType.
*/
public abstract ByteBuffer exp(ByteBuffer input);
public abstract ByteBuffer exp(Number input);
/**
* Takes the natural logrithm of the argument.
@ -248,7 +123,7 @@ public abstract class NumberType<T extends Number> extends AbstractType<T>
* will be a double, unless the input is a DecimalType or IntegerType, in which the ByteBuffer will contain a
* DecimalType.
*/
public abstract ByteBuffer log(ByteBuffer input);
public abstract ByteBuffer log(Number input);
/**
* Takes the log base 10 of the arguement.
@ -258,7 +133,7 @@ public abstract class NumberType<T extends Number> extends AbstractType<T>
* will be a double, unless the input is a DecimalType or IntegerType, in which the ByteBuffer will contain a
* DecimalType.
*/
public abstract ByteBuffer log10(ByteBuffer input);
public abstract ByteBuffer log10(Number input);
/**
* Rounds the argument to the nearest whole number.
@ -268,5 +143,39 @@ public abstract class NumberType<T extends Number> extends AbstractType<T>
* a copy of the input. If the input is a float, the ByteBuffer will contain an int32. If the input is a double, the
* output will contain a long. If the input is a DecimalType, the output will contain an IntegerType.
*/
public abstract ByteBuffer round(ByteBuffer input);
public abstract ByteBuffer round(Number input);
/**
* Base class for numeric type {@link ArgumentDeserializer}.
*
* <p>This class uses and returns a mutable wrapper instead of the Java immutable primitive wrapper. This wrapper
* is being reused between each call to minimize the amount of objects instantiated.</p>
*
* @param <M> The Mutable wrapper type
*/
protected abstract static class NumberArgumentDeserializer<M extends Mutable<Number>> implements ArgumentDeserializer
{
protected final M wrapper;
public NumberArgumentDeserializer(M wrapper)
{
this.wrapper = wrapper;
}
@Override
public Object deserialize(ProtocolVersion protocolVersion, ByteBuffer buffer)
{
if (buffer == null || !buffer.hasRemaining())
return null;
setMutableValue(wrapper, buffer);
return wrapper;
}
/**
* Sets the value of the mutable.
* @param buffer the serialized value.
*/
protected abstract void setMutableValue(M mutable, ByteBuffer buffer);
}
}

View File

@ -22,6 +22,7 @@ import java.util.Iterator;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.db.DecoratedKey;
import org.apache.cassandra.db.PartitionPosition;
import org.apache.cassandra.serializers.TypeSerializer;
@ -130,11 +131,18 @@ public class PartitionerDefinedOrder extends AbstractType<ByteBuffer>
throw new IllegalStateException("You shouldn't be validating this.");
}
@Override
public TypeSerializer<ByteBuffer> getSerializer()
{
throw new UnsupportedOperationException("You can't do this with a local partitioner.");
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
throw new UnsupportedOperationException("You can't do this with a local partitioner.");
}
@Override
public String toString()
{

View File

@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.TypeSerializer;
@ -139,11 +140,18 @@ public class ReversedType<T> extends AbstractType<T>
return baseType.asCQL3Type();
}
@Override
public TypeSerializer<T> getSerializer()
{
return baseType.getSerializer();
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return baseType.getArgumentDeserializer();
}
@Override
public <V> boolean referencesUserType(V name, ValueAccessor<V> accessor)
{

View File

@ -20,9 +20,12 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import java.util.Objects;
import org.apache.commons.lang3.mutable.MutableShort;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.ShortSerializer;
import org.apache.cassandra.serializers.TypeSerializer;
@ -111,76 +114,82 @@ public class ShortType extends NumberType<Short>
}
@Override
public short toShort(ByteBuffer value)
public ArgumentDeserializer getArgumentDeserializer()
{
return ByteBufferUtil.toShort(value);
return new NumberArgumentDeserializer<MutableShort>(new MutableShort())
{
@Override
protected void setMutableValue(MutableShort mutable, ByteBuffer buffer)
{
mutable.setValue(ByteBufferUtil.toShort(buffer));
}
};
}
@Override
public int toInt(ByteBuffer value)
public ByteBuffer add(Number left, Number right)
{
return toShort(value);
return ByteBufferUtil.bytes((short) (left.shortValue() + right.shortValue()));
}
@Override
public ByteBuffer add(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
public ByteBuffer substract(Number left, Number right)
{
return ByteBufferUtil.bytes((short) (leftType.toShort(left) + rightType.toShort(right)));
}
public ByteBuffer substract(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((short) (leftType.toShort(left) - rightType.toShort(right)));
}
public ByteBuffer multiply(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((short) (leftType.toShort(left) * rightType.toShort(right)));
}
public ByteBuffer divide(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((short) (leftType.toShort(left) / rightType.toShort(right)));
}
public ByteBuffer mod(NumberType<?> leftType, ByteBuffer left, NumberType<?> rightType, ByteBuffer right)
{
return ByteBufferUtil.bytes((short) (leftType.toShort(left) % rightType.toShort(right)));
}
public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes((short) -toShort(input));
return ByteBufferUtil.bytes((short) (left.shortValue() - right.shortValue()));
}
@Override
public ByteBuffer abs(ByteBuffer input)
public ByteBuffer multiply(Number left, Number right)
{
return ByteBufferUtil.bytes((short) Math.abs(toShort(input)));
return ByteBufferUtil.bytes((short) (left.shortValue() * right.shortValue()));
}
@Override
public ByteBuffer exp(ByteBuffer input)
public ByteBuffer divide(Number left, Number right)
{
return ByteBufferUtil.bytes((short) Math.exp(toShort(input)));
return ByteBufferUtil.bytes((short) (left.shortValue() / right.shortValue()));
}
@Override
public ByteBuffer log(ByteBuffer input)
public ByteBuffer mod(Number left, Number right)
{
return ByteBufferUtil.bytes((short) Math.log(toShort(input)));
return ByteBufferUtil.bytes((short) (left.shortValue() % right.shortValue()));
}
@Override
public ByteBuffer log10(ByteBuffer input)
public ByteBuffer negate(Number input)
{
return ByteBufferUtil.bytes((short) Math.log10(toShort(input)));
return ByteBufferUtil.bytes((short) -input.shortValue());
}
@Override
public ByteBuffer round(ByteBuffer input)
public ByteBuffer abs(Number input)
{
return ByteBufferUtil.clone(input);
return ByteBufferUtil.bytes((short) Math.abs(input.shortValue()));
}
@Override
public ByteBuffer exp(Number input)
{
return ByteBufferUtil.bytes((short) Math.exp(input.shortValue()));
}
@Override
public ByteBuffer log(Number input)
{
return ByteBufferUtil.bytes((short) Math.log(input.shortValue()));
}
@Override
public ByteBuffer log10(Number input)
{
return ByteBufferUtil.bytes((short) Math.log10(input.shortValue()));
}
@Override
public ByteBuffer round(Number input)
{
return decompose(input.shortValue());
}
@Override

View File

@ -27,14 +27,8 @@ public abstract class StringType extends AbstractType<String>
super(comparisonType);
}
public ByteBuffer concat(StringType leftType,
ByteBuffer left,
StringType rightType,
ByteBuffer right)
public ByteBuffer concat(String left, String right)
{
String leftS = leftType.compose(left);
String rightS = rightType.compose(right);
return decompose(leftS + rightS);
return decompose(left + right);
}
}

View File

@ -19,7 +19,11 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import org.apache.commons.lang3.mutable.MutableLong;
import org.apache.cassandra.cql3.Duration;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.transport.ProtocolVersion;
import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis;
@ -70,13 +74,11 @@ public abstract class TemporalType<T> extends AbstractType<T>
* @param duration the duration to add
* @return the addition result
*/
public ByteBuffer addDuration(ByteBuffer temporal,
ByteBuffer duration)
public ByteBuffer addDuration(Number temporal, Duration duration)
{
long timeInMillis = toTimeInMillis(temporal);
Duration d = DurationType.instance.compose(duration);
validateDuration(d);
return fromTimeInMillis(d.addTo(timeInMillis));
long timeInMillis = temporal.longValue();
validateDuration(duration);
return fromTimeInMillis(duration.addTo(timeInMillis));
}
/**
@ -86,13 +88,11 @@ public abstract class TemporalType<T> extends AbstractType<T>
* @param duration the duration to substract
* @return the substracion result
*/
public ByteBuffer substractDuration(ByteBuffer temporal,
ByteBuffer duration)
public ByteBuffer substractDuration(Number temporal, Duration duration)
{
long timeInMillis = toTimeInMillis(temporal);
Duration d = DurationType.instance.compose(duration);
validateDuration(d);
return fromTimeInMillis(d.substractFrom(timeInMillis));
long timeInMillis = temporal.longValue();
validateDuration(duration);
return fromTimeInMillis(duration.substractFrom(timeInMillis));
}
/**
@ -102,4 +102,23 @@ public abstract class TemporalType<T> extends AbstractType<T>
protected void validateDuration(Duration duration)
{
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return new ArgumentDeserializer()
{
private final MutableLong wrapper = new MutableLong();
@Override
public Object deserialize(ProtocolVersion protocolVersion, ByteBuffer buffer)
{
if (buffer == null || (!buffer.hasRemaining() && isEmptyValueMeaningless()))
return null;
wrapper.setValue(toTimeInMillis(buffer));
return wrapper;
}
};
}
}

View File

@ -23,6 +23,7 @@ import java.time.ZoneOffset;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TimeSerializer;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.serializers.TypeSerializer;
@ -40,7 +41,10 @@ public class TimeType extends TemporalType<Long>
{
public static final TimeType instance = new TimeType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer DEFAULT_MASKED_VALUE = instance.decompose(0L);
private TimeType() {super(ComparisonType.BYTE_ORDER);} // singleton
public ByteBuffer fromString(String source) throws MarshalException
@ -93,11 +97,18 @@ public class TimeType extends TemporalType<Long>
return CQL3Type.Native.TIME;
}
@Override
public TypeSerializer<Long> getSerializer()
{
return TimeSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
@Override
public ByteBuffer now()
{

View File

@ -25,6 +25,7 @@ import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.UTF8Serializer;
@ -36,6 +37,8 @@ public class UTF8Type extends StringType
{
public static final UTF8Type instance = new UTF8Type();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose("****");
UTF8Type() {super(ComparisonType.BYTE_ORDER);} // singleton
@ -80,16 +83,24 @@ public class UTF8Type extends StringType
return this == previous || previous == AsciiType.instance;
}
@Override
public CQL3Type asCQL3Type()
{
return CQL3Type.Native.TEXT;
}
@Override
public TypeSerializer<String> getSerializer()
{
return UTF8Serializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
@Override
public ByteBuffer getMaskedValue()
{

View File

@ -26,6 +26,7 @@ import com.google.common.primitives.UnsignedLongs;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.UUIDSerializer;
@ -49,6 +50,8 @@ public class UUIDType extends AbstractType<UUID>
{
public static final UUIDType instance = new UUIDType();
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
private static final ByteBuffer MASKED_VALUE = instance.decompose(UUID.fromString("00000000-0000-0000-0000-000000000000"));
UUIDType()
@ -189,11 +192,18 @@ public class UUIDType extends AbstractType<UUID>
return CQL3Type.Native.UUID;
}
@Override
public TypeSerializer<UUID> getSerializer()
{
return UUIDSerializer.instance;
}
@Override
public ArgumentDeserializer getArgumentDeserializer()
{
return ARGUMENT_DESERIALIZER;
}
static final Pattern regexPattern = Pattern.compile("[A-Fa-f0-9]{8}\\-[A-Fa-f0-9]{4}\\-[A-Fa-f0-9]{4}\\-[A-Fa-f0-9]{4}\\-[A-Fa-f0-9]{12}");
static ByteBuffer parse(String source)

View File

@ -38,7 +38,6 @@ import org.apache.cassandra.cql3.ColumnSpecification;
import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UpdateParameters;
import org.apache.cassandra.cql3.functions.UDHelper;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.types.UserType;
import org.apache.cassandra.cql3.statements.ModificationStatement;
@ -68,6 +67,7 @@ import org.apache.cassandra.schema.Views;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.JavaDriverUtils;
import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis;
@ -132,7 +132,7 @@ public class CQLSSTableWriter implements Closeable
this.writer = writer;
this.modificationStatement = modificationStatement;
this.boundNames = boundNames;
this.typeCodecs = boundNames.stream().map(bn -> UDHelper.codecFor(UDHelper.driverType(bn.type)))
this.typeCodecs = boundNames.stream().map(bn -> JavaDriverUtils.codecFor(JavaDriverUtils.driverType(bn.type)))
.collect(Collectors.toList());
}
@ -339,7 +339,7 @@ public class CQLSSTableWriter implements Closeable
{
KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(modificationStatement.keyspace());
org.apache.cassandra.db.marshal.UserType userType = ksm.types.getNullable(ByteBufferUtil.bytes(dataType));
return (UserType) UDHelper.driverType(userType);
return (UserType) JavaDriverUtils.driverType(userType);
}
/**

View File

@ -1150,7 +1150,7 @@ public final class SchemaKeyspace
values[i] = argumentTypes.get(i + 1).fromString(valuesAsCQL.get(i));
}
mask = ColumnMask.build((ScalarFunction) function, values);
mask = new ColumnMask((ScalarFunction) function, values);
}
return new ColumnMetadata(keyspace, table, name, type, position, kind, mask);

View File

@ -0,0 +1,91 @@
/*
* 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.utils;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import org.apache.cassandra.cql3.functions.types.CodecRegistry;
import org.apache.cassandra.cql3.functions.types.DataType;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* Utility methods to convert from {@link AbstractType} to {@link DataType} or to {@link TypeCodec}.
*/
public final class JavaDriverUtils
{
private static final CodecRegistry codecRegistry = new CodecRegistry();
private static final MethodHandle methodParseOne;
static
{
try
{
Class<?> cls = Class.forName("org.apache.cassandra.cql3.functions.types.DataTypeClassNameParser");
Method m = cls.getDeclaredMethod("parseOne", String.class, ProtocolVersion.class, CodecRegistry.class);
m.setAccessible(true);
methodParseOne = MethodHandles.lookup().unreflect(m);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
public static TypeCodec<Object> codecFor(AbstractType<?> abstractType)
{
return codecFor(driverType(abstractType));
}
public static TypeCodec<Object> codecFor(DataType dataType)
{
return codecRegistry.codecFor(dataType);
}
/**
* Returns the Java Driver {@link DataType} for the C* internal type.
*/
public static DataType driverType(AbstractType<?> abstractType)
{
try
{
return (DataType) methodParseOne.invoke(abstractType.toString(), ProtocolVersion.CURRENT, codecRegistry);
}
catch (RuntimeException | Error e)
{
// immediately rethrow these...
throw e;
}
catch (Throwable e)
{
throw new RuntimeException("cannot parse driver type " + abstractType, e);
}
}
/**
* The class should never be instantiated as it contains only static methods.
*/
private JavaDriverUtils()
{
}
}

View File

@ -4,29 +4,30 @@ import java.nio.ByteBuffer;
import java.util.*;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.UDFDataType;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.types.TupleValue;
import org.apache.cassandra.cql3.functions.types.UDTValue;
public final class #class_name# extends JavaUDF
{
public #class_name#(TypeCodec<Object> returnCodec, TypeCodec<Object>[] argCodecs, UDFContext udfContext)
public #class_name#(UDFDataType returnType, UDFContext udfContext)
{
super(returnCodec, argCodecs, udfContext);
super(returnType, udfContext);
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
protected ByteBuffer executeImpl(Arguments arguments)
{
#return_type# result = #execute_internal_name#(
#arguments#
#arguments#
);
return super.decompose(protocolVersion, result);
return decompose(arguments.getProtocolVersion(), result);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
#return_type# result = #execute_internal_name#(
#arguments_aggregate#

View File

@ -0,0 +1,159 @@
/*
* 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.test.microbench;
import java.util.concurrent.TimeUnit;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.OverrideConfigurationLoader;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.commitlog.CommitLog;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
/**
* Benchmarks the performance of CQL functions calls with terminal arguments.
*/
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 15, time = 2) // seconds
@Measurement(iterations = 10, time = 2) // seconds
@Fork(value = 1)
@Threads(1)
@State(Scope.Benchmark)
public class FunctionWithTerminalArgsBench extends CQLTester
{
private static final int NUM_PARTITIONS = 10;
private static final int NUM_CLUSTERINGS = 100;
// We repeat the number of times that the function call appears on the query to amplify its effect on the benchmark,
// since its impact is relatively small in the context of a full query.
private static final int NUM_FUNCTION_CALLS = 10;
private String udf;
@Setup(Level.Trial)
public void setup() throws Throwable
{
// we disable UDF threads to avoid the overhead and better see the impact of the terminal arguments
OverrideConfigurationLoader.override((config) -> {
config.allow_insecure_udfs = true;
config.user_defined_functions_threads_enabled = false;
});
DatabaseDescriptor.daemonInitialization();
CQLTester.setUpClass();
beforeTest();
udf = createFunction(KEYSPACE,
"int, text",
" CREATE FUNCTION %s (a1 text, a2 text, a3 text, a4 text, a5 text)" +
" CALLED ON NULL INPUT" +
" RETURNS text" +
" LANGUAGE java" +
" AS 'return a1 + a2 + a3 + a4 + a5;'");
String table = createTable(KEYSPACE, "CREATE TABLE %s (k int, c int, v text, PRIMARY KEY(k, c))");
ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore(table);
cfs.disableAutoCompaction();
System.out.println("Writing " + (NUM_PARTITIONS * NUM_CLUSTERINGS) + " rows...");
int n = 0;
for (int k = 0; k < NUM_PARTITIONS; k++)
{
for (int c = 0; c < NUM_CLUSTERINGS; c++)
{
execute("INSERT INTO %s(k, c, v) VALUES (?, ?, ?)", k, c, String.valueOf(n++));
}
}
}
@TearDown(Level.Trial)
public void teardown() throws InterruptedException
{
CommitLog.instance.shutdownBlocking();
CQLTester.tearDownClass();
CQLTester.cleanup();
}
@Benchmark
public Object none()
{
return execute("v");
}
@Benchmark
public Object add()
{
return execute("v + 'abc'");
}
@Benchmark
public Object udf()
{
return execute(udf + "(v, 'a1', 'a2', 'a3', 'a4')");
}
@Benchmark
public Object maskReplace()
{
return execute("mask_replace(v, '***')");
}
@Benchmark
public Object maskPartial()
{
return execute("mask_inner(v, 1, 1, '#')");
}
@Benchmark
public Object maskHash()
{
return execute("mask_hash(v, 'MD5')");
}
private UntypedResultSet execute(String functionCall)
{
StringBuilder sb = new StringBuilder();
sb.append("SELECT ");
for (int i = 0; i < NUM_FUNCTION_CALLS; i++)
{
if (i > 0)
sb.append(", ");
sb.append(functionCall);
}
sb.append(" FROM %s");
return super.execute(sb.toString());
}
}

View File

@ -1,151 +0,0 @@
/*
* 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.cql3;
import java.nio.ByteBuffer;
import org.junit.Assert;
import org.junit.Test;
import org.apache.cassandra.cql3.functions.UDHelper;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.BooleanType;
import org.apache.cassandra.db.marshal.ByteType;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.CounterColumnType;
import org.apache.cassandra.db.marshal.DateType;
import org.apache.cassandra.db.marshal.DecimalType;
import org.apache.cassandra.db.marshal.DoubleType;
import org.apache.cassandra.db.marshal.FloatType;
import org.apache.cassandra.db.marshal.InetAddressType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.db.marshal.IntegerType;
import org.apache.cassandra.db.marshal.LongType;
import org.apache.cassandra.db.marshal.ReversedType;
import org.apache.cassandra.db.marshal.ShortType;
import org.apache.cassandra.db.marshal.SimpleDateType;
import org.apache.cassandra.db.marshal.TimeType;
import org.apache.cassandra.db.marshal.TimeUUIDType;
import org.apache.cassandra.db.marshal.TimestampType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.marshal.UUIDType;
import org.apache.cassandra.db.marshal.ValueAccessor;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.utils.ByteBufferUtil;
public class UDHelperTest
{
static class UFTestCustomType extends AbstractType<String>
{
protected UFTestCustomType()
{
super(ComparisonType.CUSTOM);
}
public ByteBuffer fromString(String source) throws MarshalException
{
return ByteBuffer.wrap(source.getBytes());
}
public Term fromJSONObject(Object parsed) throws MarshalException
{
throw new UnsupportedOperationException();
}
public TypeSerializer<String> getSerializer()
{
return UTF8Type.instance.getSerializer();
}
public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR)
{
return ValueAccessor.compare(left, accessorL, right, accessorR);
}
}
@Test
public void testEmptyVariableLengthTypes()
{
AbstractType<?>[] types = new AbstractType<?>[]{
AsciiType.instance,
BytesType.instance,
UTF8Type.instance,
new UFTestCustomType()
};
for (AbstractType<?> type : types)
{
Assert.assertFalse("type " + type.getClass().getName(),
UDHelper.isNullOrEmpty(type, ByteBufferUtil.EMPTY_BYTE_BUFFER));
}
}
@Test
public void testNonEmptyPrimitiveTypes()
{
AbstractType<?>[] types = new AbstractType<?>[]{
TimeType.instance,
SimpleDateType.instance,
ByteType.instance,
ShortType.instance
};
for (AbstractType<?> type : types)
{
try
{
type.getSerializer().validate(ByteBufferUtil.EMPTY_BYTE_BUFFER);
Assert.fail(type.getClass().getSimpleName());
}
catch (MarshalException e)
{
//
}
}
}
@Test
public void testEmptiableTypes()
{
AbstractType<?>[] types = new AbstractType<?>[]{
BooleanType.instance,
CounterColumnType.instance,
DateType.instance,
DecimalType.instance,
DoubleType.instance,
FloatType.instance,
InetAddressType.instance,
Int32Type.instance,
IntegerType.instance,
LongType.instance,
TimestampType.instance,
TimeUUIDType.instance,
UUIDType.instance
};
for (AbstractType<?> type : types)
{
Assert.assertTrue(type.getClass().getSimpleName(), UDHelper.isNullOrEmpty(type, ByteBufferUtil.EMPTY_BYTE_BUFFER));
Assert.assertTrue("reversed " + type.getClass().getSimpleName(),
UDHelper.isNullOrEmpty(ReversedType.getInstance(type), ByteBufferUtil.EMPTY_BYTE_BUFFER));
}
}
}

View File

@ -53,9 +53,15 @@ public class FunctionFactoryTest extends CQLTester
return new NativeScalarFunction(name.name, argTypes.get(0), argTypes.get(0))
{
@Override
public ByteBuffer execute(ProtocolVersion protocol, List<ByteBuffer> parameters)
public Arguments newArguments(ProtocolVersion version)
{
return parameters.get(0);
return FunctionArguments.newNoopInstance(version, 1);
}
@Override
public ByteBuffer execute(Arguments arguments)
{
return arguments.containsNulls() ? null : arguments.get(0);
}
};
}
@ -73,13 +79,13 @@ public class FunctionFactoryTest extends CQLTester
return new NativeScalarFunction(name.name, UTF8Type.instance, argTypes.get(0))
{
@Override
public ByteBuffer execute(ProtocolVersion protocol, List<ByteBuffer> parameters)
public ByteBuffer execute(Arguments arguments)
{
ByteBuffer value = parameters.get(0);
if (value == null)
if (arguments.containsNulls())
return null;
return UTF8Type.instance.decompose(argTypes.get(0).compose(value).toString());
Object value = arguments.get(0);
return UTF8Type.instance.decompose(value.toString());
}
};
}

View File

@ -21,8 +21,6 @@ package org.apache.cassandra.cql3.functions;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
@ -296,8 +294,9 @@ public class MathFctsTest
private static ByteBuffer executeFunction(Function function, ByteBuffer input)
{
List<ByteBuffer> params = Collections.singletonList(input);
return ((ScalarFunction) function).execute(ProtocolVersion.CURRENT, params);
Arguments arguments = function.newArguments(ProtocolVersion.CURRENT);
arguments.set(0, input);
return ((ScalarFunction) function).execute(arguments);
}
private <T extends Number> void assertFctEquals(NativeFunction fct, T expected, NumberType<T> inputType, T inputValue)

View File

@ -20,9 +20,7 @@ package org.apache.cassandra.cql3.functions;
import java.nio.ByteBuffer;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.junit.Test;
@ -185,7 +183,8 @@ public class TimeFctsTest
private static ByteBuffer executeFunction(Function function, ByteBuffer input)
{
List<ByteBuffer> params = Collections.singletonList(input);
return ((ScalarFunction) function).execute(ProtocolVersion.CURRENT, params);
Arguments arguments = function.newArguments(ProtocolVersion.CURRENT);
arguments.set(0, input);
return ((ScalarFunction) function).execute(arguments);
}
}

View File

@ -29,6 +29,7 @@ import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.FunctionFactory;
import org.apache.cassandra.cql3.functions.FunctionParameter;
import org.apache.cassandra.cql3.functions.NativeFunction;
@ -564,15 +565,10 @@ public class ColumnMaskTest extends ColumnMaskTester
return new MaskingFunction(name, argTypes.get(0), argTypes.get(0))
{
@Override
public Masker masker(ByteBuffer... parameters)
public ByteBuffer execute(Arguments arguments) throws InvalidRequestException
{
return bb -> {
if (bb == null)
return null;
Integer value = Int32Type.instance.compose(bb) * -1;
return Int32Type.instance.decompose(value);
};
Integer value = arguments.getAsInt(0) * -1;
return Int32Type.instance.decompose(value);
}
};
}

View File

@ -40,11 +40,11 @@ public class PartialMaskingFunctionTest extends MaskingFunctionTester
@Override
protected void testMaskingOnColumn(String name, CQL3Type type, Object value) throws Throwable
{
testMaskingOnColumn(PartialMaskingFunction.Type.INNER, name, type, value);
testMaskingOnColumn(PartialMaskingFunction.Type.OUTER, name, type, value);
testMaskingOnColumn(PartialMaskingFunction.Kind.INNER, name, type, value);
testMaskingOnColumn(PartialMaskingFunction.Kind.OUTER, name, type, value);
}
protected void testMaskingOnColumn(PartialMaskingFunction.Type masker, String name, CQL3Type type, Object value) throws Throwable
protected void testMaskingOnColumn(PartialMaskingFunction.Kind masker, String name, CQL3Type type, Object value) throws Throwable
{
String functionName = SchemaConstants.SYSTEM_KEYSPACE_NAME + ".mask_" + masker.name().toLowerCase();
@ -169,9 +169,9 @@ public class PartialMaskingFunctionTest extends MaskingFunctionTester
String innerMaskedValue,
String outerMaskedValue)
{
Assertions.assertThat(PartialMaskingFunction.Type.INNER.mask(unmaskedValue, begin, end, padding))
Assertions.assertThat(PartialMaskingFunction.Kind.INNER.mask(unmaskedValue, begin, end, padding))
.isIn(innerMaskedValue);
Assertions.assertThat(PartialMaskingFunction.Type.OUTER.mask(unmaskedValue, begin, end, padding))
Assertions.assertThat(PartialMaskingFunction.Kind.OUTER.mask(unmaskedValue, begin, end, padding))
.isIn(outerMaskedValue);
}
}

View File

@ -119,12 +119,12 @@ public class UFJavaTest extends CQLTester
@Test
public void testJavaFunctionInvalidReturn() throws Throwable
{
assertInvalidMessage("cannot convert from long to Double",
assertInvalidMessage("cannot convert from boolean to double",
"CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfir(val double) " +
"RETURNS NULL ON NULL INPUT " +
"RETURNS double " +
"LANGUAGE JAVA\n" +
"AS 'return 1L;';");
"AS 'return true;';");
}
@Test

View File

@ -27,9 +27,9 @@ import org.junit.Test;
import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.functions.UDHelper;
import org.apache.cassandra.exceptions.FunctionExecutionException;
import org.apache.cassandra.service.ClientWarn;
import org.apache.cassandra.utils.JavaDriverUtils;
public class UFSecurityTest extends CQLTester
{
@ -58,7 +58,7 @@ public class UFSecurityTest extends CQLTester
}
String[] cfnSources =
{ "try { Class.forName(\"" + UDHelper.class.getName() + "\"); } catch (Exception e) { throw new RuntimeException(e); } return 0d;",
{ "try { Class.forName(\"" + JavaDriverUtils.class.getName() + "\"); } catch (Exception e) { throw new RuntimeException(e); } return 0d;",
"try { Class.forName(\"sun.misc.Unsafe\"); } catch (Exception e) { throw new RuntimeException(e); } return 0d;" };
for (String source : cfnSources)
{

View File

@ -23,18 +23,14 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import com.google.common.reflect.TypeToken;
import org.junit.Assert;
import org.junit.Test;
import com.datastax.driver.core.TypeTokens;
import com.datastax.driver.core.UDTValue;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.cql3.functions.FunctionName;
import org.apache.cassandra.cql3.functions.JavaBasedUDFunction;
import org.apache.cassandra.cql3.functions.UDFunction;
import org.apache.cassandra.db.marshal.CollectionType;
import org.apache.cassandra.exceptions.InvalidRequestException;
@ -52,15 +48,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class UFTest extends CQLTester
{
@Test
public void testJavaSourceName()
{
Assert.assertEquals("String", JavaBasedUDFunction.javaSourceName(TypeToken.of(String.class)));
Assert.assertEquals("java.util.Map<Integer, String>", JavaBasedUDFunction.javaSourceName(TypeTokens.mapOf(Integer.class, String.class)));
Assert.assertEquals("com.datastax.driver.core.UDTValue", JavaBasedUDFunction.javaSourceName(TypeToken.of(UDTValue.class)));
Assert.assertEquals("java.util.Set<com.datastax.driver.core.UDTValue>", JavaBasedUDFunction.javaSourceName(TypeTokens.setOf(UDTValue.class)));
}
@Test
public void testNonExistingOnes() throws Throwable
{

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class CallClone extends JavaUDF
{
public CallClone(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public CallClone(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
try
{

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class CallFinalize extends JavaUDF
{
public CallFinalize(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public CallFinalize(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
try
{

View File

@ -19,30 +19,31 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class CallOrgApache extends JavaUDF
{
public CallOrgApache(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public CallOrgApache(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
DatabaseDescriptor.getClusterName();
return null;

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class ClassWithField extends JavaUDF
{
public ClassWithField(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public ClassWithField(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
return null;
}

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class ClassWithInitializer extends JavaUDF
{
public ClassWithInitializer(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public ClassWithInitializer(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
return null;
}

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class ClassWithInitializer2 extends JavaUDF
{
public ClassWithInitializer2(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public ClassWithInitializer2(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
return null;
}

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class ClassWithInitializer3 extends JavaUDF
{
public ClassWithInitializer3(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public ClassWithInitializer3(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
return null;
}

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class ClassWithInnerClass extends JavaUDF
{
public ClassWithInnerClass(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public ClassWithInnerClass(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
return null;
}

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class ClassWithInnerClass2 extends JavaUDF
{
public ClassWithInnerClass2(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public ClassWithInnerClass2(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
// this is fine
new Runnable() {

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class ClassWithStaticInitializer extends JavaUDF
{
public ClassWithStaticInitializer(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public ClassWithStaticInitializer(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
return null;
}

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class ClassWithStaticInnerClass extends JavaUDF
{
public ClassWithStaticInnerClass(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public ClassWithStaticInnerClass(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
return null;
}

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class GoodClass extends JavaUDF
{
public GoodClass(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public GoodClass(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
return null;
}

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class UseOfSynchronized extends JavaUDF
{
public UseOfSynchronized(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public UseOfSynchronized(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
synchronized (this)
{

View File

@ -19,29 +19,30 @@
package org.apache.cassandra.cql3.validation.entities.udfverify;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.cql3.functions.types.TypeCodec;
import org.apache.cassandra.cql3.functions.Arguments;
import org.apache.cassandra.cql3.functions.JavaUDF;
import org.apache.cassandra.cql3.functions.UDFContext;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.UDFDataType;
/**
* Used by {@link org.apache.cassandra.cql3.validation.entities.UFVerifierTest}.
*/
public final class UseOfSynchronizedWithNotify extends JavaUDF
{
public UseOfSynchronizedWithNotify(TypeCodec<Object> returnDataType, TypeCodec<Object>[] argDataTypes, UDFContext udfContext)
public UseOfSynchronizedWithNotify(UDFDataType returnType, UDFContext udfContext)
{
super(returnDataType, argDataTypes, udfContext);
super(returnType, udfContext);
}
protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List<ByteBuffer> params)
@Override
protected Object executeAggregateImpl(Object state, Arguments arguments)
{
throw new UnsupportedOperationException();
}
protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List<ByteBuffer> params)
@Override
protected ByteBuffer executeImpl(Arguments arguments)
{
synchronized (this)
{

Some files were not shown because too many files have changed in this diff Show More