diff --git a/CHANGES.txt b/CHANGES.txt index b3af28ac01..4b46f22d93 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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) diff --git a/src/java/org/apache/cassandra/cql3/Constants.java b/src/java/org/apache/cassandra/cql3/Constants.java index f4418fdada..03341d22bd 100644 --- a/src/java/org/apache/cassandra/cql3/Constants.java +++ b/src/java/org/apache/cassandra/cql3/Constants.java @@ -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) diff --git a/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java b/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java index ce8e8e12d4..9942869a5f 100644 --- a/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java @@ -114,7 +114,8 @@ public abstract class AggregateFcts return LongType.instance.decompose(count); } - public void addInput(ProtocolVersion protocolVersion, List 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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)); } } } diff --git a/src/java/org/apache/cassandra/cql3/functions/AggregateFunction.java b/src/java/org/apache/cassandra/cql3/functions/AggregateFunction.java index b20756330a..57a966f0a8 100644 --- a/src/java/org/apache/cassandra/cql3/functions/AggregateFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/AggregateFunction.java @@ -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 values) throws InvalidRequestException; + public void addInput(Arguments arguments) throws InvalidRequestException; /** * Computes and returns the aggregate current value. diff --git a/src/java/org/apache/cassandra/cql3/functions/ArgumentDeserializer.java b/src/java/org/apache/cassandra/cql3/functions/ArgumentDeserializer.java new file mode 100644 index 0000000000..19c2093996 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/functions/ArgumentDeserializer.java @@ -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); +} diff --git a/src/java/org/apache/cassandra/cql3/functions/Arguments.java b/src/java/org/apache/cassandra/cql3/functions/Arguments.java new file mode 100644 index 0000000000..18cace3648 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/functions/Arguments.java @@ -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. + *

The class can be reused for calling the same function multiple times.

+ */ +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 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.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.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.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.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.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.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.get(i).doubleValue(); + } + + /** + * Returns the current number of arguments. + * + * @return the current number of arguments. + */ + int size(); +} diff --git a/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java b/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java index 8a8eb9c0c4..6c032dbc91 100644 --- a/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java @@ -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 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 parameters) + public ByteBuffer execute(Arguments arguments) { - ByteBuffer val = parameters.get(0); + ByteBuffer val = arguments.get(0); if (val != null) { diff --git a/src/java/org/apache/cassandra/cql3/functions/CastFcts.java b/src/java/org/apache/cassandra/cql3/functions/CastFcts.java index 1761778f51..0246a0bac7 100644 --- a/src/java/org/apache/cassandra/cql3/functions/CastFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/CastFcts.java @@ -263,13 +263,13 @@ public final class CastFcts return new JavaFunctionWrapper<>(inputType(), outputType(), converter, true); } - public final ByteBuffer execute(ProtocolVersion protocolVersion, List 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 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 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)); } } diff --git a/src/java/org/apache/cassandra/cql3/functions/CollectionFcts.java b/src/java/org/apache/cassandra/cql3/functions/CollectionFcts.java index ebc96b78d3..a3bc4726f9 100644 --- a/src/java/org/apache/cassandra/cql3/functions/CollectionFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/CollectionFcts.java @@ -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 parameters) + public ByteBuffer execute(Arguments arguments) { - ByteBuffer value = parameters.get(0); - if (value == null) + if (arguments.containsNulls()) return null; - Map map = inputType.compose(value); + Map map = arguments.get(0); Set 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 parameters) + public ByteBuffer execute(Arguments arguments) { - ByteBuffer value = parameters.get(0); - if (value == null) + if (arguments.containsNulls()) return null; - Map map = inputType.compose(value); + Map map = arguments.get(0); List 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 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 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()); } } } diff --git a/src/java/org/apache/cassandra/cql3/functions/FromJsonFct.java b/src/java/org/apache/cassandra/cql3/functions/FromJsonFct.java index 34a7799556..356003e8e8 100644 --- a/src/java/org/apache/cassandra/cql3/functions/FromJsonFct.java +++ b/src/java/org/apache/cassandra/cql3/functions/FromJsonFct.java @@ -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 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) { diff --git a/src/java/org/apache/cassandra/cql3/functions/Function.java b/src/java/org/apache/cassandra/cql3/functions/Function.java index 0a8d331e2c..5af03b2fd3 100644 --- a/src/java/org/apache/cassandra/cql3/functions/Function.java +++ b/src/java/org/apache/cassandra/cql3/functions/Function.java @@ -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 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 compare(Function other) { throw new UnsupportedOperationException(); diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionArguments.java b/src/java/org/apache/cassandra/cql3/functions/FunctionArguments.java new file mode 100644 index 0000000000..102fa5e7cc --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/functions/FunctionArguments.java @@ -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 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. + *

Native functions can use different {@link ArgumentDeserializer} to avoid instanciating primitive wrappers.

+ * + * @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> 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 get(int i) + { + return (T) arguments[i]; + } + + @Override + public int size() + { + return arguments.length; + } +} diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java index 33696573b2..ee5fda7816 100644 --- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java +++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java @@ -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 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 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; diff --git a/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java b/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java index 10be467c0e..132721e986 100644 --- a/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java @@ -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 argNames, List> 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 params) + @Override + protected ByteBuffer executeUserDefined(Arguments arguments) { - return javaUDF.executeImpl(protocolVersion, params); + return javaUDF.executeImpl(arguments); } - protected Object executeAggregateUserDefined(ProtocolVersion protocolVersion, Object firstParam, List 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 argNames) + private static String generateArgumentList(List argTypes, List 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))} *

