Skip to content

Commit

Permalink
Add Mathematical Functions
Browse files Browse the repository at this point in the history
Patch by Simon Chess; review by Benjamin Lerer Ekaterina Dimitrova for CASSANDRA-17221

This patch add the abs, exp, log, log10, and round functions for the numeric types.
  • Loading branch information
XV4DE authored and blerer committed Nov 22, 2022
1 parent 48d4897 commit 88dc64d
Show file tree
Hide file tree
Showing 18 changed files with 859 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .build/cassandra-deps-template.xml
Expand Up @@ -340,6 +340,10 @@
<groupId>com.github.seancfoley</groupId>
<artifactId>ipaddress</artifactId>
</dependency>
<dependency>
<groupId>ch.obermuhlner</groupId>
<artifactId>big-math</artifactId>
</dependency>
<dependency>
<groupId>org.agrona</groupId>
<artifactId>agrona</artifactId>
Expand Down
5 changes: 5 additions & 0 deletions .build/parent-pom-template.xml
Expand Up @@ -1029,6 +1029,11 @@
<artifactId>agrona</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>ch.obermuhlner</groupId>
<artifactId>big-math</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
1 change: 1 addition & 0 deletions CHANGES.txt
@@ -1,4 +1,5 @@
4.2
* Add Mathematical functions (CASSANDRA-17221)
* Make incremental backup configurable per table (CASSANDRA-15402)
* Change shebangs of Python scripts to resolve Python 3 from env command (CASSANDRA-17832)
* Add reasons to guardrail messages and consider guardrails in the error message for needed ALLOW FILTERING (CASSANDRA-17967)
Expand Down
1 change: 1 addition & 0 deletions NEWS.txt
Expand Up @@ -57,6 +57,7 @@ using the provided 'sstableupgrade' tool.

New features
------------
- Added new Mathematical CQL functions: abs, exp, log, log10 and round.
- Adds a trie-based memtable implementation, which improves memory use, garbage collection efficiency and lookup
performance. The new memtable is implemented by the TrieMemtable class and can be selected using the memtable
API, see src/java/org/apache/cassandra/db/memtable/Memtable_API.md.
Expand Down
20 changes: 20 additions & 0 deletions doc/modules/cassandra/pages/cql/functions.adoc
Expand Up @@ -238,6 +238,26 @@ For every xref:cql/types.adoc#native-types[type] supported by CQL, the function
Conversely, the function `blobAsType` takes a 64-bit `blob` argument and converts it to a `bigint` value.
For example, `bigintAsBlob(3)` returns `0x0000000000000003` and `blobAsBigint(0x0000000000000003)` returns `3`.

==== Math Functions

Cql provides the following math functions: `abs`, `exp`, `log`, `log10`, and `round`.
The return type for these functions is always the same as the input type.

[cols=",",options="header",]
|===
|Function name |Description

|`abs` | Returns the absolute value of the input.

|`exp` | Returns the number e to the power of the input.

|`log` | Returns the natural log of the input.

|`log10` | Returns the log base 10 of the input.

|`round` | Rounds the input to the nearest whole number using rounding mode `HALF_UP`.
|===

[[user-defined-scalar-functions]]
==== User-defined functions

