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