*/ - private static String generateArguments(TypeToken[] paramTypes, List argNames, boolean forAggregate) + private static String generateArguments(List argTypes, List 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; } - }} + } +} diff --git a/src/java/org/apache/cassandra/cql3/functions/JavaUDF.java b/src/java/org/apache/cassandra/cql3/functions/JavaUDF.java index 3134da98ed..74cdaf17d2 100644 --- a/src/java/org/apache/cassandra/cql3/functions/JavaUDF.java +++ b/src/java/org/apache/cassandra/cql3/functions/JavaUDF.java @@ -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 returnCodec; - private final TypeCodec[] argCodecs; - + private final UDFDataType returnType; protected final UDFContext udfContext; - protected JavaUDF(TypeCodec returnCodec, TypeCodec[] 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 params); + protected abstract ByteBuffer executeImpl(Arguments arguments); - protected abstract Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List 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); } } diff --git a/src/java/org/apache/cassandra/cql3/functions/MathFcts.java b/src/java/org/apache/cassandra/cql3/functions/MathFcts.java index 553ac40fe0..1f6ac2b7d1 100644 --- a/src/java/org/apache/cassandra/cql3/functions/MathFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/MathFcts.java @@ -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 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 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 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 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, Number, ByteBuffer> f) + { + return new NativeScalarFunction(name, type, type) { @Override - public ByteBuffer execute(ProtocolVersion protocolVersion, List 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); } }; } diff --git a/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java b/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java index 285cf639a0..3437a8d158 100644 --- a/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java @@ -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); + } + } diff --git a/src/java/org/apache/cassandra/cql3/functions/OperationFcts.java b/src/java/org/apache/cassandra/cql3/functions/OperationFcts.java index 9e0fef1443..d0b7ac382c 100644 --- a/src/java/org/apache/cassandra/cql3/functions/OperationFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/OperationFcts.java @@ -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 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 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)); } } } diff --git a/src/java/org/apache/cassandra/cql3/functions/PartialScalarFunction.java b/src/java/org/apache/cassandra/cql3/functions/PartialScalarFunction.java index 6970768f58..4486a40978 100644 --- a/src/java/org/apache/cassandra/cql3/functions/PartialScalarFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/PartialScalarFunction.java @@ -41,5 +41,5 @@ public interface PartialScalarFunction extends ScalarFunction * * @return the list of input parameters for the function */ - public List getPartialParameters(); + public List getPartialArguments(); } diff --git a/src/java/org/apache/cassandra/cql3/functions/PartiallyAppliedScalarFunction.java b/src/java/org/apache/cassandra/cql3/functions/PartiallyAppliedScalarFunction.java index 301160b29d..7a5e5fb71f 100644 --- a/src/java/org/apache/cassandra/cql3/functions/PartiallyAppliedScalarFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/PartiallyAppliedScalarFunction.java @@ -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 getPartialParameters() + public Arguments newArguments(ProtocolVersion version) + { + return new PartialFunctionArguments(version, function, partialParameters, argTypes.size()); + } + + @Override + public List getPartialArguments() { return partialParameters; } @@ -81,16 +86,10 @@ final class PartiallyAppliedScalarFunction extends NativeScalarFunction implemen return argTypes; } - public ByteBuffer execute(ProtocolVersion protocolVersion, List parameters) throws InvalidRequestException + @Override + public ByteBuffer execute(Arguments arguments) throws InvalidRequestException { - List 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 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 get(int i) + { + return arguments.get(i); + } + + @Override + public int size() + { + return arguments.size(); + } + } } diff --git a/src/java/org/apache/cassandra/cql3/functions/PreComputedScalarFunction.java b/src/java/org/apache/cassandra/cql3/functions/PreComputedScalarFunction.java index 9670132386..0dd32ed0c4 100644 --- a/src/java/org/apache/cassandra/cql3/functions/PreComputedScalarFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/PreComputedScalarFunction.java @@ -40,20 +40,20 @@ class PreComputedScalarFunction extends NativeScalarFunction implements PartialS private final ProtocolVersion valueVersion; private final ScalarFunction function; - private final List parameters; + private final List arguments; PreComputedScalarFunction(AbstractType returnType, ByteBuffer value, ProtocolVersion valueVersion, ScalarFunction function, - List parameters) + List 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 getPartialParameters() + public List getPartialArguments() { - return parameters; + return arguments; } - public ByteBuffer execute(ProtocolVersion protocolVersion, List 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 nothing) throws InvalidRequestException diff --git a/src/java/org/apache/cassandra/cql3/functions/ScalarFunction.java b/src/java/org/apache/cassandra/cql3/functions/ScalarFunction.java index fc18aae6a2..986242a413 100644 --- a/src/java/org/apache/cassandra/cql3/functions/ScalarFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/ScalarFunction.java @@ -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 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. *

* To take an example, if you consider the function *

@@ -66,21 +65,21 @@ public interface ScalarFunction extends Function
      * 