Expand Down
134 changes: 134 additions & 0 deletions src/java/org/apache/cassandra/cql3/functions/MathFcts.java
@@ -0,0 +1,134 @@
/*
* 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.Collection;
import java.util.List;

import com.google.common.collect.ImmutableList;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.transport.ProtocolVersion;

public final class MathFcts
{
public static void addFunctionsTo(NativeFunctions functions)
{
final List<NumberType<?>> numericTypes = ImmutableList.of(ByteType.instance,
ShortType.instance,
Int32Type.instance,
FloatType.instance,
LongType.instance,
DoubleType.instance,
IntegerType.instance,
DecimalType.instance,
CounterColumnType.instance);

numericTypes.stream()
.map(t -> ImmutableList.of(absFct(t),
expFct(t),
logFct(t),
log10Fct(t),
roundFct(t)))
.flatMap(Collection::stream)
.forEach(f -> functions.add(f));
}

public static NativeFunction absFct(final NumberType<?> type)
{
return new NativeScalarFunction("abs", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.abs(bb);
}
};
}

public static NativeFunction expFct(final NumberType<?> type)
{
return new NativeScalarFunction("exp", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.exp(bb);
}
};
}

public static NativeFunction logFct(final NumberType<?> type)
{
return new NativeScalarFunction("log", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.log(bb);

}
};
}

public static NativeFunction log10Fct(final NumberType<?> type)
{
return new NativeScalarFunction("log10", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.log10(bb);

}
};
}

public static NativeFunction roundFct(final NumberType<?> type)
{
return new NativeScalarFunction("round", type, type)
{
@Override
public ByteBuffer execute(ProtocolVersion protocolVersion, List<ByteBuffer> parameters)
{
ByteBuffer bb = parameters.get(0);
if (bb == null)
return null;
return type.round(bb);

}
};
}

private MathFcts()
{
}
}
Expand Up @@ -41,6 +41,7 @@ public class NativeFunctions
OperationFcts.addFunctionsTo(this);
AggregateFcts.addFunctionsTo(this);
BytesConversionFcts.addFunctionsTo(this);
MathFcts.addFunctionsTo(this);
}
};

Expand Down
30 changes: 30 additions & 0 deletions src/java/org/apache/cassandra/db/marshal/ByteType.java
Expand Up @@ -153,4 +153,34 @@ public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes((byte) -toByte(input));
}

@Override
public ByteBuffer abs(ByteBuffer input)
{
return ByteBufferUtil.bytes((byte) Math.abs(toByte(input)));
}

@Override
public ByteBuffer exp(ByteBuffer input)
{
return ByteBufferUtil.bytes((byte) Math.exp(toByte(input)));
}

@Override
public ByteBuffer log(ByteBuffer input)
{
return ByteBufferUtil.bytes((byte) Math.log(toByte(input)));
}

@Override
public ByteBuffer log10(ByteBuffer input)
{
return ByteBufferUtil.bytes((byte) Math.log10(toByte(input)));
}

@Override
public ByteBuffer round(ByteBuffer input)
{
return ByteBufferUtil.clone(input);
}
}
30 changes: 30 additions & 0 deletions src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
Expand Up @@ -128,4 +128,34 @@ public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes(-toLong(input));
}

@Override
public ByteBuffer abs(ByteBuffer input)
{
return ByteBufferUtil.bytes(Math.abs(toLong(input)));
}

@Override
public ByteBuffer exp(ByteBuffer input)
{
return ByteBufferUtil.bytes((long) Math.exp(toLong(input)));
}

@Override
public ByteBuffer log(ByteBuffer input)
{
return ByteBufferUtil.bytes((long) Math.log(toLong(input)));
}

@Override
public ByteBuffer log10(ByteBuffer input)
{
return ByteBufferUtil.bytes((long) Math.log10(toLong(input)));
}

@Override
public ByteBuffer round(ByteBuffer input)
{
return ByteBufferUtil.clone(input);
}
}
59 changes: 59 additions & 0 deletions src/java/org/apache/cassandra/db/marshal/DecimalType.java
Expand Up @@ -37,6 +37,8 @@
import org.apache.cassandra.utils.bytecomparable.ByteComparable;
import org.apache.cassandra.utils.bytecomparable.ByteSource;

import ch.obermuhlner.math.big.BigDecimalMath;

public class DecimalType extends NumberType<BigDecimal>
{
public static final DecimalType instance = new DecimalType();
Expand Down Expand Up @@ -390,4 +392,61 @@ public ByteBuffer negate(ByteBuffer input)
{
return decompose(toBigDecimal(input).negate());
}

@Override
public ByteBuffer abs(ByteBuffer input)
{
return decompose(toBigDecimal(input).abs());
}

@Override
public ByteBuffer exp(ByteBuffer input)
{
return decompose(exp(toBigDecimal(input)));
}

protected BigDecimal exp(BigDecimal input)
{
int precision = input.precision();
precision = Math.max(MIN_SIGNIFICANT_DIGITS, precision);
precision = Math.min(MAX_PRECISION.getPrecision(), precision);
return BigDecimalMath.exp(input, new MathContext(precision, RoundingMode.HALF_EVEN));
}

@Override
public ByteBuffer log(ByteBuffer input)
{
return decompose(log(toBigDecimal(input)));
}

protected BigDecimal log(BigDecimal input)
{
if (input.compareTo(BigDecimal.ZERO) <= 0) throw new ArithmeticException("Natural log of number zero or less");
int precision = input.precision();
precision = Math.max(MIN_SIGNIFICANT_DIGITS, precision);
precision = Math.min(MAX_PRECISION.getPrecision(), precision);
return BigDecimalMath.log(input, new MathContext(precision, RoundingMode.HALF_EVEN));
}

@Override
public ByteBuffer log10(ByteBuffer input)
{
return decompose(log10(toBigDecimal(input)));
}

protected BigDecimal log10(BigDecimal input)
{
if (input.compareTo(BigDecimal.ZERO) <= 0) throw new ArithmeticException("Log10 of number zero or less");
int precision = input.precision();
precision = Math.max(MIN_SIGNIFICANT_DIGITS, precision);
precision = Math.min(MAX_PRECISION.getPrecision(), precision);
return BigDecimalMath.log10(input, new MathContext(precision, RoundingMode.HALF_EVEN));
}

@Override
public ByteBuffer round(ByteBuffer input)
{
return DecimalType.instance.decompose(
toBigDecimal(input).setScale(0, RoundingMode.HALF_UP));
}
}
30 changes: 30 additions & 0 deletions src/java/org/apache/cassandra/db/marshal/DoubleType.java
Expand Up @@ -179,4 +179,34 @@ public ByteBuffer negate(ByteBuffer input)
{
return ByteBufferUtil.bytes(-toDouble(input));
}

@Override
public ByteBuffer abs(ByteBuffer input)
{
return ByteBufferUtil.bytes(Math.abs(toDouble(input)));
}

@Override
public ByteBuffer exp(ByteBuffer input)
{
return ByteBufferUtil.bytes(Math.exp(toDouble(input)));
}

@Override
public ByteBuffer log(ByteBuffer input)
{
return ByteBufferUtil.bytes(Math.log(toDouble(input)));
}

@Override
public ByteBuffer log10(ByteBuffer input)
{
return ByteBufferUtil.bytes(Math.log10(toDouble(input)));
}

@Override
public ByteBuffer round(ByteBuffer input)
{
return ByteBufferUtil.bytes((double) Math.round(toDouble(input)));
}
}

0 comments on commit 88dc64d

Please sign in to comment.