From 8ddcd43b0cfcebfda882a238532d00905fe85eb8 Mon Sep 17 00:00:00 2001 From: Kanthi Subramanian Date: Sun, 17 Oct 2021 17:18:58 -0400 Subject: [PATCH] Add unix time conversion functions Patch by Kanthi Subramanian; reviewed by Benjamin Lerer and Brandon Williams for CASSANDRA-17029 --- CHANGES.txt | 1 + NEWS.txt | 2 + .../cassandra/cql3/functions/TimeFcts.java | 79 ++++++++++++++++++- .../apache/cassandra/db/marshal/LongType.java | 2 +- .../cql3/functions/TimeFctsTest.java | 39 +++++++++ .../validation/entities/TimeuuidTest.java | 2 +- 6 files changed, 122 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6ca209e000..f4369173b0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.1 + * Add unix time conversion functions (CASSANDRA-17029) * JVMStabilityInspector.forceHeapSpaceOomMaybe should handle all non-heap OOMs rather than only supporting direct only (CASSANDRA-17128) * Forbid other Future implementations with checkstyle (CASSANDRA-17055) * commit log was switched from non-daemon to daemon threads, which causes the JVM to exit in some case as no non-daemon threads are active (CASSANDRA-17085) diff --git a/NEWS.txt b/NEWS.txt index d950100aa7..db91c27d55 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -38,6 +38,8 @@ using the provided 'sstableupgrade' tool. New features ------------ + - New native functions to convert unix time values into C* native types: toDate(bigint), toTimestamp(bigint), + mintimeuuid(bigint) and maxtimeuuid(bigint) - Support for multiple permission in a single GRANT/REVOKE/LIST statement has been added. It allows to grant/revoke/list multiple permissions using a single statement by providing a list of comma-separated permissions. diff --git a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java index f029e59de8..331eaa19af 100644 --- a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java @@ -42,14 +42,18 @@ public abstract class TimeFcts now("currentdate", SimpleDateType.instance), now("currenttime", TimeType.instance), minTimeuuidFct, + minTimeuuidFct(LongType.instance), maxTimeuuidFct, + maxTimeuuidFct(LongType.instance), dateOfFct, unixTimestampOfFct, toDate(TimeUUIDType.instance), toTimestamp(TimeUUIDType.instance), + toTimestamp(LongType.instance), toUnixTimestamp(TimeUUIDType.instance), toUnixTimestamp(TimestampType.instance), toDate(TimestampType.instance), + toDate(LongType.instance), toUnixTimestamp(SimpleDateType.instance), toTimestamp(SimpleDateType.instance)); } @@ -64,7 +68,7 @@ public abstract class TimeFcts return type.now(); } }; - }; + } public static final Function minTimeuuidFct = new NativeScalarFunction("mintimeuuid", TimeUUIDType.instance, TimestampType.instance) { @@ -78,6 +82,21 @@ public abstract class TimeFcts } }; + public static final NativeScalarFunction minTimeuuidFct(final LongType type) + { + return new NativeScalarFunction("mintimeuuid", TimeUUIDType.instance, type) + { + public ByteBuffer execute(ProtocolVersion protocolVersion, List parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + return UUIDGen.toByteBuffer(UUIDGen.minTimeUUID(LongType.instance.toLong(bb))); + } + }; + } + public static final Function maxTimeuuidFct = new NativeScalarFunction("maxtimeuuid", TimeUUIDType.instance, TimestampType.instance) { public ByteBuffer execute(ProtocolVersion protocolVersion, List parameters) @@ -90,6 +109,21 @@ public abstract class TimeFcts } }; + public static NativeScalarFunction maxTimeuuidFct(final LongType type) + { + return new NativeScalarFunction("maxtimeuuid", TimeUUIDType.instance, type) + { + public ByteBuffer execute(ProtocolVersion protocolVersion, List parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + return UUIDGen.toByteBuffer(UUIDGen.maxTimeUUID(LongType.instance.toLong(bb))); + } + }; + } + /** * Function that convert a value of TIMEUUID into a value of type TIMESTAMP. * @deprecated Replaced by the {@link #timeUuidToTimestamp} function @@ -162,6 +196,28 @@ public abstract class TimeFcts }; } + /** + * Creates a function that convert a value of CQL(bigint) into a DATE. + * @param type the temporal type + * @return a function that convert CQL(bigint) into a DATE. + */ + public static NativeScalarFunction toDate(final LongType type) + { + return new NativeScalarFunction("todate", SimpleDateType.instance, type) + { + public ByteBuffer execute(ProtocolVersion protocolVersion, List parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null || !bb.hasRemaining()) + return null; + + long millis = LongType.instance.toLong(bb); + return SimpleDateType.instance.fromTimeInMillis(millis); + } + }; + } + + /** * Creates a function that convert a value of the specified type into a TIMESTAMP. * @param type the temporal type @@ -183,6 +239,27 @@ public abstract class TimeFcts }; } + /** + * Creates a function that convert a value of the specified type into a TIMESTAMP. + * @param type the temporal type + * @return a function that convert a value of the specified type into a TIMESTAMP. + */ + public static NativeScalarFunction toTimestamp(final LongType type) + { + return new NativeScalarFunction("totimestamp", TimestampType.instance, type) + { + public ByteBuffer execute(ProtocolVersion protocolVersion, List parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null || !bb.hasRemaining()) + return null; + + long millis = LongType.instance.toLong(bb); + return TimestampType.instance.fromTimeInMillis(millis); + } + }; + } + /** * Creates a function that convert a value of the specified type into an UNIX timestamp. * @param type the temporal type diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java index ad539f70de..d4b1400bc5 100644 --- a/src/java/org/apache/cassandra/db/marshal/LongType.java +++ b/src/java/org/apache/cassandra/db/marshal/LongType.java @@ -139,7 +139,7 @@ public class LongType extends NumberType } @Override - protected long toLong(ByteBuffer value) + public long toLong(ByteBuffer value) { return ByteBufferUtil.toLong(value); } diff --git a/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java b/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java index b0a4bb98ea..25fb290c69 100644 --- a/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java +++ b/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java @@ -26,6 +26,7 @@ import java.util.List; import org.junit.Test; +import org.apache.cassandra.db.marshal.IntegerType; import org.apache.cassandra.db.marshal.LongType; import org.apache.cassandra.db.marshal.SimpleDateType; import org.apache.cassandra.db.marshal.TimeUUIDType; @@ -59,6 +60,15 @@ public class TimeFctsTest assertEquals(UUIDGen.minTimeUUID(timeInMillis), TimeUUIDType.instance.compose(output)); } + @Test + public void testMinTimeUuidFromBigInt() + { + long timeInMillis = DATE_TIME.toInstant().toEpochMilli(); + ByteBuffer input = LongType.instance.decompose(timeInMillis); + ByteBuffer output = executeFunction(TimeFcts.minTimeuuidFct(LongType.instance), input); + assertEquals(UUIDGen.minTimeUUID(timeInMillis), TimeUUIDType.instance.compose(output)); + } + @Test public void testMaxTimeUuid() { @@ -68,6 +78,15 @@ public class TimeFctsTest assertEquals(UUIDGen.maxTimeUUID(timeInMillis), TimeUUIDType.instance.compose(output)); } + @Test + public void testMaxTimeUuidFromBigInt() + { + long timeInMillis = DATE_TIME.toInstant().toEpochMilli(); + ByteBuffer input = LongType.instance.decompose(timeInMillis); + ByteBuffer output = executeFunction(TimeFcts.maxTimeuuidFct(LongType.instance), input); + assertEquals(UUIDGen.maxTimeUUID(timeInMillis), TimeUUIDType.instance.compose(output)); + } + @Test public void testDateOf() { @@ -141,6 +160,16 @@ public class TimeFctsTest assertEquals(DATE.toInstant().toEpochMilli(), SimpleDateType.instance.toTimeInMillis(output)); } + @Test + public void testBigIntegerToDate() + { + long millis = DATE.toInstant().toEpochMilli(); + + ByteBuffer input = LongType.instance.decompose(millis); + ByteBuffer output = executeFunction(toDate(LongType.instance), input); + assertEquals(DATE.toInstant().toEpochMilli(), SimpleDateType.instance.toTimeInMillis(output)); + } + @Test public void testTimestampToDateWithEmptyInput() { @@ -156,6 +185,16 @@ public class TimeFctsTest assertEquals(DATE_TIME.toInstant().toEpochMilli(), LongType.instance.compose(output).longValue()); } + @Test + public void testBigIntegerToTimestamp() + { + long millis = DATE_TIME.toInstant().toEpochMilli(); + + ByteBuffer input = LongType.instance.decompose(millis); + ByteBuffer output = executeFunction(toTimestamp(LongType.instance), input); + assertEquals(DATE_TIME.toInstant().toEpochMilli(), LongType.instance.compose(output).longValue()); + } + @Test public void testTimestampToUnixTimestampWithEmptyInput() { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/TimeuuidTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/TimeuuidTest.java index 0f1f8f04f0..40ab4796e6 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/TimeuuidTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TimeuuidTest.java @@ -64,7 +64,7 @@ public class TimeuuidTest extends CQLTester row(new Date(timestamp), timestamp)); } - assertEmpty(execute("SELECT t FROM %s WHERE k = 0 AND t > maxTimeuuid(1234567) AND t < minTimeuuid('2012-11-07 18:18:22-0800')")); + assertEmpty(execute("SELECT t FROM %s WHERE k = 0 AND t > maxTimeuuid(1564830182000) AND t < minTimeuuid('2012-11-07 18:18:22-0800')")); } /**