* 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 must 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 must 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 partialParameters) + default ScalarFunction partialApplication(ProtocolVersion protocolVersion, List 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); } } diff --git a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java index e1557e6ce5..eb547f0f62 100644 --- a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java @@ -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 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 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 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 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 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 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 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 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 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); diff --git a/src/java/org/apache/cassandra/cql3/functions/ToJsonFct.java b/src/java/org/apache/cassandra/cql3/functions/ToJsonFct.java index 8dd7fd722d..a1182a9c9e 100644 --- a/src/java/org/apache/cassandra/cql3/functions/ToJsonFct.java +++ b/src/java/org/apache/cassandra/cql3/functions/ToJsonFct.java @@ -54,14 +54,26 @@ public class ToJsonFct extends NativeScalarFunction super(name, UTF8Type.instance, argType); } - public ByteBuffer execute(ProtocolVersion protocolVersion, List 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.get(0)); } public static void addFunctionsTo(NativeFunctions functions) diff --git a/src/java/org/apache/cassandra/cql3/functions/TokenFct.java b/src/java/org/apache/cassandra/cql3/functions/TokenFct.java index fb6185bacf..dd163b6b6d 100644 --- a/src/java/org/apache/cassandra/cql3/functions/TokenFct.java +++ b/src/java/org/apache/cassandra/cql3/functions/TokenFct.java @@ -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 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); diff --git a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java index 67e96cd0b5..99f8966b4d 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java @@ -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 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 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); diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFByteCodeVerifier.java b/src/java/org/apache/cassandra/cql3/functions/UDFByteCodeVerifier.java index e0b18069ee..09b84a74e0 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFByteCodeVerifier.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFByteCodeVerifier.java @@ -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 disallowedClasses = new HashSet<>(); private final Multimap 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 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 params) + // the executeImpl method - ByteBuffer executeImpl(Object state, Arguments arguments) return new ExecuteImplVisitor(errors); } if ("".equals(name)) diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFContextImpl.java b/src/java/org/apache/cassandra/cql3/functions/UDFContextImpl.java index d4bdf203a2..761e0c8c0d 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFContextImpl.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFContextImpl.java @@ -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> byName = new HashMap<>(); - private final TypeCodec[] argCodecs; - private final TypeCodec returnCodec; + private final Map byName = new HashMap<>(); + private final List argTypes; + private final UDFDataType returnType; - UDFContextImpl(List argNames, TypeCodec[] argCodecs, TypeCodec returnCodec, + UDFContextImpl(List argNames, + List 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 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 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 codecFor(String argName) - { - TypeCodec 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 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 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; + } } diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFDataType.java b/src/java/org/apache/cassandra/cql3/functions/UDFDataType.java new file mode 100644 index 0000000000..a1aa202f87 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/functions/UDFDataType.java @@ -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. + *

