Add functions to convert timeuuid to date or time, deprecate dateOf and unixTimestampOf
patch by Benjamin Lerer; reviewed by Robert Stupp for CASSANDRA-9229 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c08aaabd Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c08aaabd Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c08aaabd Branch: refs/heads/trunk Commit: c08aaabd95d4872593c29807de6ec1485cefa7fa Parents: 6dfde0e Author: Benjamin Lerer <benjamin.le...@datastax.com> Authored: Thu Jun 11 10:18:05 2015 +0200 Committer: Robert Stupp <sn...@snazy.de> Committed: Thu Jun 11 10:18:05 2015 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + NEWS.txt | 7 + doc/cql3/CQL.textile | 17 +- .../cassandra/cql3/functions/Functions.java | 17 +- .../cassandra/cql3/functions/TimeFcts.java | 229 +++++++++++++++++++ .../cassandra/cql3/functions/TimeuuidFcts.java | 88 ------- .../cassandra/db/marshal/SimpleDateType.java | 10 + .../cassandra/db/marshal/TimestampType.java | 5 + .../repair/SystemDistributedKeyspace.java | 12 +- .../serializers/SimpleDateSerializer.java | 18 +- .../apache/cassandra/cql3/AggregationTest.java | 12 +- .../org/apache/cassandra/cql3/TypeTest.java | 9 +- test/unit/org/apache/cassandra/cql3/UFTest.java | 10 +- .../cassandra/cql3/functions/TimeFctsTest.java | 206 +++++++++++++++++ 14 files changed, 523 insertions(+), 118 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 355eefb..0a03e60 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -24,6 +24,7 @@ * Revert CASSANDRA-7807 (tracing completion client notifications) (CASSANDRA-9429) * Add ability to stop compaction by ID (CASSANDRA-7207) * Let CassandraVersion handle SNAPSHOT version (CASSANDRA-9438) + * Add functions to convert timeuuid to date or time, deprecate dateOf and unixTimestampOf (CASSANDRA-9229) Merged from 2.1: * Make nodetool exit with non-0 status on failure (CASSANDRA-9569) * (cqlsh) Fix using COPY through SOURCE or -f (CASSANDRA-9083) http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/NEWS.txt ---------------------------------------------------------------------- diff --git a/NEWS.txt b/NEWS.txt index 9beb911..3c71310 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -88,6 +88,13 @@ New features - New `ShortType` (cql smallint). 2-byte signed integer - New `SimpleDateType` (cql date). 4-byte unsigned integer - New `TimeType` (cql time). 8-byte long + - The toDate(timeuuid), toTimestamp(timeuuid) and toUnixTimestamp(timeuuid) functions have been added to allow + to convert from timeuuid into date type, timestamp type and bigint raw value. + The functions unixTimestampOf(timeuuid) and dateOf(timeuuid) have been deprecated. + - The toDate(timestamp) and toUnixTimestamp(timestamp) functions have been added to allow + to convert from timestamp into date type and bigint raw value. + - The toTimestamp(date) and toUnixTimestamp(date) functions have been added to allow + to convert from date into timestamp type and bigint raw value. Upgrading http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/doc/cql3/CQL.textile ---------------------------------------------------------------------- diff --git a/doc/cql3/CQL.textile b/doc/cql3/CQL.textile index 9cf7b23..3755a2d 100644 --- a/doc/cql3/CQL.textile +++ b/doc/cql3/CQL.textile @@ -1819,9 +1819,20 @@ will select all rows where the @timeuuid@ column @t@ is strictly older than '201 _Warning_: We called the values generated by @minTimeuuid@ and @maxTimeuuid@ _fake_ UUID because they do no respect the Time-Based UUID generation process specified by the "RFC 4122":http://www.ietf.org/rfc/rfc4122.txt. In particular, the value returned by these 2 methods will not be unique. This means you should only use those methods for querying (as in the example above). Inserting the result of those methods is almost certainly _a bad idea_. -h4. @dateOf@ and @unixTimestampOf@ - -The @dateOf@ and @unixTimestampOf@ functions take a @timeuuid@ argument and extract the embedded timestamp. However, while the @dateof@ function return it with the @timestamp@ type (that most client, including cqlsh, interpret as a date), the @unixTimestampOf@ function returns it as a @bigint@ raw value. +h3(#timeFun). Time conversion functions + +A number of functions are provided to "convert" a @timeuuid@, a @timestamp@ or a @date@ into another @native@ type. + +|_. function name |_. input type |_. description| +|@toDate@ |@timeuuid@ |Converts the @timeuuid@ argument into a @date@ type| +|@toDate@ |@timestamp@ |Converts the @timestamp@ argument into a @date@ type| +|@toTimestamp@ |@timeuuid@ |Converts the @timeuuid@ argument into a @timestamp@ type| +|@toTimestamp@ |@date@ |Converts the @date@ argument into a @timestamp@ type| +|@toUnixTimestamp@ |@timeuuid@ |Converts the @timeuuid@ argument into a @bigInt@ raw value| +|@toUnixTimestamp@ |@timestamp@ |Converts the @timestamp@ argument into a @bigInt@ raw value| +|@toUnixTimestamp@ |@date@ |Converts the @date@ argument into a @bigInt@ raw value| +|@dateOf@ |@timeuuid@ |Similar to @toTimestamp(timeuuid)@ (DEPRECATED)| +|@unixTimestampOf@ |@timeuuid@ |Similar to @toUnixTimestamp(timeuuid)@ (DEPRECATED)| h3(#blobFun). Blob conversion functions http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/src/java/org/apache/cassandra/cql3/functions/Functions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/Functions.java b/src/java/org/apache/cassandra/cql3/functions/Functions.java index 7ac8039..c940787 100644 --- a/src/java/org/apache/cassandra/cql3/functions/Functions.java +++ b/src/java/org/apache/cassandra/cql3/functions/Functions.java @@ -46,11 +46,18 @@ public abstract class Functions static { declare(AggregateFcts.countRowsFunction); - declare(TimeuuidFcts.nowFct); - declare(TimeuuidFcts.minTimeuuidFct); - declare(TimeuuidFcts.maxTimeuuidFct); - declare(TimeuuidFcts.dateOfFct); - declare(TimeuuidFcts.unixTimestampOfFct); + declare(TimeFcts.nowFct); + declare(TimeFcts.minTimeuuidFct); + declare(TimeFcts.maxTimeuuidFct); + declare(TimeFcts.dateOfFct); + declare(TimeFcts.unixTimestampOfFct); + declare(TimeFcts.timeUuidtoDate); + declare(TimeFcts.timeUuidToTimestamp); + declare(TimeFcts.timeUuidToUnixTimestamp); + declare(TimeFcts.timestampToDate); + declare(TimeFcts.timestampToUnixTimestamp); + declare(TimeFcts.dateToTimestamp); + declare(TimeFcts.dateToUnixTimestamp); declare(UuidFcts.uuidFct); for (CQL3Type type : CQL3Type.Native.values()) http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java new file mode 100644 index 0000000..a4623cd --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java @@ -0,0 +1,229 @@ +/* + * 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.Date; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.cassandra.db.marshal.*; +import org.apache.cassandra.serializers.TimestampSerializer; +import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.utils.UUIDGen; + +public abstract class TimeFcts +{ + public static Logger logger = LoggerFactory.getLogger(TimeFcts.class); + + public static final Function nowFct = new NativeScalarFunction("now", TimeUUIDType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + return ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()); + } + }; + + public static final Function minTimeuuidFct = new NativeScalarFunction("mintimeuuid", TimeUUIDType.instance, TimestampType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + return ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.minTimeUUID(TimestampType.instance.compose(bb).getTime()))); + } + }; + + public static final Function maxTimeuuidFct = new NativeScalarFunction("maxtimeuuid", TimeUUIDType.instance, TimestampType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + return ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.maxTimeUUID(TimestampType.instance.compose(bb).getTime()))); + } + }; + + /** + * Function that convert a value of <code>TIMEUUID</code> into a value of type <code>TIMESTAMP</code>. + * @deprecated Replaced by the {@link #timeUuidToTimestamp} function + */ + public static final Function dateOfFct = new NativeScalarFunction("dateof", TimestampType.instance, TimeUUIDType.instance) + { + private volatile boolean hasLoggedDeprecationWarning; + + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + if (!hasLoggedDeprecationWarning) + { + hasLoggedDeprecationWarning = true; + logger.warn("The function 'dateof' is deprecated." + + " Use the function 'toTimestamp' instead."); + } + + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + long timeInMillis = UUIDGen.unixTimestamp(UUIDGen.getUUID(bb)); + return ByteBufferUtil.bytes(timeInMillis); + } + }; + + /** + * Function that convert a value of type <code>TIMEUUID</code> into an UNIX timestamp. + * @deprecated Replaced by the {@link #timeUuidToUnixTimestamp} function + */ + public static final Function unixTimestampOfFct = new NativeScalarFunction("unixtimestampof", LongType.instance, TimeUUIDType.instance) + { + private volatile boolean hasLoggedDeprecationWarning; + + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + if (!hasLoggedDeprecationWarning) + { + hasLoggedDeprecationWarning = true; + logger.warn("The function 'unixtimestampof' is deprecated." + + " Use the function 'toUnixTimestamp' instead."); + } + + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + return ByteBufferUtil.bytes(UUIDGen.unixTimestamp(UUIDGen.getUUID(bb))); + } + }; + + /** + * Function that convert a value of <code>TIMEUUID</code> into a value of type <code>DATE</code>. + */ + public static final Function timeUuidtoDate = new NativeScalarFunction("todate", SimpleDateType.instance, TimeUUIDType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + long timeInMillis = UUIDGen.unixTimestamp(UUIDGen.getUUID(bb)); + return SimpleDateType.instance.fromTimeInMillis(timeInMillis); + } + }; + + /** + * Function that convert a value of type <code>TIMEUUID</code> into a value of type <code>TIMESTAMP</code>. + */ + public static final Function timeUuidToTimestamp = new NativeScalarFunction("totimestamp", TimestampType.instance, TimeUUIDType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + long timeInMillis = UUIDGen.unixTimestamp(UUIDGen.getUUID(bb)); + return TimestampType.instance.fromTimeInMillis(timeInMillis); + } + }; + + /** + * Function that convert a value of type <code>TIMEUUID</code> into an UNIX timestamp. + */ + public static final Function timeUuidToUnixTimestamp = new NativeScalarFunction("tounixtimestamp", LongType.instance, TimeUUIDType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + return ByteBufferUtil.bytes(UUIDGen.unixTimestamp(UUIDGen.getUUID(bb))); + } + }; + + /** + * Function that convert a value of type <code>TIMESTAMP</code> into an UNIX timestamp. + */ + public static final Function timestampToUnixTimestamp = new NativeScalarFunction("tounixtimestamp", LongType.instance, TimestampType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + Date date = TimestampType.instance.compose(bb); + return date == null ? null : ByteBufferUtil.bytes(date.getTime()); + } + }; + + /** + * Function that convert a value of type <code>TIMESTAMP</code> into a <code>DATE</code>. + */ + public static final Function timestampToDate = new NativeScalarFunction("todate", SimpleDateType.instance, TimestampType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + Date date = TimestampType.instance.compose(bb); + return date == null ? null : SimpleDateType.instance.fromTimeInMillis(date.getTime()); + } + }; + + /** + * Function that convert a value of type <code>TIMESTAMP</code> into a <code>DATE</code>. + */ + public static final Function dateToTimestamp = new NativeScalarFunction("totimestamp", TimestampType.instance, SimpleDateType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + long millis = SimpleDateType.instance.toTimeInMillis(bb); + return TimestampType.instance.fromTimeInMillis(millis); + } + }; + + /** + * Function that convert a value of type <code>DATE</code> into an UNIX timestamp. + */ + public static final Function dateToUnixTimestamp = new NativeScalarFunction("tounixtimestamp", LongType.instance, SimpleDateType.instance) + { + public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) + { + ByteBuffer bb = parameters.get(0); + if (bb == null) + return null; + + return ByteBufferUtil.bytes(SimpleDateType.instance.toTimeInMillis(bb)); + } + }; +} + http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java b/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java deleted file mode 100644 index d24572b..0000000 --- a/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java +++ /dev/null @@ -1,88 +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.Date; -import java.util.List; - -import org.apache.cassandra.db.marshal.TimestampType; -import org.apache.cassandra.db.marshal.TimeUUIDType; -import org.apache.cassandra.db.marshal.LongType; -import org.apache.cassandra.utils.ByteBufferUtil; -import org.apache.cassandra.utils.UUIDGen; - -public abstract class TimeuuidFcts -{ - public static final Function nowFct = new NativeScalarFunction("now", TimeUUIDType.instance) - { - public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) - { - return ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()); - } - }; - - public static final Function minTimeuuidFct = new NativeScalarFunction("mintimeuuid", TimeUUIDType.instance, TimestampType.instance) - { - public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) - { - ByteBuffer bb = parameters.get(0); - if (bb == null) - return null; - - return ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.minTimeUUID(TimestampType.instance.compose(bb).getTime()))); - } - }; - - public static final Function maxTimeuuidFct = new NativeScalarFunction("maxtimeuuid", TimeUUIDType.instance, TimestampType.instance) - { - public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) - { - ByteBuffer bb = parameters.get(0); - if (bb == null) - return null; - - return ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.maxTimeUUID(TimestampType.instance.compose(bb).getTime()))); - } - }; - - public static final Function dateOfFct = new NativeScalarFunction("dateof", TimestampType.instance, TimeUUIDType.instance) - { - public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) - { - ByteBuffer bb = parameters.get(0); - if (bb == null) - return null; - - return TimestampType.instance.decompose(new Date(UUIDGen.unixTimestamp(UUIDGen.getUUID(bb)))); - } - }; - - public static final Function unixTimestampOfFct = new NativeScalarFunction("unixtimestampof", LongType.instance, TimeUUIDType.instance) - { - public ByteBuffer execute(int protocolVersion, List<ByteBuffer> parameters) - { - ByteBuffer bb = parameters.get(0); - if (bb == null) - return null; - - return ByteBufferUtil.bytes(UUIDGen.unixTimestamp(UUIDGen.getUUID(bb))); - } - }; -} - http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java index 225b9cc..747709e 100644 --- a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java +++ b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java @@ -50,6 +50,16 @@ public class SimpleDateType extends AbstractType<Integer> return ByteBufferUtil.bytes(SimpleDateSerializer.dateStringToDays(source)); } + public ByteBuffer fromTimeInMillis(long millis) throws MarshalException + { + return ByteBufferUtil.bytes(SimpleDateSerializer.timeInMillisToDay(millis)); + } + + public long toTimeInMillis(ByteBuffer buffer) throws MarshalException + { + return SimpleDateSerializer.dayToTimeInMillis(ByteBufferUtil.toInt(buffer)); + } + @Override public boolean isCompatibleWith(AbstractType<?> previous) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/src/java/org/apache/cassandra/db/marshal/TimestampType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TimestampType.java b/src/java/org/apache/cassandra/db/marshal/TimestampType.java index 38e0296..b01651d 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimestampType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimestampType.java @@ -64,6 +64,11 @@ public class TimestampType extends AbstractType<Date> return ByteBufferUtil.bytes(TimestampSerializer.dateStringToTimestamp(source)); } + public ByteBuffer fromTimeInMillis(long millis) throws MarshalException + { + return ByteBufferUtil.bytes(millis); + } + @Override public Term fromJSONObject(Object parsed) throws MarshalException { http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java b/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java index 0f4bde8..2509597 100644 --- a/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java +++ b/src/java/org/apache/cassandra/repair/SystemDistributedKeyspace.java @@ -105,14 +105,14 @@ public final class SystemDistributedKeyspace { String query = "INSERT INTO %s.%s (parent_id, keyspace_name, columnfamily_names, requested_ranges, started_at)"+ - " VALUES (%s, '%s', { '%s' }, { '%s' }, dateOf(now()))"; + " VALUES (%s, '%s', { '%s' }, { '%s' }, toTimestamp(now()))"; String fmtQry = String.format(query, NAME, PARENT_REPAIR_HISTORY, parent_id.toString(), keyspaceName, Joiner.on("','").join(cfnames), Joiner.on("','").join(ranges)); processSilent(fmtQry); } public static void failParentRepair(UUID parent_id, Throwable t) { - String query = "UPDATE %s.%s SET finished_at = dateOf(now()), exception_message=?, exception_stacktrace=? WHERE parent_id=%s"; + String query = "UPDATE %s.%s SET finished_at = toTimestamp(now()), exception_message=?, exception_stacktrace=? WHERE parent_id=%s"; StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); @@ -123,7 +123,7 @@ public final class SystemDistributedKeyspace public static void successfulParentRepair(UUID parent_id, Collection<Range<Token>> successfulRanges) { - String query = "UPDATE %s.%s SET finished_at = dateOf(now()), successful_ranges = {'%s'} WHERE parent_id=%s"; + String query = "UPDATE %s.%s SET finished_at = toTimestamp(now()), successful_ranges = {'%s'} WHERE parent_id=%s"; String fmtQuery = String.format(query, NAME, PARENT_REPAIR_HISTORY, Joiner.on("','").join(successfulRanges), parent_id.toString()); processSilent(fmtQuery); } @@ -138,7 +138,7 @@ public final class SystemDistributedKeyspace String query = "INSERT INTO %s.%s (keyspace_name, columnfamily_name, id, parent_id, range_begin, range_end, coordinator, participants, status, started_at) " + - "VALUES ( '%s', '%s', %s, %s, '%s', '%s', '%s', { '%s' }, '%s', dateOf(now()))"; + "VALUES ( '%s', '%s', %s, %s, '%s', '%s', '%s', { '%s' }, '%s', toTimestamp(now()))"; for (String cfname : cfnames) { @@ -164,7 +164,7 @@ public final class SystemDistributedKeyspace public static void successfulRepairJob(UUID id, String keyspaceName, String cfname) { - String query = "UPDATE %s.%s SET status = '%s', finished_at = dateOf(now()) WHERE keyspace_name = '%s' AND columnfamily_name = '%s' AND id = %s"; + String query = "UPDATE %s.%s SET status = '%s', finished_at = toTimestamp(now()) WHERE keyspace_name = '%s' AND columnfamily_name = '%s' AND id = %s"; String fmtQuery = String.format(query, NAME, REPAIR_HISTORY, RepairState.SUCCESS.toString(), keyspaceName, @@ -175,7 +175,7 @@ public final class SystemDistributedKeyspace public static void failedRepairJob(UUID id, String keyspaceName, String cfname, Throwable t) { - String query = "UPDATE %s.%s SET status = '%s', finished_at = dateOf(now()), exception_message=?, exception_stacktrace=? WHERE keyspace_name = '%s' AND columnfamily_name = '%s' AND id = %s"; + String query = "UPDATE %s.%s SET status = '%s', finished_at = toTimestamp(now()), exception_message=?, exception_stacktrace=? WHERE keyspace_name = '%s' AND columnfamily_name = '%s' AND id = %s"; StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); t.printStackTrace(pw); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/src/java/org/apache/cassandra/serializers/SimpleDateSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/serializers/SimpleDateSerializer.java b/src/java/org/apache/cassandra/serializers/SimpleDateSerializer.java index 82cedc0..075094c 100644 --- a/src/java/org/apache/cassandra/serializers/SimpleDateSerializer.java +++ b/src/java/org/apache/cassandra/serializers/SimpleDateSerializer.java @@ -86,9 +86,7 @@ public class SimpleDateSerializer implements TypeSerializer<Integer> if (millis > maxSupportedDateMillis) throw new MarshalException(String.format("Input date %s is greater than max supported date %s", source, new LocalDate(maxSupportedDateMillis).toString())); - Integer result = (int)TimeUnit.MILLISECONDS.toDays(millis); - result -= Integer.MIN_VALUE; - return result; + return timeInMillisToDay(millis); } catch (IllegalArgumentException e1) { @@ -96,6 +94,18 @@ public class SimpleDateSerializer implements TypeSerializer<Integer> } } + public static int timeInMillisToDay(long millis) + { + Integer result = (int) TimeUnit.MILLISECONDS.toDays(millis); + result -= Integer.MIN_VALUE; + return result; + } + + public static long dayToTimeInMillis(int days) + { + return TimeUnit.DAYS.toMillis(days - Integer.MIN_VALUE); + } + public void validate(ByteBuffer bytes) throws MarshalException { if (bytes.remaining() != 4) @@ -107,7 +117,7 @@ public class SimpleDateSerializer implements TypeSerializer<Integer> if (value == null) return ""; - return formatter.print(new LocalDate(TimeUnit.DAYS.toMillis(value - Integer.MIN_VALUE), DateTimeZone.UTC)); + return formatter.print(new LocalDate(dayToTimeInMillis(value), DateTimeZone.UTC)); } public Class<Integer> getType() http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/test/unit/org/apache/cassandra/cql3/AggregationTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/AggregationTest.java b/test/unit/org/apache/cassandra/cql3/AggregationTest.java index af68ddc..768fdc3 100644 --- a/test/unit/org/apache/cassandra/cql3/AggregationTest.java +++ b/test/unit/org/apache/cassandra/cql3/AggregationTest.java @@ -111,10 +111,10 @@ public class AggregationTest extends CQLTester "LANGUAGE JAVA " + "AS 'return Double.valueOf(Math.copySign(magnitude, sign));';"); - assertColumnNames(execute("SELECT max(a), max(unixTimestampOf(b)) FROM %s"), "system.max(a)", "system.max(system.unixtimestampof(b))"); - assertRows(execute("SELECT max(a), max(unixTimestampOf(b)) FROM %s"), row(null, null)); - assertColumnNames(execute("SELECT max(a), unixTimestampOf(max(b)) FROM %s"), "system.max(a)", "system.unixtimestampof(system.max(b))"); - assertRows(execute("SELECT max(a), unixTimestampOf(max(b)) FROM %s"), row(null, null)); + assertColumnNames(execute("SELECT max(a), max(toUnixTimestamp(b)) FROM %s"), "system.max(a)", "system.max(system.tounixtimestamp(b))"); + assertRows(execute("SELECT max(a), max(toUnixTimestamp(b)) FROM %s"), row(null, null)); + assertColumnNames(execute("SELECT max(a), toUnixTimestamp(max(b)) FROM %s"), "system.max(a)", "system.tounixtimestamp(system.max(b))"); + assertRows(execute("SELECT max(a), toUnixTimestamp(max(b)) FROM %s"), row(null, null)); assertColumnNames(execute("SELECT max(" + copySign + "(c, d)) FROM %s"), "system.max(" + copySign + "(c, d))"); assertRows(execute("SELECT max(" + copySign + "(c, d)) FROM %s"), row((Object) null)); @@ -128,8 +128,8 @@ public class AggregationTest extends CQLTester Date date = format.parse("2011-02-03 04:10:00"); date = DateUtils.truncate(date, Calendar.MILLISECOND); - assertRows(execute("SELECT max(a), max(unixTimestampOf(b)) FROM %s"), row(3, date.getTime())); - assertRows(execute("SELECT max(a), unixTimestampOf(max(b)) FROM %s"), row(3, date.getTime())); + assertRows(execute("SELECT max(a), max(toUnixTimestamp(b)) FROM %s"), row(3, date.getTime())); + assertRows(execute("SELECT max(a), toUnixTimestamp(max(b)) FROM %s"), row(3, date.getTime())); assertRows(execute("SELECT " + copySign + "(max(c), min(c)) FROM %s"), row(-1.4)); assertRows(execute("SELECT " + copySign + "(c, d) FROM %s"), row(1.2), row(-1.3), row(1.4)); http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/test/unit/org/apache/cassandra/cql3/TypeTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/TypeTest.java b/test/unit/org/apache/cassandra/cql3/TypeTest.java index 7e12f9f..0605554 100644 --- a/test/unit/org/apache/cassandra/cql3/TypeTest.java +++ b/test/unit/org/apache/cassandra/cql3/TypeTest.java @@ -36,8 +36,13 @@ public class TypeTest extends CQLTester public void testDateCompatibility() throws Throwable { createTable("CREATE TABLE %s (a int, b timestamp, c bigint, d varint, PRIMARY KEY (a, b, c, d))"); - execute("INSERT INTO %s (a, b, c, d) VALUES (0, unixTimestampOf(now()), dateOf(now()), dateOf(now()))"); - UntypedResultSet results = execute("SELECT * FROM %s WHERE a=0 AND b < unixTimestampOf(now())"); + + execute("INSERT INTO %s (a, b, c, d) VALUES (0, toUnixTimestamp(now()), toTimestamp(now()), toTimestamp(now()))"); + UntypedResultSet results = execute("SELECT * FROM %s WHERE a=0 AND b < toUnixTimestamp(now())"); + assertEquals(1, results.size()); + + execute("INSERT INTO %s (a, b, c, d) VALUES (1, unixTimestampOf(now()), dateOf(now()), dateOf(now()))"); + results = execute("SELECT * FROM %s WHERE a=1 AND b < toUnixTimestamp(now())"); assertEquals(1, results.size()); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/test/unit/org/apache/cassandra/cql3/UFTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/UFTest.java b/test/unit/org/apache/cassandra/cql3/UFTest.java index db94a4c..5a20c18 100644 --- a/test/unit/org/apache/cassandra/cql3/UFTest.java +++ b/test/unit/org/apache/cassandra/cql3/UFTest.java @@ -388,7 +388,7 @@ public class UFTest extends CQLTester execute("DROP FUNCTION IF EXISTS " + fSin); // can't drop native functions - assertInvalidMessage("system keyspace is not user-modifiable", "DROP FUNCTION dateof"); + assertInvalidMessage("system keyspace is not user-modifiable", "DROP FUNCTION totimestamp"); assertInvalidMessage("system keyspace is not user-modifiable", "DROP FUNCTION uuid"); // sin() no longer exists @@ -732,10 +732,11 @@ public class UFTest extends CQLTester @Test public void testFunctionInSystemKS() throws Throwable { - execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".dateof(val timeuuid) " + + execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".totimestamp(val timeuuid) " + "RETURNS NULL ON NULL INPUT " + "RETURNS timestamp " + "LANGUAGE JAVA\n" + + "AS 'return null;';"); assertInvalidMessage("system keyspace is not user-modifiable", @@ -745,10 +746,11 @@ public class UFTest extends CQLTester "LANGUAGE JAVA\n" + "AS 'return null;';"); assertInvalidMessage("system keyspace is not user-modifiable", - "CREATE OR REPLACE FUNCTION system.dateof(val timeuuid) " + + "CREATE OR REPLACE FUNCTION system.totimestamp(val timeuuid) " + "RETURNS NULL ON NULL INPUT " + "RETURNS timestamp " + "LANGUAGE JAVA\n" + + "AS 'return null;';"); assertInvalidMessage("system keyspace is not user-modifiable", "DROP FUNCTION system.now"); @@ -761,7 +763,7 @@ public class UFTest extends CQLTester "LANGUAGE JAVA\n" + "AS 'return null;';"); assertInvalidMessage("system keyspace is not user-modifiable", - "CREATE OR REPLACE FUNCTION dateof(val timeuuid) " + + "CREATE OR REPLACE FUNCTION totimestamp(val timeuuid) " + "RETURNS NULL ON NULL INPUT " + "RETURNS timestamp " + "LANGUAGE JAVA\n" + http://git-wip-us.apache.org/repos/asf/cassandra/blob/c08aaabd/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java b/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java new file mode 100644 index 0000000..f6fca20 --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java @@ -0,0 +1,206 @@ +/* + * 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.junit.Test; + +import org.apache.cassandra.db.marshal.LongType; +import org.apache.cassandra.db.marshal.SimpleDateType; +import org.apache.cassandra.db.marshal.TimeUUIDType; +import org.apache.cassandra.db.marshal.TimestampType; +import org.apache.cassandra.transport.Server; +import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.utils.UUIDGen; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.DateTimeFormat; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class TimeFctsTest +{ + @Test + public void testMinTimeUuid() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21 11:03:02"); + + long timeInMillis = dateTime.getMillis(); + ByteBuffer input = TimestampType.instance.fromString("2015-05-21 11:03:02+00"); + ByteBuffer output = executeFunction(TimeFcts.minTimeuuidFct, input); + assertEquals(UUIDGen.minTimeUUID(timeInMillis), TimeUUIDType.instance.compose(output)); + } + + @Test + public void testMaxTimeUuid() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21 11:03:02"); + + long timeInMillis = dateTime.getMillis(); + ByteBuffer input = TimestampType.instance.fromString("2015-05-21 11:03:02+00"); + ByteBuffer output = executeFunction(TimeFcts.maxTimeuuidFct, input); + assertEquals(UUIDGen.maxTimeUUID(timeInMillis), TimeUUIDType.instance.compose(output)); + } + + @Test + public void testDateOf() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21 11:03:02"); + + long timeInMillis = dateTime.getMillis(); + ByteBuffer input = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes(timeInMillis, 0)); + ByteBuffer output = executeFunction(TimeFcts.dateOfFct, input); + assertEquals(dateTime.toDate(), TimestampType.instance.compose(output)); + } + + @Test + public void testTimeUuidToTimestamp() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21 11:03:02"); + + long timeInMillis = dateTime.getMillis(); + ByteBuffer input = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes(timeInMillis, 0)); + ByteBuffer output = executeFunction(TimeFcts.timeUuidToTimestamp, input); + assertEquals(dateTime.toDate(), TimestampType.instance.compose(output)); + } + + @Test + public void testUnixTimestampOfFct() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21 11:03:02"); + + long timeInMillis = dateTime.getMillis(); + ByteBuffer input = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes(timeInMillis, 0)); + ByteBuffer output = executeFunction(TimeFcts.unixTimestampOfFct, input); + assertEquals(timeInMillis, LongType.instance.compose(output).longValue()); + } + + @Test + public void testTimeUuidToUnixTimestamp() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21 11:03:02"); + + long timeInMillis = dateTime.getMillis(); + ByteBuffer input = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes(timeInMillis, 0)); + ByteBuffer output = executeFunction(TimeFcts.timeUuidToUnixTimestamp, input); + assertEquals(timeInMillis, LongType.instance.compose(output).longValue()); + } + + @Test + public void testTimeUuidToDate() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21 11:03:02"); + + long timeInMillis = dateTime.getMillis(); + ByteBuffer input = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes(timeInMillis, 0)); + ByteBuffer output = executeFunction(TimeFcts.timeUuidtoDate, input); + + long expectedTime = DateTimeFormat.forPattern("yyyy-MM-dd") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21") + .getMillis(); + + assertEquals(expectedTime, SimpleDateType.instance.toTimeInMillis(output)); + } + + @Test + public void testDateToTimestamp() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21"); + + ByteBuffer input = SimpleDateType.instance.fromString("2015-05-21"); + ByteBuffer output = executeFunction(TimeFcts.dateToTimestamp, input); + assertEquals(dateTime.toDate(), TimestampType.instance.compose(output)); + } + + @Test + public void testDateToUnixTimestamp() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21"); + + ByteBuffer input = SimpleDateType.instance.fromString("2015-05-21"); + ByteBuffer output = executeFunction(TimeFcts.dateToUnixTimestamp, input); + assertEquals(dateTime.getMillis(), LongType.instance.compose(output).longValue()); + } + + @Test + public void testTimestampToDate() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21"); + + ByteBuffer input = TimestampType.instance.fromString("2015-05-21 11:03:02+00"); + ByteBuffer output = executeFunction(TimeFcts.timestampToDate, input); + assertEquals(dateTime.getMillis(), SimpleDateType.instance.toTimeInMillis(output)); + } + + @Test + public void testTimestampToDateWithEmptyInput() + { + ByteBuffer output = executeFunction(TimeFcts.timestampToDate, ByteBufferUtil.EMPTY_BYTE_BUFFER); + assertNull(output); + } + + @Test + public void testTimestampToUnixTimestamp() + { + DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss") + .withZone(DateTimeZone.UTC) + .parseDateTime("2015-05-21 11:03:02"); + + ByteBuffer input = TimestampType.instance.decompose(dateTime.toDate()); + ByteBuffer output = executeFunction(TimeFcts.timestampToUnixTimestamp, input); + assertEquals(dateTime.getMillis(), LongType.instance.compose(output).longValue()); + } + + @Test + public void testTimestampToUnixTimestampWithEmptyInput() + { + ByteBuffer output = executeFunction(TimeFcts.timestampToUnixTimestamp, ByteBufferUtil.EMPTY_BYTE_BUFFER); + assertNull(output); + } + + private static ByteBuffer executeFunction(Function function, ByteBuffer input) + { + List<ByteBuffer> params = Arrays.asList(input); + return ((ScalarFunction) function).execute(Server.CURRENT_VERSION, params); + } +}