+ * 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 wrap(List> argTypes, boolean usePrimitive) + { + List 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) 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; + } +} diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java index 58454a49ac..f2dedaf64d 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java @@ -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[] argCodecs; - protected final TypeCodec returnCodec; + protected final List 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 argNames, - List> 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 parameters) + @Override + protected Object executeAggregateUserDefined(Object firstParam, Arguments arguments) { throw broken(); } - public ByteBuffer executeUserDefined(ProtocolVersion protocolVersion, List 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 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 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 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 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 makeEmptyParametersNull(List parameters) - { - List 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 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 parameters); + protected abstract ByteBuffer executeUserDefined(Arguments arguments); - protected abstract Object executeAggregateUserDefined(ProtocolVersion protocolVersion, Object firstParam, List 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[] 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 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 diff --git a/src/java/org/apache/cassandra/cql3/functions/UDHelper.java b/src/java/org/apache/cassandra/cql3/functions/UDHelper.java deleted file mode 100644 index 8c145d9912..0000000000 --- a/src/java/org/apache/cassandra/cql3/functions/UDHelper.java +++ /dev/null @@ -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[] codecsFor(DataType[] dataType) - { - TypeCodec[] codecs = new TypeCodec[dataType.length]; - for (int i = 0; i < dataType.length; i++) - codecs[i] = codecFor(dataType[i]); - return codecs; - } - - public static TypeCodec 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[] 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> 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()); - } -} diff --git a/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java b/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java index 0083883623..3e90deb683 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/UuidFcts.java @@ -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 parameters) + @Override + public ByteBuffer execute(Arguments arguments) { return UUIDSerializer.instance.serialize(UUID.randomUUID()); } diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java b/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java index add2228133..77f88c0ee9 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java @@ -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 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) diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/DefaultMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/DefaultMaskingFunction.java index b1116716d3..e5c0ee2e7d 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/DefaultMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/DefaultMaskingFunction.java @@ -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 DefaultMaskingFunction(FunctionName name, AbstractType 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. */ diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/HashMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/HashMaskingFunction.java index aa515dd9dc..3291b156f0 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/HashMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/HashMaskingFunction.java @@ -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 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 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(); } diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/MaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/MaskingFunction.java index 0ed037d48d..37bc791711 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/MaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/MaskingFunction.java @@ -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 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) diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/NullMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/NullMaskingFunction.java index 0d82a76bb9..830a1c3b54 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/NullMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/NullMaskingFunction.java @@ -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. */ diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunction.java index 80826a4714..8f5a794b8e 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunction.java @@ -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}: *
    *
  • 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.
  • @@ -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 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 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 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> argTypes, AbstractType receiverType) { AbstractType inputType = (AbstractType) argTypes.get(0); - return new PartialMaskingFunction(name, type, inputType, argTypes.size() == 4); + return new PartialMaskingFunction(name, kind, inputType, argTypes.size() == 4); } }; } diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/ReplaceMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/ReplaceMaskingFunction.java index 1510521188..880f941abf 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/ReplaceMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/ReplaceMaskingFunction.java @@ -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. */ diff --git a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java index b21b5f870a..f7853aee1f 100644 --- a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java @@ -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 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 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 partialParameters = new ArrayList<>(numberOfArguments); + List 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 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 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 argSelectors); } protected final T fun; @@ -108,7 +132,7 @@ abstract class AbstractFunctionSelector 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 args; + private final Arguments args; protected final List argSelectors; public static Factory newFactory(final Function fun, final SelectorFactories factories) throws InvalidRequestException @@ -154,7 +178,7 @@ abstract class AbstractFunctionSelector 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 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 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 extends Selector }; } - protected AbstractFunctionSelector(Kind kind, T fun, List argSelectors) + protected AbstractFunctionSelector(Kind kind, ProtocolVersion version, T fun, List 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 extends Selector args.set(i, value); } - protected List args() + protected Arguments args() { return args; } @@ -311,14 +339,14 @@ abstract class AbstractFunctionSelector extends Selector if (isPartial) { - List partialParameters = ((PartialScalarFunction) fun).getPartialParameters(); + List 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 extends Selector if (isPartial) { - List partialParameters = ((PartialScalarFunction) fun).getPartialParameters(); + List 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 extends Selector serializer.serialize(argSelectors.get(i), out, version); } - private int computeBitSet(List partialParameters) + private int computeBitSet(List 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; diff --git a/src/java/org/apache/cassandra/cql3/selection/AggregateFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/AggregateFunctionSelector.java index 8d21c1e9ef..d91981b04d 100644 --- a/src/java/org/apache/cassandra/cql3/selection/AggregateFunctionSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/AggregateFunctionSelector.java @@ -27,12 +27,12 @@ import org.apache.cassandra.transport.ProtocolVersion; final class AggregateFunctionSelector extends AbstractFunctionSelector { - protected static final SelectorDeserializer deserializer = new AbstractFunctionSelectorDeserializer() + static final SelectorDeserializer deserializer = new AbstractFunctionSelectorDeserializer() { @Override - protected Selector newFunctionSelector(Function function, List argSelectors) + protected Selector newFunctionSelector(ProtocolVersion version, Function function, List argSelectors) { - return new AggregateFunctionSelector(function, argSelectors); + return new AggregateFunctionSelector(version, function, argSelectors); } }; @@ -55,7 +55,7 @@ final class AggregateFunctionSelector extends AbstractFunctionSelector argSelectors) throws InvalidRequestException + AggregateFunctionSelector(ProtocolVersion version, Function fun, List 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(); } diff --git a/src/java/org/apache/cassandra/cql3/selection/ScalarFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/ScalarFunctionSelector.java index 5e9711a9e7..6df2b85b08 100644 --- a/src/java/org/apache/cassandra/cql3/selection/ScalarFunctionSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/ScalarFunctionSelector.java @@ -28,12 +28,12 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue; final class ScalarFunctionSelector extends AbstractFunctionSelector { - protected static final SelectorDeserializer deserializer = new AbstractFunctionSelectorDeserializer() + static final SelectorDeserializer deserializer = new AbstractFunctionSelectorDeserializer() { @Override - protected Selector newFunctionSelector(Function function, List argSelectors) + protected Selector newFunctionSelector(ProtocolVersion version, Function function, List argSelectors) { - return new ScalarFunctionSelector(function, argSelectors); + return new ScalarFunctionSelector(version, function, argSelectors); } }; @@ -58,7 +58,7 @@ final class ScalarFunctionSelector extends AbstractFunctionSelector argSelectors) + ScalarFunctionSelector(ProtocolVersion version, Function fun, List argSelectors) { - super(Kind.SCALAR_FUNCTION_SELECTOR, (ScalarFunction) fun, argSelectors); + super(Kind.SCALAR_FUNCTION_SELECTOR, version, (ScalarFunction) fun, argSelectors); } } diff --git a/src/java/org/apache/cassandra/cql3/selection/SimpleSelector.java b/src/java/org/apache/cassandra/cql3/selection/SimpleSelector.java index 22045ebc3d..befbd2c04e 100644 --- a/src/java/org/apache/cassandra/cql3/selection/SimpleSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/SimpleSelector.java @@ -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 diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java index 010f46412d..7fc1185ed2 100644 --- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java @@ -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; diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java b/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java index 38af812377..1c4088ee97 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java @@ -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 extends TemporalType } @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(); } diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java index bc3dd01e42..c8a3904851 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java @@ -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 implements Comparator, Assignm public abstract TypeSerializer 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 names) { @@ -723,4 +732,26 @@ public abstract class AbstractType implements Comparator, 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); + } + } } diff --git a/src/java/org/apache/cassandra/db/marshal/AsciiType.java b/src/java/org/apache/cassandra/db/marshal/AsciiType.java index ac585a2982..119965abeb 100644 --- a/src/java/org/apache/cassandra/db/marshal/AsciiType.java +++ b/src/java/org/apache/cassandra/db/marshal/AsciiType.java @@ -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() { diff --git a/src/java/org/apache/cassandra/db/marshal/BooleanType.java b/src/java/org/apache/cassandra/db/marshal/BooleanType.java index 99a2da1556..acaba5ba7a 100644 --- a/src/java/org/apache/cassandra/db/marshal/BooleanType.java +++ b/src/java/org/apache/cassandra/db/marshal/BooleanType.java @@ -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 { 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 return BooleanSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return ARGUMENT_DESERIALIZER; + } + @Override public int valueLengthIfFixed() { diff --git a/src/java/org/apache/cassandra/db/marshal/ByteType.java b/src/java/org/apache/cassandra/db/marshal/ByteType.java index 5aa88807df..614bdf9052 100644 --- a/src/java/org/apache/cassandra/db/marshal/ByteType.java +++ b/src/java/org/apache/cassandra/db/marshal/ByteType.java @@ -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 } @Override - public byte toByte(ByteBuffer value) + public ArgumentDeserializer getArgumentDeserializer() { - return ByteBufferUtil.toByte(value); + return new NumberArgumentDeserializer(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 diff --git a/src/java/org/apache/cassandra/db/marshal/BytesType.java b/src/java/org/apache/cassandra/db/marshal/BytesType.java index 5e742e7979..b5c5cc8bdf 100644 --- a/src/java/org/apache/cassandra/db/marshal/BytesType.java +++ b/src/java/org/apache/cassandra/db/marshal/BytesType.java @@ -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 return BytesSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return ArgumentDeserializer.NOOP_DESERIALIZER; + } + @Override public ByteBuffer getMaskedValue() { diff --git a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java index ad02bfb2a4..b5ce7d829e 100644 --- a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java +++ b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java @@ -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 } @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 diff --git a/src/java/org/apache/cassandra/db/marshal/DateType.java b/src/java/org/apache/cassandra/db/marshal/DateType.java index f5f786d80b..ca3e112cee 100644 --- a/src/java/org/apache/cassandra/db/marshal/DateType.java +++ b/src/java/org/apache/cassandra/db/marshal/DateType.java @@ -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 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 return TimestampSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return ARGUMENT_DESERIALIZER; + } + @Override public int valueLengthIfFixed() { diff --git a/src/java/org/apache/cassandra/db/marshal/DecimalType.java b/src/java/org/apache/cassandra/db/marshal/DecimalType.java index 92d688c4e3..51d2561aa6 100644 --- a/src/java/org/apache/cassandra/db/marshal/DecimalType.java +++ b/src/java/org/apache/cassandra/db/marshal/DecimalType.java @@ -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 { 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 } @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 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 } @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 } @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 } @Override - public ByteBuffer round(ByteBuffer input) + public ByteBuffer round(Number input) { return DecimalType.instance.decompose( toBigDecimal(input).setScale(0, RoundingMode.HALF_UP)); diff --git a/src/java/org/apache/cassandra/db/marshal/DoubleType.java b/src/java/org/apache/cassandra/db/marshal/DoubleType.java index 944bab0b53..e1062dc538 100644 --- a/src/java/org/apache/cassandra/db/marshal/DoubleType.java +++ b/src/java/org/apache/cassandra/db/marshal/DoubleType.java @@ -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 return DoubleSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return new NumberArgumentDeserializer(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 } @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 diff --git a/src/java/org/apache/cassandra/db/marshal/DurationType.java b/src/java/org/apache/cassandra/db/marshal/DurationType.java index 1f4a199a0e..0c46617554 100644 --- a/src/java/org/apache/cassandra/db/marshal/DurationType.java +++ b/src/java/org/apache/cassandra/db/marshal/DurationType.java @@ -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 { 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 return DurationSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return ARGUMENT_DESERIALIZER; + } + @Override public CQL3Type asCQL3Type() { diff --git a/src/java/org/apache/cassandra/db/marshal/EmptyType.java b/src/java/org/apache/cassandra/db/marshal/EmptyType.java index 33c45240a3..d9c7c22815 100644 --- a/src/java/org/apache/cassandra/db/marshal/EmptyType.java +++ b/src/java/org/apache/cassandra/db/marshal/EmptyType.java @@ -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 return EmptySerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + throw new UnsupportedOperationException(); + } + @Override public int valueLengthIfFixed() { diff --git a/src/java/org/apache/cassandra/db/marshal/FloatType.java b/src/java/org/apache/cassandra/db/marshal/FloatType.java index 74411dc79b..abed92960d 100644 --- a/src/java/org/apache/cassandra/db/marshal/FloatType.java +++ b/src/java/org/apache/cassandra/db/marshal/FloatType.java @@ -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 return FloatSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return new NumberArgumentDeserializer(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 } @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 diff --git a/src/java/org/apache/cassandra/db/marshal/FrozenType.java b/src/java/org/apache/cassandra/db/marshal/FrozenType.java index 64a5b6ae03..03a80d60fa 100644 --- a/src/java/org/apache/cassandra/db/marshal/FrozenType.java +++ b/src/java/org/apache/cassandra/db/marshal/FrozenType.java @@ -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 { throw new UnsupportedOperationException(); } + + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + throw new UnsupportedOperationException(); + } } diff --git a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java index 7e0e58ce62..820e0c2f77 100644 --- a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java +++ b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java @@ -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 { 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 return InetAddressSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return ARGUMENT_DESERIALIZER; + } + @Override public ByteBuffer getMaskedValue() { diff --git a/src/java/org/apache/cassandra/db/marshal/Int32Type.java b/src/java/org/apache/cassandra/db/marshal/Int32Type.java index 2d70ac4032..68c32ad90e 100644 --- a/src/java/org/apache/cassandra/db/marshal/Int32Type.java +++ b/src/java/org/apache/cassandra/db/marshal/Int32Type.java @@ -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 return Int32Serializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return new NumberArgumentDeserializer(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 } @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 diff --git a/src/java/org/apache/cassandra/db/marshal/IntegerType.java b/src/java/org/apache/cassandra/db/marshal/IntegerType.java index 291895368f..99fac1640d 100644 --- a/src/java/org/apache/cassandra/db/marshal/IntegerType.java +++ b/src/java/org/apache/cassandra/db/marshal/IntegerType.java @@ -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 { 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 } @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 } @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 } @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 } @Override - public ByteBuffer round(ByteBuffer input) + public ByteBuffer round(Number input) { - return ByteBufferUtil.clone(input); + return decompose(toBigInteger(input)); } @Override diff --git a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java index eac94a4542..1dfc997d97 100644 --- a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java @@ -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 { 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 } } + @Override public TypeSerializer getSerializer() { return UUIDSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return ARGUMENT_DESERIALIZER; + } + @Override public int valueLengthIfFixed() { diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java index c8095fc890..e0348fdb11 100644 --- a/src/java/org/apache/cassandra/db/marshal/LongType.java +++ b/src/java/org/apache/cassandra/db/marshal/LongType.java @@ -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 return LongSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return new NumberArgumentDeserializer(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 } @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 diff --git a/src/java/org/apache/cassandra/db/marshal/NumberType.java b/src/java/org/apache/cassandra/db/marshal/NumberType.java index 5b10720cc0..04c6e1dc1b 100644 --- a/src/java/org/apache/cassandra/db/marshal/NumberType.java +++ b/src/java/org/apache/cassandra/db/marshal/NumberType.java @@ -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 extends AbstractType return false; } - /** - * Converts the specified value into a BigInteger 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 BigDecimal. - * - * @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 byte 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 short 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 int 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 long 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 float 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 double 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 extends AbstractType * @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 extends AbstractType * @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 extends AbstractType * 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 extends AbstractType * 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 extends AbstractType * 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 extends AbstractType * 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}. + * + *

    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.

    + * + * @param The Mutable wrapper type + */ + protected abstract static class NumberArgumentDeserializer> 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); + } } diff --git a/src/java/org/apache/cassandra/db/marshal/PartitionerDefinedOrder.java b/src/java/org/apache/cassandra/db/marshal/PartitionerDefinedOrder.java index 1ad1aea544..be4b354ad0 100644 --- a/src/java/org/apache/cassandra/db/marshal/PartitionerDefinedOrder.java +++ b/src/java/org/apache/cassandra/db/marshal/PartitionerDefinedOrder.java @@ -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 throw new IllegalStateException("You shouldn't be validating this."); } + @Override public TypeSerializer 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() { diff --git a/src/java/org/apache/cassandra/db/marshal/ReversedType.java b/src/java/org/apache/cassandra/db/marshal/ReversedType.java index 079836d9b0..89d1adb039 100644 --- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java +++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java @@ -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 extends AbstractType return baseType.asCQL3Type(); } + @Override public TypeSerializer getSerializer() { return baseType.getSerializer(); } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return baseType.getArgumentDeserializer(); + } + @Override public boolean referencesUserType(V name, ValueAccessor accessor) { diff --git a/src/java/org/apache/cassandra/db/marshal/ShortType.java b/src/java/org/apache/cassandra/db/marshal/ShortType.java index 24d8e923a5..96047daf73 100644 --- a/src/java/org/apache/cassandra/db/marshal/ShortType.java +++ b/src/java/org/apache/cassandra/db/marshal/ShortType.java @@ -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 } @Override - public short toShort(ByteBuffer value) + public ArgumentDeserializer getArgumentDeserializer() { - return ByteBufferUtil.toShort(value); + return new NumberArgumentDeserializer(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 diff --git a/src/java/org/apache/cassandra/db/marshal/StringType.java b/src/java/org/apache/cassandra/db/marshal/StringType.java index 29aff58528..d823632b73 100644 --- a/src/java/org/apache/cassandra/db/marshal/StringType.java +++ b/src/java/org/apache/cassandra/db/marshal/StringType.java @@ -27,14 +27,8 @@ public abstract class StringType extends AbstractType 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); } } diff --git a/src/java/org/apache/cassandra/db/marshal/TemporalType.java b/src/java/org/apache/cassandra/db/marshal/TemporalType.java index 945dae04f4..9b6b1ff81d 100644 --- a/src/java/org/apache/cassandra/db/marshal/TemporalType.java +++ b/src/java/org/apache/cassandra/db/marshal/TemporalType.java @@ -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 extends AbstractType * @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 extends AbstractType * @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 extends AbstractType 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; + } + }; + } } diff --git a/src/java/org/apache/cassandra/db/marshal/TimeType.java b/src/java/org/apache/cassandra/db/marshal/TimeType.java index d750095109..67cf7dbceb 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimeType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimeType.java @@ -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 { 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 return CQL3Type.Native.TIME; } + @Override public TypeSerializer getSerializer() { return TimeSerializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return ARGUMENT_DESERIALIZER; + } + @Override public ByteBuffer now() { diff --git a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java b/src/java/org/apache/cassandra/db/marshal/UTF8Type.java index 20de5fcd0f..ca668f7b7b 100644 --- a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java +++ b/src/java/org/apache/cassandra/db/marshal/UTF8Type.java @@ -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 getSerializer() { return UTF8Serializer.instance; } + @Override + public ArgumentDeserializer getArgumentDeserializer() + { + return ARGUMENT_DESERIALIZER; + } + @Override public ByteBuffer getMaskedValue() { diff --git a/src/java/org/apache/cassandra/db/marshal/UUIDType.java b/src/java/org/apache/cassandra/db/marshal/UUIDType.java index 0de904172f..c949524613 100644 --- a/src/java/org/apache/cassandra/db/marshal/UUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/UUIDType.java @@ -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 { 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 return CQL3Type.Native.UUID; } + @Override public TypeSerializer 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) diff --git a/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java b/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java index d0bcafd21c..ad15c991b9 100644 --- a/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java +++ b/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java @@ -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); } /** diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java index d11f8705f9..241ec8dca0 100644 --- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java +++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java @@ -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); diff --git a/src/java/org/apache/cassandra/utils/JavaDriverUtils.java b/src/java/org/apache/cassandra/utils/JavaDriverUtils.java new file mode 100644 index 0000000000..6c0567d03f --- /dev/null +++ b/src/java/org/apache/cassandra/utils/JavaDriverUtils.java @@ -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 codecFor(AbstractType abstractType) + { + return codecFor(driverType(abstractType)); + } + + public static TypeCodec 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() + { + } +} diff --git a/src/resources/org/apache/cassandra/cql3/functions/JavaSourceUDF.txt b/src/resources/org/apache/cassandra/cql3/functions/JavaSourceUDF.txt index fd9ca36677..c528a8c2b4 100644 --- a/src/resources/org/apache/cassandra/cql3/functions/JavaSourceUDF.txt +++ b/src/resources/org/apache/cassandra/cql3/functions/JavaSourceUDF.txt @@ -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 returnCodec, TypeCodec[] argCodecs, UDFContext udfContext) + public #class_name#(UDFDataType returnType, UDFContext udfContext) { - super(returnCodec, argCodecs, udfContext); + super(returnType, udfContext); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List 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 params) + protected Object executeAggregateImpl(Object state, Arguments arguments) { #return_type# result = #execute_internal_name#( #arguments_aggregate# diff --git a/test/microbench/org/apache/cassandra/test/microbench/FunctionWithTerminalArgsBench.java b/test/microbench/org/apache/cassandra/test/microbench/FunctionWithTerminalArgsBench.java new file mode 100644 index 0000000000..ce37836ed5 --- /dev/null +++ b/test/microbench/org/apache/cassandra/test/microbench/FunctionWithTerminalArgsBench.java @@ -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()); + } +} diff --git a/test/unit/org/apache/cassandra/cql3/UDHelperTest.java b/test/unit/org/apache/cassandra/cql3/UDHelperTest.java deleted file mode 100644 index 6e322e1738..0000000000 --- a/test/unit/org/apache/cassandra/cql3/UDHelperTest.java +++ /dev/null @@ -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 - { - 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 getSerializer() - { - return UTF8Type.instance.getSerializer(); - } - - public int compareCustom(VL left, ValueAccessor accessorL, VR right, ValueAccessor 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)); - } - } -} diff --git a/test/unit/org/apache/cassandra/cql3/functions/FunctionFactoryTest.java b/test/unit/org/apache/cassandra/cql3/functions/FunctionFactoryTest.java index 3107d16e75..8db1c182ac 100644 --- a/test/unit/org/apache/cassandra/cql3/functions/FunctionFactoryTest.java +++ b/test/unit/org/apache/cassandra/cql3/functions/FunctionFactoryTest.java @@ -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 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 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()); } }; } diff --git a/test/unit/org/apache/cassandra/cql3/functions/MathFctsTest.java b/test/unit/org/apache/cassandra/cql3/functions/MathFctsTest.java index daefc0aedf..5cc42fd4e9 100644 --- a/test/unit/org/apache/cassandra/cql3/functions/MathFctsTest.java +++ b/test/unit/org/apache/cassandra/cql3/functions/MathFctsTest.java @@ -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 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 void assertFctEquals(NativeFunction fct, T expected, NumberType inputType, T inputValue) diff --git a/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java b/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java index b0cc1e03ae..35a21e351d 100644 --- a/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java +++ b/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java @@ -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 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); } } diff --git a/test/unit/org/apache/cassandra/cql3/functions/masking/ColumnMaskTest.java b/test/unit/org/apache/cassandra/cql3/functions/masking/ColumnMaskTest.java index d7e10e27be..bf0a9156cd 100644 --- a/test/unit/org/apache/cassandra/cql3/functions/masking/ColumnMaskTest.java +++ b/test/unit/org/apache/cassandra/cql3/functions/masking/ColumnMaskTest.java @@ -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); } }; } diff --git a/test/unit/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunctionTest.java b/test/unit/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunctionTest.java index 25f75a686d..89d20d0d58 100644 --- a/test/unit/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunctionTest.java +++ b/test/unit/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunctionTest.java @@ -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); } } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFJavaTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFJavaTest.java index ad59b4cc60..ccb1513f19 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFJavaTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFJavaTest.java @@ -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 diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java index fe1ec706e2..05b353acf2 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java @@ -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) { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java index 1a9e6d2e20..0a247738e4 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java @@ -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", 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", JavaBasedUDFunction.javaSourceName(TypeTokens.setOf(UDTValue.class))); - } - @Test public void testNonExistingOnes() throws Throwable { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallClone.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallClone.java index 1d131888dc..b9175b2f92 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallClone.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallClone.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public CallClone(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { try { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallFinalize.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallFinalize.java index 786129f266..e4c05b3bbd 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallFinalize.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallFinalize.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public CallFinalize(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { try { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallOrgApache.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallOrgApache.java index 77664ac0fc..3c331a07cd 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallOrgApache.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/CallOrgApache.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public CallOrgApache(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { DatabaseDescriptor.getClusterName(); return null; diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithField.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithField.java index 5fa12ed868..ee4ec96c7f 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithField.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithField.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public ClassWithField(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { return null; } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer.java index e1c866a5d3..57c6560e3e 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public ClassWithInitializer(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { return null; } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer2.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer2.java index 15d60dee83..41e134a39f 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer2.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer2.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public ClassWithInitializer2(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { return null; } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer3.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer3.java index fc5d61c869..677ae07c2b 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer3.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInitializer3.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public ClassWithInitializer3(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { return null; } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInnerClass.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInnerClass.java index b387061782..bee00bb708 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInnerClass.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInnerClass.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public ClassWithInnerClass(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { return null; } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInnerClass2.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInnerClass2.java index 0d2db3288e..ebf711a368 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInnerClass2.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithInnerClass2.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public ClassWithInnerClass2(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { // this is fine new Runnable() { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithStaticInitializer.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithStaticInitializer.java index bc7b2532bc..6278d64894 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithStaticInitializer.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithStaticInitializer.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public ClassWithStaticInitializer(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { return null; } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithStaticInnerClass.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithStaticInnerClass.java index 2b6562139e..e467878c9f 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithStaticInnerClass.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/ClassWithStaticInnerClass.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public ClassWithStaticInnerClass(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { return null; } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/GoodClass.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/GoodClass.java index 4bab493496..23aaa4ece1 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/GoodClass.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/GoodClass.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public GoodClass(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { return null; } diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronized.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronized.java index 2c39a83896..7c045bfff8 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronized.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronized.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public UseOfSynchronized(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { synchronized (this) { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithNotify.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithNotify.java index 6240ad1ff5..5876ed1391 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithNotify.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithNotify.java @@ -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 returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public UseOfSynchronizedWithNotify(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { synchronized (this) { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithNotifyAll.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithNotifyAll.java index 60e81aa287..c6a06b790d 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithNotifyAll.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithNotifyAll.java @@ -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 UseOfSynchronizedWithNotifyAll extends JavaUDF { - public UseOfSynchronizedWithNotifyAll(TypeCodec returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public UseOfSynchronizedWithNotifyAll(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { synchronized (this) { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWait.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWait.java index 71951bb783..60c8f242f1 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWait.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWait.java @@ -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 UseOfSynchronizedWithWait extends JavaUDF { - public UseOfSynchronizedWithWait(TypeCodec returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public UseOfSynchronizedWithWait(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { synchronized (this) { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWaitL.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWaitL.java index 13e1501825..c22aecd04a 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWaitL.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWaitL.java @@ -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 UseOfSynchronizedWithWaitL extends JavaUDF { - public UseOfSynchronizedWithWaitL(TypeCodec returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public UseOfSynchronizedWithWaitL(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { synchronized (this) { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWaitLI.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWaitLI.java index b0b344d798..bca76ddcd4 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWaitLI.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UseOfSynchronizedWithWaitLI.java @@ -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 UseOfSynchronizedWithWaitLI extends JavaUDF { - public UseOfSynchronizedWithWaitLI(TypeCodec returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public UseOfSynchronizedWithWaitLI(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { synchronized (this) { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UsingMapEntry.java b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UsingMapEntry.java index cf6ee64b53..c8271ea62a 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UsingMapEntry.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/udfverify/UsingMapEntry.java @@ -20,30 +20,31 @@ package org.apache.cassandra.cql3.validation.entities.udfverify; import java.nio.ByteBuffer; import java.util.HashMap; -import java.util.List; import java.util.Map; -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 UsingMapEntry extends JavaUDF { - public UsingMapEntry(TypeCodec returnDataType, TypeCodec[] argDataTypes, UDFContext udfContext) + public UsingMapEntry(UDFDataType returnType, UDFContext udfContext) { - super(returnDataType, argDataTypes, udfContext); + super(returnType, udfContext); } - protected Object executeAggregateImpl(ProtocolVersion protocolVersion, Object firstParam, List params) + @Override + protected Object executeAggregateImpl(Object state, Arguments arguments) { throw new UnsupportedOperationException(); } - protected ByteBuffer executeImpl(ProtocolVersion protocolVersion, List params) + @Override + protected ByteBuffer executeImpl(Arguments arguments) { Map map = new HashMap<>(); // Map.Entry is passed in as an "inner class usage" diff --git a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java index 5d81d6ccfa..204ca865b1 100644 --- a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java @@ -43,7 +43,6 @@ import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; import org.apache.cassandra.config.*; import org.apache.cassandra.cql3.*; -import org.apache.cassandra.cql3.functions.UDHelper; import org.apache.cassandra.cql3.functions.types.*; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.commitlog.CommitLog; @@ -621,8 +620,8 @@ public class CQLSSTableWriterTest loadSSTables(dataDir, keyspace); UntypedResultSet resultSet = QueryProcessor.executeInternal("SELECT * FROM " + keyspace + "." + table); - TypeCodec collectionCodec = UDHelper.codecFor(DataType.CollectionType.list(tuple2Type)); - TypeCodec tuple3Codec = UDHelper.codecFor(tuple3Type); + TypeCodec collectionCodec = JavaDriverUtils.codecFor(DataType.CollectionType.list(tuple2Type)); + TypeCodec tuple3Codec = JavaDriverUtils.codecFor(tuple3Type); assertEquals(resultSet.size(), 100); int cnt = 0; @@ -666,8 +665,8 @@ public class CQLSSTableWriterTest UserType tuple2Type = writer.getUDType("tuple2"); UserType nestedTuple = writer.getUDType("nested_tuple"); - TypeCodec tuple2Codec = UDHelper.codecFor(tuple2Type); - TypeCodec nestedTupleCodec = UDHelper.codecFor(nestedTuple); + TypeCodec tuple2Codec = JavaDriverUtils.codecFor(tuple2Type); + TypeCodec nestedTupleCodec = JavaDriverUtils.codecFor(nestedTuple); for (int i = 0; i < 100; i++) { diff --git a/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java b/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java index cdd05ec3e7..30497fd687 100644 --- a/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java +++ b/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java @@ -41,7 +41,6 @@ import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.CqlParser; import org.apache.cassandra.cql3.QueryOptions; 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.statements.UpdateStatement; import org.apache.cassandra.cql3.statements.schema.CreateTableStatement; @@ -67,6 +66,7 @@ import org.apache.cassandra.schema.Types; 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 StressCQLSSTableWriter implements Closeable this.writer = writer; this.insert = insert; 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()); }