You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jm...@apache.org on 2015/03/09 18:28:37 UTC
[2/3] cassandra git commit: Add date and time types
Add date and time types
Patch by jmckenzie; reviewed by thobbs and cyeksigian for CASSANDRA-7523
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/107545b3
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/107545b3
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/107545b3
Branch: refs/heads/trunk
Commit: 107545b3929c32c61e7d00c6ca448c10046db792
Parents: 2e3a287
Author: Joshua McKenzie <jm...@apache.org>
Authored: Mon Mar 9 12:20:28 2015 -0500
Committer: Joshua McKenzie <jm...@apache.org>
Committed: Mon Mar 9 12:20:28 2015 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 5 +
bin/cqlsh | 47 +++-
doc/cql3/CQL.textile | 41 +++-
lib/joda-time-2.4.jar | Bin 0 -> 586201 bytes
lib/licenses/joda-time-2.4.txt | 201 +++++++++++++++
pylib/cqlshlib/cql3handling.py | 6 +-
pylib/cqlshlib/displaying.py | 2 +
pylib/cqlshlib/formatting.py | 65 +++--
pylib/cqlshlib/helptopics.py | 23 ++
pylib/cqlshlib/test/test_cqlsh_output.py | 17 +-
pylib/cqlshlib/test/test_keyspace_init.cql | 41 ++--
.../org/apache/cassandra/cql3/CQL3Type.java | 34 +--
.../org/apache/cassandra/cql3/Constants.java | 5 +-
src/java/org/apache/cassandra/cql3/Cql.g | 4 +
.../cassandra/db/marshal/IntegerType.java | 5 +
.../cassandra/db/marshal/SimpleDateType.java | 72 ++++++
.../apache/cassandra/db/marshal/TimeType.java | 72 ++++++
.../serializers/SimpleDateSerializer.java | 117 +++++++++
.../cassandra/serializers/TimeSerializer.java | 198 +++++++++++++++
.../cassandra/serializers/TypeSerializer.java | 1 +
.../apache/cassandra/transport/DataType.java | 2 +
.../db/marshal/SimpleDateTypeTest.java | 153 ++++++++++++
.../cassandra/db/marshal/TimeTypeTest.java | 61 +++++
.../serializers/SimpleDateSerializerTest.java | 155 ++++++++++++
.../serializers/TimeSerializerTest.java | 242 +++++++++++++++++++
26 files changed, 1483 insertions(+), 87 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 5acc288..31828fc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.1.4
+ * Add SimpleDate (cql date) and Time (cql time) types (CASSANDRA-7523)
* Use long for key count in cfstats (CASSANDRA-8913)
* Make SSTableRewriter.abort() more robust to failure (CASSANDRA-8832)
* Remove cold_reads_to_omit from STCS (CASSANDRA-8860)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index 06013b8..4ead1ea 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -21,6 +21,11 @@ Upgrading
removed - it is almost always better to use date tiered compaction for
workloads that have cold data.
+New features
+------------
+ - New `SimpleDateType` (cql date). 4-byte unsigned integer w/out timestamp
+ - New `TimeType` (cql time). 8-byte long nano-second resolution timestamp
+
2.1.3
=====
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/bin/cqlsh
----------------------------------------------------------------------
diff --git a/bin/cqlsh b/bin/cqlsh
index 31dc080..3ec9457 100755
--- a/bin/cqlsh
+++ b/bin/cqlsh
@@ -122,6 +122,10 @@ from cqlshlib.displaying import (RED, BLUE, CYAN, ANSI_RESET, COLUMN_NAME_COLORS
FormattedValue, colorme)
from cqlshlib.formatting import format_by_type, formatter_for, format_value_utype
from cqlshlib.util import trim_if_present, get_file_encoding_bomsize
+from cqlshlib.formatting import DateTimeFormat
+from cqlshlib.formatting import DEFAULT_TIMESTAMP_FORMAT
+from cqlshlib.formatting import DEFAULT_DATE_FORMAT
+from cqlshlib.formatting import DEFAULT_NANOTIME_FORMAT
from cqlshlib.tracing import print_trace_session, print_trace
DEFAULT_HOST = '127.0.0.1'
@@ -129,7 +133,6 @@ DEFAULT_PORT = 9042
DEFAULT_CQLVER = '3.2.0'
DEFAULT_PROTOCOL_VERSION = 3
-DEFAULT_TIME_FORMAT = '%Y-%m-%d %H:%M:%S%z'
DEFAULT_FLOAT_PRECISION = 5
DEFAULT_MAX_TRACE_WAIT = 10
@@ -431,7 +434,7 @@ def full_cql_version(ver):
vertuple = tuple(map(int, ver_parts[0].split('.')) + [ver_parts[1]])
return ver, vertuple
-def format_value(val, output_encoding, addcolor=False, time_format=None,
+def format_value(val, output_encoding, addcolor=False, date_time_format=None,
float_precision=None, colormap=None, nullval=None):
if isinstance(val, DecodeError):
if addcolor:
@@ -439,7 +442,7 @@ def format_value(val, output_encoding, addcolor=False, time_format=None,
else:
return FormattedValue(repr(val.thebytes))
return format_by_type(type(val), val, output_encoding, colormap=colormap,
- addcolor=addcolor, nullval=nullval, time_format=time_format,
+ addcolor=addcolor, nullval=nullval, date_time_format=date_time_format,
float_precision=float_precision)
def show_warning_without_quoting_line(message, category, filename, lineno, file=None, line=None):
@@ -529,7 +532,9 @@ class Shell(cmd.Cmd):
completekey=DEFAULT_COMPLETEKEY, use_conn=None,
cqlver=DEFAULT_CQLVER, keyspace=None,
tracing_enabled=False, expand_enabled=False,
- display_time_format=DEFAULT_TIME_FORMAT,
+ display_nanotime_format=DEFAULT_NANOTIME_FORMAT,
+ display_timestamp_format=DEFAULT_TIMESTAMP_FORMAT,
+ display_date_format=DEFAULT_DATE_FORMAT,
display_float_precision=DEFAULT_FLOAT_PRECISION,
max_trace_wait=DEFAULT_MAX_TRACE_WAIT,
ssl=False,
@@ -564,7 +569,11 @@ class Shell(cmd.Cmd):
self.session = self.conn.connect()
self.color = color
- self.display_time_format = display_time_format
+
+ self.display_nanotime_format = display_nanotime_format
+ self.display_timestamp_format = display_timestamp_format
+ self.display_date_format = display_date_format
+
self.display_float_precision = display_float_precision
# Workaround for CASSANDRA-8521 until PYTHON-205 is resolved.
@@ -588,6 +597,10 @@ class Shell(cmd.Cmd):
self.current_keyspace = keyspace
+ self.display_timestamp_format = display_timestamp_format
+ self.display_nanotime_format = display_nanotime_format
+ self.display_date_format = display_date_format
+
self.max_trace_wait = max_trace_wait
self.session.max_trace_wait = max_trace_wait
if encoding is None:
@@ -634,8 +647,10 @@ class Shell(cmd.Cmd):
if isinstance(val, DecodeError):
self.decoding_errors.append(val)
try:
+ dtformats = DateTimeFormat(timestamp_format=self.display_timestamp_format,
+ date_format=self.display_date_format, nanotime_format=self.display_nanotime_format)
return format_value(val, self.output_codec.name,
- addcolor=self.color, time_format=self.display_time_format,
+ addcolor=self.color, date_time_format=dtformats,
float_precision=self.display_float_precision, **kwargs)
except Exception, e:
err = FormatError(val, e)
@@ -1458,7 +1473,7 @@ class Shell(cmd.Cmd):
cqltype = table_meta.columns[name].typestring
if value != nullval:
- if cqltype in ('ascii', 'text', 'timestamp', 'inet'):
+ if cqltype in ('ascii', 'text', 'timestamp', 'date', 'time', 'inet'):
rowmap[name] = protect_value(value)
else:
rowmap[name] = value
@@ -1520,7 +1535,7 @@ class Shell(cmd.Cmd):
return 0
wmeter = meter.Meter()
try:
-
+ dtformats = DateTimeFormat(self.display_timestamp_format, self.display_date_format, self.display_nanotime_format)
dump = self.prep_export_dump(ks, cf, columns)
writer = csv.writer(csvdest, **dialect_options)
if header:
@@ -1528,7 +1543,7 @@ class Shell(cmd.Cmd):
for row in dump:
fmt = lambda v: \
format_value(v, output_encoding=encoding, nullval=nullval,
- time_format=self.display_time_format,
+ date_time_format=dtformats,
float_precision=self.display_float_precision).strval
writer.writerow(map(fmt, row.values()))
wmeter.mark_written()
@@ -1610,7 +1625,9 @@ class Shell(cmd.Cmd):
subshell = Shell(self.hostname, self.port,
color=self.color, encoding=self.encoding, stdin=f,
tty=False, use_conn=self.conn, cqlver=self.cql_version,
- display_time_format=self.display_time_format,
+ display_timestamp_format=self.display_timestamp_format,
+ display_date_format=self.display_date_format,
+ display_nanotime_format=self.display_nanotime_format,
display_float_precision=self.display_float_precision,
max_trace_wait=self.max_trace_wait)
subshell.cmdloop()
@@ -1918,7 +1935,11 @@ def read_options(cmdlineargs, environment):
DEFAULT_COMPLETEKEY)
optvalues.color = option_with_default(configs.getboolean, 'ui', 'color')
optvalues.time_format = raw_option_with_default(configs, 'ui', 'time_format',
- DEFAULT_TIME_FORMAT)
+ DEFAULT_TIMESTAMP_FORMAT)
+ optvalues.nanotime_format = raw_option_with_default(configs, 'ui', 'nanotime_format',
+ DEFAULT_NANOTIME_FORMAT)
+ optvalues.date_format = raw_option_with_default(configs, 'ui', 'date_format',
+ DEFAULT_DATE_FORMAT)
optvalues.float_precision = option_with_default(configs.getint, 'ui', 'float_precision',
DEFAULT_FLOAT_PRECISION)
optvalues.max_trace_wait = option_with_default(configs.getfloat, 'tracing', 'max_trace_wait',
@@ -2035,7 +2056,9 @@ def main(options, hostname, port):
completekey=options.completekey,
cqlver=options.cqlversion,
keyspace=options.keyspace,
- display_time_format=options.time_format,
+ display_timestamp_format=options.time_format,
+ display_nanotime_format=options.nanotime_format,
+ display_date_format=options.date_format,
display_float_precision=options.float_precision,
max_trace_wait=options.max_trace_wait,
ssl=options.ssl,
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/doc/cql3/CQL.textile
----------------------------------------------------------------------
diff --git a/doc/cql3/CQL.textile b/doc/cql3/CQL.textile
index 0221bc1..0c09b32 100644
--- a/doc/cql3/CQL.textile
+++ b/doc/cql3/CQL.textile
@@ -944,12 +944,14 @@ bc(syntax)..
| blob
| boolean
| counter
+ | date
| decimal
| double
| float
| inet
| int
| text
+ | time
| timestamp
| timeuuid
| uuid
@@ -970,13 +972,15 @@ p. The following table gives additional informations on the native data types, a
|@blob@ | blobs |Arbitrary bytes (no validation)|
|@boolean@ | booleans |true or false|
|@counter@ | integers |Counter column (64-bit signed value). See "Counters":#counters for details|
+|@date@ | integers, strings |A date (with no corresponding time value). See "Working with dates":#usingdates below for more information.|
|@decimal@ | integers, floats |Variable-precision decimal|
|@double@ | integers |64-bit IEEE-754 floating point|
|@float@ | integers, floats |32-bit IEEE-754 floating point|
|@inet@ | strings |An IP address. It can be either 4 bytes long (IPv4) or 16 bytes long (IPv6). There is no @inet@ constant, IP address should be inputed as strings|
|@int@ | integers |32-bit signed int|
|@text@ | strings |UTF8 encoded string|
-|@timestamp@| integers, strings |A timestamp. Strings constant are allow to input timestamps as dates, see "Working with dates":#usingdates below for more information.|
+|@time@ | integers, strings |A time with nanosecond precision. See "Working with time":#usingtime below for more information.|
+|@timestamp@| integers, strings |A timestamp. Strings constant are allow to input timestamps as dates, see "Working with timestamps":#usingtimestamps below for more information.|
|@timeuuid@ | uuids |Type 1 UUID. This is generally used as a "conflict-free" timestamp. Also see the "functions on Timeuuid":#timeuuidFun|
|@uuid@ | uuids |Type 1 or type 4 UUID|
|@varchar@ | strings |UTF8 encoded string|
@@ -984,7 +988,7 @@ p. The following table gives additional informations on the native data types, a
For more information on how to use the collection types, see the "Working with collections":#collections section below.
-h3(#usingdates). Working with dates
+h3(#usingtimestamps). Working with timestamps
Values of the @timestamp@ type are encoded as 64-bit signed integers representing a number of milliseconds since the standard base time known as "the epoch": January 1 1970 at 00:00:00 GMT.
@@ -1018,6 +1022,31 @@ The time of day may also be omitted, if the date is the only piece that matters:
In that case, the time of day will default to 00:00:00, in the specified or default time zone.
+h3(#usingdates). Working with dates
+
+Values of the @date@ type are encoded as 32-bit unsigned integers representing a number of days with "the epoch" at the center of the range (2^31). Epoch is January 1st, 1970
+
+A date can be input in CQL as an unsigned integer as defined above.
+
+They can also be input as string literals in the following format:
+
+* @2014-01-01@
+
+
+h3(#usingtime). Working with time
+
+Values of the @time@ type are encoded as 64-bit signed integers representing the number of nanoseconds since midnight.
+
+A time can be input in CQL as simple long integers, giving the number of nanoseconds since midnight.
+
+They can also be input as string literals in any of the following formats:
+
+* @08:12:54@
+* @08:12:54.123@
+* @08:12:54.123456@
+* @08:12:54.123456789@
+
+
h3(#counters). Counters
The @counter@ type is used to define _counter columns_. A counter column is a column whose value is a 64-bit signed integer and on which 2 operations are supported: incrementation and decrementation (see "@UPDATE@":#updateStmt for syntax). Note the value of a counter cannot be set. A counter doesn't exist until first incremented/decremented, and the first incrementation/decrementation is made as if the previous value was 0. Deletion of counter columns is supported but have some limitations (see the "Cassandra Wiki":http://wiki.apache.org/cassandra/Counters for more information).
@@ -1182,7 +1211,7 @@ will never return any result by design, since the value returned by @now()@ is g
h4. @minTimeuuid@ and @maxTimeuuid@
-The @minTimeuuid@ (resp. @maxTimeuuid@) function takes a @timestamp@ value @t@ (which can be "either a timestamp or a date string":#usingdates) and return a _fake_ @timeuuid@ corresponding to the _smallest_ (resp. _biggest_) possible @timeuuid@ having for timestamp @t@. So for instance:
+The @minTimeuuid@ (resp. @maxTimeuuid@) function takes a @timestamp@ value @t@ (which can be "either a timestamp or a date string":#usingtimestamps) and return a _fake_ @timeuuid@ corresponding to the _smallest_ (resp. _biggest_) possible @timeuuid@ having for timestamp @t@. So for instance:
bc(sample).
SELECT * FROM myTable WHERE t > maxTimeuuid('2013-01-01 00:05+0000') AND t < minTimeuuid('2013-02-02 10:00+0000')
@@ -1290,6 +1319,8 @@ CQL distinguishes between _reserved_ and _non-reserved_ keywords. Reserved keywo
| @WITH@ | yes |
| @WRITETIME@ | no |
| @DISTINCT@ | no |
+| @DATE@ | no |
+| @TIME@ | no |
h2(#appendixB). Appendix B: CQL Reserved Types
@@ -1338,7 +1369,7 @@ h3. 3.1.4
h3. 3.1.3
-* Millisecond precision formats have been added to the timestamp parser (see "working with dates":#usingdates).
+* Millisecond precision formats have been added to the timestamp parser (see "working with dates":#usingtimestamps).
h3. 3.1.2
@@ -1378,7 +1409,7 @@ h3. 3.0.2
h3. 3.0.1
-* "Date strings":#usingdates (and timestamps) are no longer accepted as valid @timeuuid@ values. Doing so was a bug in the sense that date string are not valid @timeuuid@, and it was thus resulting in "confusing behaviors":https://issues.apache.org/jira/browse/CASSANDRA-4936. However, the following new methods have been added to help working with @timeuuid@: @now@, @minTimeuuid@, @maxTimeuuid@ , @dateOf@ and @unixTimestampOf@. See the "section dedicated to these methods":#usingtimeuuid for more detail.
+* "Date strings":#usingtimestamps (and timestamps) are no longer accepted as valid @timeuuid@ values. Doing so was a bug in the sense that date string are not valid @timeuuid@, and it was thus resulting in "confusing behaviors":https://issues.apache.org/jira/browse/CASSANDRA-4936. However, the following new methods have been added to help working with @timeuuid@: @now@, @minTimeuuid@, @maxTimeuuid@ , @dateOf@ and @unixTimestampOf@. See the "section dedicated to these methods":#usingtimeuuid for more detail.
* "Float constants"#constants now support the exponent notation. In other words, @4.2E10@ is now a valid floating point value.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/lib/joda-time-2.4.jar
----------------------------------------------------------------------
diff --git a/lib/joda-time-2.4.jar b/lib/joda-time-2.4.jar
new file mode 100644
index 0000000..ace67d7
Binary files /dev/null and b/lib/joda-time-2.4.jar differ
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/lib/licenses/joda-time-2.4.txt
----------------------------------------------------------------------
diff --git a/lib/licenses/joda-time-2.4.txt b/lib/licenses/joda-time-2.4.txt
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/lib/licenses/joda-time-2.4.txt
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/pylib/cqlshlib/cql3handling.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py
index 88f042e..0da6a7c 100644
--- a/pylib/cqlshlib/cql3handling.py
+++ b/pylib/cqlshlib/cql3handling.py
@@ -19,8 +19,8 @@ from cassandra.metadata import maybe_escape_name
from cassandra.metadata import escape_name
-simple_cql_types = set(('ascii', 'bigint', 'blob', 'boolean', 'counter', 'decimal', 'double', 'float', 'inet', 'int',
- 'text', 'timestamp', 'timeuuid', 'uuid', 'varchar', 'varint'))
+simple_cql_types = set(('ascii', 'bigint', 'blob', 'boolean', 'counter', 'date', 'decimal', 'double', 'float', 'inet', 'int',
+ 'text', 'time', 'timestamp', 'timeuuid', 'uuid', 'varchar', 'varint'))
simple_cql_types.difference_update(('set', 'map', 'list'))
from . import helptopics
@@ -42,7 +42,7 @@ class Cql3ParsingRuleSet(CqlParsingRuleSet):
'limit', 'using', 'use', 'count', 'set',
'begin', 'apply', 'batch', 'truncate', 'delete', 'in', 'create',
'keyspace', 'schema', 'columnfamily', 'table', 'index', 'on', 'drop',
- 'primary', 'into', 'values', 'timestamp', 'ttl', 'alter', 'add', 'type',
+ 'primary', 'into', 'values', 'date', 'time', 'timestamp', 'ttl', 'alter', 'add', 'type',
'compact', 'storage', 'order', 'by', 'asc', 'desc', 'clustering',
'token', 'writetime', 'map', 'list', 'to', 'custom', 'if', 'not'
))
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/pylib/cqlshlib/displaying.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/displaying.py b/pylib/cqlshlib/displaying.py
index 13e3cf4..0cac309 100644
--- a/pylib/cqlshlib/displaying.py
+++ b/pylib/cqlshlib/displaying.py
@@ -95,6 +95,8 @@ DEFAULT_VALUE_COLORS = dict(
error=RED,
blob=DARK_MAGENTA,
timestamp=GREEN,
+ date=GREEN,
+ time=GREEN,
int=GREEN,
float=GREEN,
decimal=GREEN,
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/pylib/cqlshlib/formatting.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/formatting.py b/pylib/cqlshlib/formatting.py
index f03540d..ac12fe6 100644
--- a/pylib/cqlshlib/formatting.py
+++ b/pylib/cqlshlib/formatting.py
@@ -14,13 +14,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import re
-import time
import calendar
import math
+import re
+import time
+import sys
from collections import defaultdict
from . import wcwidth
from .displaying import colorme, FormattedValue, DEFAULT_VALUE_COLORS
+from datetime import datetime, timedelta
from cassandra.cqltypes import EMPTY
unicode_controlchars_re = re.compile(r'[\x00-\x31\x7f-\xa0]')
@@ -45,13 +47,12 @@ def _make_turn_bits_red_f(color1, color2):
return _turn_bits_red
default_null_placeholder = 'null'
-default_time_format = ''
default_float_precision = 3
default_colormap = DEFAULT_VALUE_COLORS
empty_colormap = defaultdict(lambda: '')
def format_by_type(cqltype, val, encoding, colormap=None, addcolor=False,
- nullval=None, time_format=None, float_precision=None):
+ nullval=None, date_time_format=None, float_precision=None):
if nullval is None:
nullval = default_null_placeholder
if val is None:
@@ -60,12 +61,12 @@ def format_by_type(cqltype, val, encoding, colormap=None, addcolor=False,
colormap = empty_colormap
elif colormap is None:
colormap = default_colormap
- if time_format is None:
- time_format = default_time_format
+ if date_time_format is None:
+ date_time_format = DateTimeFormat()
if float_precision is None:
float_precision = default_float_precision
return format_value(cqltype, val, encoding=encoding, colormap=colormap,
- time_format=time_format, float_precision=float_precision,
+ date_time_format=date_time_format, float_precision=float_precision,
nullval=nullval)
def color_text(bval, colormap, displaywidth=None):
@@ -86,6 +87,16 @@ def color_text(bval, colormap, displaywidth=None):
displaywidth -= bval.count(r'\\')
return FormattedValue(bval, coloredval, displaywidth)
+DEFAULT_NANOTIME_FORMAT = '%H:%M:%S.%N'
+DEFAULT_DATE_FORMAT = '%Y-%m-%d'
+DEFAULT_TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S%z'
+
+class DateTimeFormat():
+ def __init__(self, timestamp_format=DEFAULT_TIMESTAMP_FORMAT, date_format=DEFAULT_DATE_FORMAT, nanotime_format=DEFAULT_NANOTIME_FORMAT):
+ self.timestamp_format=timestamp_format
+ self.date_format=date_format
+ self.nanotime_format=nanotime_format
+
def format_value_default(val, colormap, **_):
val = str(val)
escapedval = val.replace('\\', '\\\\')
@@ -157,15 +168,13 @@ def format_integer_type(val, colormap, **_):
formatter_for('long')(format_integer_type)
formatter_for('int')(format_integer_type)
-@formatter_for('date')
-def format_value_timestamp(val, colormap, time_format, quote=False, **_):
- bval = strftime(time_format, calendar.timegm(val.utctimetuple()))
+@formatter_for('datetime')
+def format_value_timestamp(val, colormap, date_time_format, quote=False, **_):
+ bval = strftime(date_time_format.timestamp_format, calendar.timegm(val.utctimetuple()))
if quote:
bval = "'%s'" % bval
return colorme(bval, colormap, 'timestamp')
-formatter_for('datetime')(format_value_timestamp)
-
def strftime(time_format, seconds):
local = time.localtime(seconds)
formatted = time.strftime(time_format, local)
@@ -183,6 +192,14 @@ def strftime(time_format, seconds):
hours, minutes = divmod(abs(offset) / 60, 60)
return formatted[:-5] + sign + '{0:0=2}{1:0=2}'.format(hours, minutes)
+@formatter_for('date')
+def format_value_uuid(val, colormap, **_):
+ return format_python_formatted_type(val, colormap, 'date')
+
+@formatter_for('Time')
+def format_value_time(val, colormap, **_):
+ return format_python_formatted_type(val, colormap, 'time')
+
@formatter_for('str')
def format_value_text(val, encoding, colormap, quote=False, **_):
escapedval = val.replace(u'\\', u'\\\\')
@@ -199,9 +216,9 @@ def format_value_text(val, encoding, colormap, quote=False, **_):
formatter_for('unicode')(format_value_text)
def format_simple_collection(val, lbracket, rbracket, encoding,
- colormap, time_format, float_precision, nullval):
+ colormap, date_time_format, float_precision, nullval):
subs = [format_value(type(sval), sval, encoding=encoding, colormap=colormap,
- time_format=time_format, float_precision=float_precision,
+ date_time_format=date_time_format, float_precision=float_precision,
nullval=nullval, quote=True)
for sval in val]
bval = lbracket + ', '.join(sval.strval for sval in subs) + rbracket
@@ -212,28 +229,28 @@ def format_simple_collection(val, lbracket, rbracket, encoding,
return FormattedValue(bval, coloredval, displaywidth)
@formatter_for('list')
-def format_value_list(val, encoding, colormap, time_format, float_precision, nullval, **_):
+def format_value_list(val, encoding, colormap, date_time_format, float_precision, nullval, **_):
return format_simple_collection(val, '[', ']', encoding, colormap,
- time_format, float_precision, nullval)
+ date_time_format, float_precision, nullval)
@formatter_for('tuple')
-def format_value_tuple(val, encoding, colormap, time_format, float_precision, nullval, **_):
+def format_value_tuple(val, encoding, colormap, date_time_format, float_precision, nullval, **_):
return format_simple_collection(val, '(', ')', encoding, colormap,
- time_format, float_precision, nullval)
+ date_time_format, float_precision, nullval)
@formatter_for('set')
-def format_value_set(val, encoding, colormap, time_format, float_precision, nullval, **_):
+def format_value_set(val, encoding, colormap, date_time_format, float_precision, nullval, **_):
return format_simple_collection(sorted(val), '{', '}', encoding, colormap,
- time_format, float_precision, nullval)
+ date_time_format, float_precision, nullval)
formatter_for('frozenset')(format_value_set)
formatter_for('sortedset')(format_value_set)
@formatter_for('dict')
-def format_value_map(val, encoding, colormap, time_format, float_precision, nullval, **_):
+def format_value_map(val, encoding, colormap, date_time_format, float_precision, nullval, **_):
def subformat(v):
return format_value(type(v), v, encoding=encoding, colormap=colormap,
- time_format=time_format, float_precision=float_precision,
+ date_time_format=date_time_format, float_precision=float_precision,
nullval=nullval, quote=True)
subs = [(subformat(k), subformat(v)) for (k, v) in sorted(val.items())]
@@ -249,12 +266,12 @@ formatter_for('OrderedDict')(format_value_map)
formatter_for('OrderedMap')(format_value_map)
-def format_value_utype(val, encoding, colormap, time_format, float_precision, nullval, **_):
+def format_value_utype(val, encoding, colormap, date_time_format, float_precision, nullval, **_):
def format_field_value(v):
if v is None:
return colorme(nullval, colormap, 'error')
return format_value(type(v), v, encoding=encoding, colormap=colormap,
- time_format=time_format, float_precision=float_precision,
+ date_time_format=date_time_format, float_precision=float_precision,
nullval=nullval, quote=True)
def format_field_name(name):
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/pylib/cqlshlib/helptopics.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/helptopics.py b/pylib/cqlshlib/helptopics.py
index bb5b382..65918ba 100644
--- a/pylib/cqlshlib/helptopics.py
+++ b/pylib/cqlshlib/helptopics.py
@@ -33,6 +33,8 @@ class CQLHelpTopics(object):
one of the following topics:
HELP TIMESTAMP_INPUT
+ HELP DATE_INPUT
+ HELP TIME_INPUT
HELP BLOB_INPUT
HELP UUID_INPUT
HELP BOOLEAN_INPUT
@@ -69,6 +71,27 @@ class CQLHelpTopics(object):
server node will be used.
"""
+ def help_date_input(self):
+ print """
+ Date input
+
+ CQL supports the following format for date specification:
+
+ yyyy-mm-dd
+ """
+
+ def help_time_input(self):
+ print """
+ Time input
+
+ CQL supports the following format for time specification:
+
+ HH:MM:SS
+ HH:MM:SS.mmm
+ HH:MM:SS.mmmuuu
+ HH:MM:SS.mmmuuunnn
+ """
+
def help_blob_input(self):
print """
Blob input
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/pylib/cqlshlib/test/test_cqlsh_output.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/test/test_cqlsh_output.py b/pylib/cqlshlib/test/test_cqlsh_output.py
index 60ef3a4..7a7ed89 100644
--- a/pylib/cqlshlib/test/test_cqlsh_output.py
+++ b/pylib/cqlshlib/test/test_cqlsh_output.py
@@ -113,10 +113,11 @@ class TestCqlshOutput(BaseTestCase):
self.assertTrue(6 <= len(output) <= 8,
msg='output: %r' % '\n'.join(output))
self.assertEqual(output[0], '')
- self.assertNicelyFormattedTableHeader(output[1])
- self.assertNicelyFormattedTableRule(output[2])
- self.assertNicelyFormattedTableData(output[3])
- self.assertEqual(output[4].strip(), '')
+ self.assertEqual(output[1], 'Warning! Non-ISO 8601 dates detected (< 1-1-1 or > 9999-12-31). Returning raw days since epoch.');
+ self.assertNicelyFormattedTableHeader(output[2])
+ self.assertNicelyFormattedTableRule(output[3])
+ self.assertNicelyFormattedTableData(output[4])
+ self.assertEqual(output[5].strip(), '')
def test_color_output(self):
for termname in ('xterm', 'unknown-garbage'):
@@ -274,9 +275,9 @@ class TestCqlshOutput(BaseTestCase):
# same query should show up as empty in cql 3
self.assertQueriesGiveColoredOutput((
(q, """
- num | asciicol | bigintcol | blobcol | booleancol | decimalcol | doublecol | floatcol | intcol | textcol | timestampcol | uuidcol | varcharcol | varintcol
- RRR MMMMMMMM MMMMMMMMM MMMMMMM MMMMMMMMMM MMMMMMMMMM MMMMMMMMM MMMMMMMM MMMMMM MMMMMMM MMMMMMMMMMMM MMMMMMM MMMMMMMMMM MMMMMMMMM
- -----+----------+-----------+---------+------------+------------+-----------+----------+--------+---------+--------------+---------+------------+-----------
+ num | asciicol | bigintcol | blobcol | booleancol | datecol | decimalcol | doublecol | floatcol | intcol | textcol | timecol | timestampcol | uuidcol | varcharcol | varintcol
+ RRR MMMMMMMM MMMMMMMMM MMMMMMM MMMMMMMMMM MMMMMMM MMMMMMMMMM MMMMMMMMM MMMMMMMM MMMMMM MMMMMMM MMMMMMM MMMMMMMMMMMM MMMMMMM MMMMMMMMMM MMMMMMMMM
+ -----+----------+-----------+---------+------------+---------+------------+-----------+----------+--------+---------+---------+--------------+---------+------------+-----------
(0 rows)
@@ -597,11 +598,13 @@ class TestCqlshOutput(BaseTestCase):
bigintcol bigint,
blobcol blob,
booleancol boolean,
+ datecol date,
decimalcol decimal,
doublecol double,
floatcol float,
intcol int,
textcol text,
+ timecol time,
timestampcol timestamp,
uuidcol uuid,
varcharcol text,
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/pylib/cqlshlib/test/test_keyspace_init.cql
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/test/test_keyspace_init.cql b/pylib/cqlshlib/test/test_keyspace_init.cql
index cd5ac75..1d1e5aa 100644
--- a/pylib/cqlshlib/test/test_keyspace_init.cql
+++ b/pylib/cqlshlib/test/test_keyspace_init.cql
@@ -5,10 +5,12 @@ CREATE TABLE has_all_types (
bigintcol bigint,
blobcol blob,
booleancol boolean,
+ datecol date,
decimalcol decimal,
doublecol double,
floatcol float,
textcol text,
+ timecol time,
timestampcol timestamp,
uuidcol uuid,
varcharcol varchar,
@@ -16,40 +18,41 @@ CREATE TABLE has_all_types (
) WITH compression = {'sstable_compression':'LZ4Compressor'};
INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol,
- decimalcol, doublecol, floatcol, textcol,
- timestampcol, uuidcol, varcharcol, varintcol)
+ datecol, decimalcol, doublecol, floatcol, textcol,
+ timecol, timestampcol, uuidcol, varcharcol, varintcol)
VALUES (0, -12, 'abcdefg', 1234567890123456789, 0x000102030405fffefd, true,
- 19952.11882, 1.0, -2.1, 'Voilá!', '2012-05-14 12:53:20+0000',
- bd1924e1-6af8-44ae-b5e1-f24131dbd460, '"', 10000000000000000000000000);
+ '2001-07-13', 19952.11882, 1.0, -2.1, 'Voilá!',
+ '23:12:15.889445123', '2012-05-14 12:53:20+0000', bd1924e1-6af8-44ae-b5e1-f24131dbd460, '"', 10000000000000000000000000);
INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol,
- decimalcol, doublecol, floatcol, textcol,
- timestampcol, uuidcol, varcharcol, varintcol)
+ datecol, decimalcol, doublecol, floatcol, textcol,
+ timecol, timestampcol, uuidcol, varcharcol, varintcol)
VALUES (1, 2147483647, '__!''$#@!~"', 9223372036854775807, 0xffffffffffffffffff, true,
- 0.00000000000001, 9999999.999, 99999.99, '∭Ƕ⑮ฑ➳❏''', '1900-01-01+0000',
- ffffffff-ffff-ffff-ffff-ffffffffffff, 'newline->
+ '2012-11-30', 0.00000000000001, 9999999.999, 99999.99, '∭Ƕ⑮ฑ➳❏''',
+ '00:01:59.998994', '1900-01-01+0000', ffffffff-ffff-ffff-ffff-ffffffffffff, 'newline->
<-', 9);
INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol,
- decimalcol, doublecol, floatcol, textcol,
- timestampcol, uuidcol, varcharcol, varintcol)
+ datecol, decimalcol, doublecol, floatcol, textcol,
+ timecol, timestampcol, uuidcol, varcharcol, varintcol)
VALUES (2, 0, '', 0, 0x, false,
- 0.0, 0.0, 0.0, '', 0,
- 00000000-0000-0000-0000-000000000000, '', 0);
+ 0, 0.0, 0.0, 0.0, '',
+ '0:0:0.1', 0, 00000000-0000-0000-0000-000000000000, '', 0);
INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol,
- decimalcol, doublecol, floatcol, textcol,
- timestampcol, uuidcol, varcharcol, varintcol)
+ datecol, decimalcol, doublecol, floatcol, textcol,
+ timecol, timestampcol, uuidcol, varcharcol, varintcol)
VALUES (3, -2147483648, '''''''', -9223372036854775808, 0x80, false,
- 10.0000000000000, -1004.10, 100000000.9, '龍馭鬱', '2038-01-19T03:14-1200',
- ffffffff-ffff-1fff-8fff-ffffffffffff, '''', -10000000000000000000000000);
+ '9999-12-31', 10.0000000000000, -1004.10, 100000000.9, '龍馭鬱',
+ '23:59:59.999999999', '2038-01-19T03:14-1200', ffffffff-ffff-1fff-8fff-ffffffffffff,
+ '''', -10000000000000000000000000);
INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol,
decimalcol, doublecol, floatcol, textcol,
timestampcol, uuidcol, varcharcol, varintcol)
-VALUES (4, blobAsInt(0x), '', blobAsBigint(0x), 0x, blobAsBoolean(0x), blobAsDecimal(0x),
- blobAsDouble(0x), blobAsFloat(0x), '', blobAsTimestamp(0x), blobAsUuid(0x), '',
- blobAsVarint(0x));
+VALUES (4, blobAsInt(0x), '', blobAsBigint(0x), 0x, blobAsBoolean(0x),
+ blobAsDecimal(0x), blobAsDouble(0x), blobAsFloat(0x), '',
+ blobAsTimestamp(0x), blobAsUuid(0x), '', blobAsVarint(0x));
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/src/java/org/apache/cassandra/cql3/CQL3Type.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CQL3Type.java b/src/java/org/apache/cassandra/cql3/CQL3Type.java
index b656de8..5fc518d 100644
--- a/src/java/org/apache/cassandra/cql3/CQL3Type.java
+++ b/src/java/org/apache/cassandra/cql3/CQL3Type.java
@@ -38,22 +38,24 @@ public interface CQL3Type
public enum Native implements CQL3Type
{
- ASCII (AsciiType.instance),
- BIGINT (LongType.instance),
- BLOB (BytesType.instance),
- BOOLEAN (BooleanType.instance),
- COUNTER (CounterColumnType.instance),
- DECIMAL (DecimalType.instance),
- DOUBLE (DoubleType.instance),
- FLOAT (FloatType.instance),
- INET (InetAddressType.instance),
- INT (Int32Type.instance),
- TEXT (UTF8Type.instance),
- TIMESTAMP(TimestampType.instance),
- UUID (UUIDType.instance),
- VARCHAR (UTF8Type.instance),
- VARINT (IntegerType.instance),
- TIMEUUID (TimeUUIDType.instance);
+ ASCII (AsciiType.instance),
+ BIGINT (LongType.instance),
+ BLOB (BytesType.instance),
+ BOOLEAN (BooleanType.instance),
+ COUNTER (CounterColumnType.instance),
+ DECIMAL (DecimalType.instance),
+ DOUBLE (DoubleType.instance),
+ FLOAT (FloatType.instance),
+ INET (InetAddressType.instance),
+ INT (Int32Type.instance),
+ TEXT (UTF8Type.instance),
+ TIMESTAMP (TimestampType.instance),
+ UUID (UUIDType.instance),
+ VARCHAR (UTF8Type.instance),
+ VARINT (IntegerType.instance),
+ TIMEUUID (TimeUUIDType.instance),
+ DATE (SimpleDateType.instance),
+ TIME (TimeType.instance);
private final AbstractType<?> type;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/src/java/org/apache/cassandra/cql3/Constants.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Constants.java b/src/java/org/apache/cassandra/cql3/Constants.java
index 01fbdf0..2d5fdaa 100644
--- a/src/java/org/apache/cassandra/cql3/Constants.java
+++ b/src/java/org/apache/cassandra/cql3/Constants.java
@@ -44,7 +44,7 @@ public abstract class Constants
public enum Type
{
- STRING, INTEGER, UUID, FLOAT, BOOLEAN, HEX;
+ STRING, INTEGER, UUID, FLOAT, DATE, TIME, BOOLEAN, HEX;
}
public static final Term.Raw NULL_LITERAL = new Term.Raw()
@@ -179,6 +179,8 @@ public abstract class Constants
case TEXT:
case INET:
case VARCHAR:
+ case DATE:
+ case TIME:
case TIMESTAMP:
return true;
}
@@ -188,6 +190,7 @@ public abstract class Constants
{
case BIGINT:
case COUNTER:
+ case DATE:
case DECIMAL:
case DOUBLE:
case FLOAT:
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index 6a21a8b..a1545d3 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -1161,6 +1161,8 @@ native_type returns [CQL3Type t]
| K_VARCHAR { $t = CQL3Type.Native.VARCHAR; }
| K_VARINT { $t = CQL3Type.Native.VARINT; }
| K_TIMEUUID { $t = CQL3Type.Native.TIMEUUID; }
+ | K_DATE { $t = CQL3Type.Native.DATE; }
+ | K_TIME { $t = CQL3Type.Native.TIME; }
;
collection_type returns [CQL3Type.Raw pt]
@@ -1326,6 +1328,8 @@ K_VARINT: V A R I N T;
K_TIMEUUID: T I M E U U I D;
K_TOKEN: T O K E N;
K_WRITETIME: W R I T E T I M E;
+K_DATE: D A T E;
+K_TIME: T I M E;
K_NULL: N U L L;
K_NOT: N O T;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/src/java/org/apache/cassandra/db/marshal/IntegerType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/IntegerType.java b/src/java/org/apache/cassandra/db/marshal/IntegerType.java
index ec1c7ad..6da8d39 100644
--- a/src/java/org/apache/cassandra/db/marshal/IntegerType.java
+++ b/src/java/org/apache/cassandra/db/marshal/IntegerType.java
@@ -60,6 +60,11 @@ public final class IntegerType extends AbstractType<BigInteger>
public int compare(ByteBuffer lhs, ByteBuffer rhs)
{
+ return IntegerType.compareIntegers(lhs, rhs);
+ }
+
+ public static int compareIntegers(ByteBuffer lhs, ByteBuffer rhs)
+ {
int lhsLen = lhs.remaining();
int rhsLen = rhs.remaining();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/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
new file mode 100644
index 0000000..1dbcf03
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java
@@ -0,0 +1,72 @@
+/*
+ * 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.db.marshal;
+
+import java.nio.ByteBuffer;
+
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.SimpleDateSerializer;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class SimpleDateType extends AbstractType<Integer>
+{
+ public static final SimpleDateType instance = new SimpleDateType();
+
+ SimpleDateType() {} // singleton
+
+ public int compare(ByteBuffer o1, ByteBuffer o2)
+ {
+ // We add Integer.MIN_VALUE to overflow to allow unsigned comparison
+ return ByteBufferUtil.compareUnsigned(o1, o2);
+ }
+
+ public boolean isByteOrderComparable()
+ {
+ return true;
+ }
+
+ public ByteBuffer fromString(String source) throws MarshalException
+ {
+ return ByteBufferUtil.bytes(SimpleDateSerializer.dateStringToDays(source));
+ }
+
+ @Override
+ public boolean isCompatibleWith(AbstractType<?> previous)
+ {
+ return super.isCompatibleWith(previous);
+ }
+
+ @Override
+ public boolean isValueCompatibleWithInternal(AbstractType<?> otherType)
+ {
+ return this == otherType || otherType == IntegerType.instance;
+ }
+
+ @Override
+ public CQL3Type asCQL3Type()
+ {
+ return CQL3Type.Native.DATE;
+ }
+
+ public TypeSerializer<Integer> getSerializer()
+ {
+ return SimpleDateSerializer.instance;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/src/java/org/apache/cassandra/db/marshal/TimeType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TimeType.java b/src/java/org/apache/cassandra/db/marshal/TimeType.java
new file mode 100644
index 0000000..b9a0076
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/marshal/TimeType.java
@@ -0,0 +1,72 @@
+/*
+ * 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.db.marshal;
+
+import java.nio.ByteBuffer;
+
+import org.apache.cassandra.serializers.TimeSerializer;
+import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+/**
+ * Nanosecond resolution time values
+ */
+public class TimeType extends AbstractType<Long>
+{
+ public static final TimeType instance = new TimeType();
+ private TimeType() {} // singleton
+
+ public int compare(ByteBuffer o1, ByteBuffer o2)
+ {
+ return ByteBufferUtil.compareUnsigned(o1, o2);
+ }
+
+ public ByteBuffer fromString(String source) throws MarshalException
+ {
+ return decompose(TimeSerializer.timeStringToLong(source));
+ }
+
+ public boolean isByteOrderComparable()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean isCompatibleWith(AbstractType<?> previous)
+ {
+ return super.isCompatibleWith(previous);
+ }
+
+ @Override
+ public boolean isValueCompatibleWithInternal(AbstractType<?> otherType)
+ {
+ return this == otherType || otherType == LongType.instance;
+ }
+
+ public CQL3Type asCQL3Type()
+ {
+ return CQL3Type.Native.TIME;
+ }
+
+ public TypeSerializer<Long> getSerializer()
+ {
+ return TimeSerializer.instance;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/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
new file mode 100644
index 0000000..221842b
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/SimpleDateSerializer.java
@@ -0,0 +1,117 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+// For byte-order comparability, we shift by Integer.MIN_VALUE and treat the data as an unsigned integer ranging from
+// min date to max date w/epoch sitting in the center @ 2^31
+public class SimpleDateSerializer implements TypeSerializer<Integer>
+{
+ private static final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd").withZone(DateTimeZone.UTC);
+ private static final long minSupportedDateMillis = TimeUnit.DAYS.toMillis(Integer.MIN_VALUE);
+ private static final long maxSupportedDateMillis = TimeUnit.DAYS.toMillis(Integer.MAX_VALUE);
+ private static final long maxSupportedDays = (long)Math.pow(2,32) - 1;
+ private static final long byteOrderShift = (long)Math.pow(2,31) * 2;
+
+ private static final Pattern rawPattern = Pattern.compile("^-?\\d+$");
+ public static final SimpleDateSerializer instance = new SimpleDateSerializer();
+
+ public Integer deserialize(ByteBuffer bytes)
+ {
+ return bytes.remaining() == 0 ? null : ByteBufferUtil.toInt(bytes);
+ }
+
+ public ByteBuffer serialize(Integer value)
+ {
+ return value == null ? ByteBufferUtil.EMPTY_BYTE_BUFFER : ByteBufferUtil.bytes(value);
+ }
+
+ public static Integer dateStringToDays(String source) throws MarshalException
+ {
+ // Raw day value in unsigned int form, epoch @ 2^31
+ if (rawPattern.matcher(source).matches())
+ {
+ try
+ {
+ Long result = Long.parseLong(source);
+
+ if (result < 0 || result > maxSupportedDays)
+ throw new NumberFormatException("Input out of bounds: " + source);
+
+ // Shift > epoch days into negative portion of Integer result for byte order comparability
+ if (result >= Integer.MAX_VALUE)
+ result -= byteOrderShift;
+
+ return result.intValue();
+ }
+ catch (NumberFormatException e)
+ {
+ throw new MarshalException(String.format("Unable to make unsigned int (for date) from: '%s'", source), e);
+ }
+ }
+
+ // Attempt to parse as date string
+ try
+ {
+ DateTime parsed = formatter.parseDateTime(source);
+ long millis = parsed.getMillis();
+ if (millis < minSupportedDateMillis)
+ throw new MarshalException(String.format("Input date %s is less than min supported date %s", source, new LocalDate(minSupportedDateMillis).toString()));
+ 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;
+ }
+ catch (IllegalArgumentException e1)
+ {
+ throw new MarshalException(String.format("Unable to coerce '%s' to a formatted date (long)", source), e1);
+ }
+ }
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ if (bytes.remaining() != 4)
+ throw new MarshalException(String.format("Expected 4 byte long for date (%d)", bytes.remaining()));
+ }
+
+ public String toString(Integer value)
+ {
+ if (value == null)
+ return "";
+
+ return formatter.print(new LocalDate(TimeUnit.DAYS.toMillis(value - Integer.MIN_VALUE), DateTimeZone.UTC));
+ }
+
+ public Class<Integer> getType()
+ {
+ return Integer.class;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/src/java/org/apache/cassandra/serializers/TimeSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/TimeSerializer.java b/src/java/org/apache/cassandra/serializers/TimeSerializer.java
new file mode 100644
index 0000000..b02d425
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/TimeSerializer.java
@@ -0,0 +1,198 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class TimeSerializer implements TypeSerializer<Long>
+{
+ public static final Pattern timePattern = Pattern.compile("^-?\\d+$");
+ public static final TimeSerializer instance = new TimeSerializer();
+
+ public Long deserialize(ByteBuffer bytes)
+ {
+ return bytes.remaining() == 0 ? null : ByteBufferUtil.toLong(bytes);
+ }
+
+ public ByteBuffer serialize(Long value)
+ {
+ return value == null ? ByteBufferUtil.EMPTY_BYTE_BUFFER : ByteBufferUtil.bytes(value);
+ }
+
+ public static Long timeStringToLong(String source) throws MarshalException
+ {
+ // nano since start of day, raw
+ if (timePattern.matcher(source).matches())
+ {
+ try
+ {
+ Long result = Long.parseLong(source);
+ if (result < 0 || result > TimeUnit.DAYS.toNanos(1))
+ throw new NumberFormatException("Input long out of bounds: " + source);
+ return result;
+ }
+ catch (NumberFormatException e)
+ {
+ throw new MarshalException(String.format("Unable to make long (for time) from: '%s'", source), e);
+ }
+ }
+
+ // Last chance, attempt to parse as time string
+ try
+ {
+ return parseTimeStrictly(source);
+ }
+ catch (IllegalArgumentException e1)
+ {
+ throw new MarshalException(String.format("(TimeType) Unable to coerce '%s' to a formatted time (long)", source), e1);
+ }
+ }
+
+ public void validate(ByteBuffer bytes) throws MarshalException
+ {
+ if (bytes.remaining() != 8)
+ throw new MarshalException(String.format("Expected 8 byte long for time (%d)", bytes.remaining()));
+ }
+
+ public String toString(Long value)
+ {
+ if (value == null)
+ return "null";
+
+ int nano = (int)(value % 1000);
+ value -= nano;
+ value /= 1000;
+ int micro = (int)(value % 1000);
+ value -= micro;
+ value /= 1000;
+ int milli = (int)(value % 1000);
+ value -= milli;
+ value /= 1000;
+ int seconds = (int)(value % 60);
+ value -= seconds;
+ value /= 60;
+ int minutes = (int)(value % 60);
+ value -= minutes;
+ value /= 60;
+ int hours = (int)(value % 24);
+ value -= hours;
+ value /= 24;
+ assert(value == 0);
+
+ StringBuilder sb = new StringBuilder();
+ leftPadZeros(hours, 2, sb);
+ sb.append(":");
+ leftPadZeros(minutes, 2, sb);
+ sb.append(":");
+ leftPadZeros(seconds, 2, sb);
+ sb.append(".");
+ leftPadZeros(milli, 3, sb);
+ leftPadZeros(micro, 3, sb);
+ leftPadZeros(nano, 3, sb);
+ return sb.toString();
+ }
+
+ private void leftPadZeros(int value, int digits, StringBuilder sb)
+ {
+ for (int i = 1; i < digits; ++i)
+ {
+ if (value < Math.pow(10, i))
+ sb.append("0");
+ }
+ sb.append(value);
+ }
+
+ public Class<Long> getType()
+ {
+ return Long.class;
+ }
+
+ // Time specific parsing loosely based on java.sql.Timestamp
+ private static Long parseTimeStrictly(String s) throws IllegalArgumentException
+ {
+ String nanos_s;
+
+ long hour;
+ long minute;
+ long second;
+ long a_nanos = 0;
+
+ int firstColon = 0;
+ int secondColon = 0;
+ int period = 0;
+ String formatError = "Timestamp format must be hh:mm:ss[.fffffffff]";
+ String zeros = "000000000";
+
+ if (s == null)
+ throw new java.lang.IllegalArgumentException(formatError);
+ s = s.trim();
+
+ // Parse the time
+ firstColon = s.indexOf(':');
+ secondColon = s.indexOf(':', firstColon+1);
+ period = s.indexOf('.', secondColon+1);
+
+ // Convert the time; default missing nanos
+ if (firstColon > 0 && secondColon > 0 && secondColon < s.length() - 1)
+ {
+ hour = Integer.parseInt(s.substring(0, firstColon));
+ if (hour < 0 || hour >= 24)
+ throw new IllegalArgumentException("Hour out of bounds.");
+
+ minute = Integer.parseInt(s.substring(firstColon + 1, secondColon));
+ if (minute < 0 || minute >= 60)
+ throw new IllegalArgumentException("Minute out of bounds.");
+
+ if (period > 0 && period < s.length() - 1)
+ {
+ second = Integer.parseInt(s.substring(secondColon + 1, period));
+ if (second < 0 || second >= 60)
+ throw new IllegalArgumentException("Second out of bounds.");
+
+ nanos_s = s.substring(period + 1);
+ if (nanos_s.length() > 9)
+ throw new IllegalArgumentException(formatError);
+ if (!Character.isDigit(nanos_s.charAt(0)))
+ throw new IllegalArgumentException(formatError);
+ nanos_s = nanos_s + zeros.substring(0, 9 - nanos_s.length());
+ a_nanos = Integer.parseInt(nanos_s);
+ }
+ else if (period > 0)
+ throw new IllegalArgumentException(formatError);
+ else
+ {
+ second = Integer.parseInt(s.substring(secondColon + 1));
+ if (second < 0 || second >= 60)
+ throw new IllegalArgumentException("Second out of bounds.");
+ }
+ }
+ else
+ throw new IllegalArgumentException(formatError);
+
+ long rawTime = 0;
+ rawTime += TimeUnit.HOURS.toNanos(hour);
+ rawTime += TimeUnit.MINUTES.toNanos(minute);
+ rawTime += TimeUnit.SECONDS.toNanos(second);
+ rawTime += a_nanos;
+ return rawTime;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/src/java/org/apache/cassandra/serializers/TypeSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/TypeSerializer.java b/src/java/org/apache/cassandra/serializers/TypeSerializer.java
index 7b037c0..cddef08 100644
--- a/src/java/org/apache/cassandra/serializers/TypeSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/TypeSerializer.java
@@ -35,3 +35,4 @@ public interface TypeSerializer<T>
public Class<T> getType();
}
+
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/src/java/org/apache/cassandra/transport/DataType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/transport/DataType.java b/src/java/org/apache/cassandra/transport/DataType.java
index 0ea353a..e13194d 100644
--- a/src/java/org/apache/cassandra/transport/DataType.java
+++ b/src/java/org/apache/cassandra/transport/DataType.java
@@ -49,6 +49,8 @@ public enum DataType implements OptionCodec.Codecable<DataType>
VARINT (14, IntegerType.instance),
TIMEUUID (15, TimeUUIDType.instance),
INET (16, InetAddressType.instance),
+ DATE (17, DateType.instance),
+ TIME (18, TimeType.instance),
LIST (32, null),
MAP (33, null),
SET (34, null),
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/test/unit/org/apache/cassandra/db/marshal/SimpleDateTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/marshal/SimpleDateTypeTest.java b/test/unit/org/apache/cassandra/db/marshal/SimpleDateTypeTest.java
new file mode 100644
index 0000000..5c9ed4e
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/marshal/SimpleDateTypeTest.java
@@ -0,0 +1,153 @@
+/**
+ * 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.db.marshal;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Test;
+import org.apache.cassandra.serializers.SimpleDateSerializer;
+
+public class SimpleDateTypeTest
+{
+ @Test public void TestComparison()
+ {
+ ByteBuffer d1 = SimpleDateType.instance.fromString("1970-01-05");
+ ByteBuffer d2 = SimpleDateSerializer.instance.serialize(makeUnsigned(4));
+ assert SimpleDateType.instance.compare(d1, d2) == 0 : "Failed == comparison";
+ String.format("Failed == comparison with %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateType.instance.fromString("1970-01-05");
+ d2 = SimpleDateSerializer.instance.serialize(makeUnsigned(10));
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed comparison of %s and %s, expected <",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateType.instance.fromString("1970-01-05"); // 4
+ d2 = SimpleDateSerializer.instance.serialize(makeUnsigned(-10));
+ assert SimpleDateType.instance.compare(d1, d2) > 0 :
+ String.format("Failed comparison of %s and %s, expected > 0",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateType.instance.fromString("1");
+ d2 = SimpleDateType.instance.fromString("1000");
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed < comparison with string inputs %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ Integer intLimit = Integer.MAX_VALUE;
+ d1 = SimpleDateType.instance.fromString("0");
+ d2 = SimpleDateType.instance.fromString(intLimit.toString());
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed < comparison with string inputs at integer bounds %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ Long overLimit = (long)(Integer.MAX_VALUE);
+ d1 = SimpleDateType.instance.fromString("0");
+ d2 = SimpleDateType.instance.fromString(overLimit.toString());
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed < comparison with string inputs at integer bounds %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ Long i1 = 0L;
+ Long i2 = (long)Math.pow(2,32) - 1;
+ d1 = SimpleDateType.instance.fromString(i1.toString());
+ d2 = SimpleDateType.instance.fromString(i2.toString());
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed limits comparison with %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateType.instance.fromString("256");
+ d2 = SimpleDateType.instance.fromString("512");
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed comparison with %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateSerializer.instance.serialize(makeUnsigned(0));
+ d2 = SimpleDateSerializer.instance.serialize(makeUnsigned(Integer.MAX_VALUE));
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed neg/pos comparison with %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateType.instance.fromString("-10000-10-10");
+ d2 = SimpleDateType.instance.fromString("10000-10-10");
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed neg/pos string comparison with %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateType.instance.fromString("1969-12-31");
+ d2 = SimpleDateType.instance.fromString("1970-1-1");
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed pre/post epoch comparison with %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateType.instance.fromString("1970-1-1");
+ d2 = SimpleDateType.instance.fromString("1970-1-1");
+ assert SimpleDateType.instance.compare(d1, d2) == 0 :
+ String.format("Failed == date from string comparison with %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ d1 = SimpleDateType.instance.fromString("1970-1-1");
+ d2 = SimpleDateType.instance.fromString("1970-1-2");
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed post epoch string comparison with %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+
+ for (int i = 0; i < 32; ++i)
+ {
+ int offset = (int)Math.pow(2,i);
+ d1 = SimpleDateSerializer.instance.serialize(makeUnsigned(0 - offset));
+ d2 = SimpleDateSerializer.instance.serialize(makeUnsigned(offset));
+ assert SimpleDateType.instance.compare(d1, d2) < 0 :
+ String.format("Failed < comparison of %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+ }
+
+ for (int i = 0; i < 32; ++i)
+ {
+ int offset = (int)Math.pow(2,i);
+ d1 = SimpleDateSerializer.instance.serialize(makeUnsigned(offset));
+ d2 = SimpleDateSerializer.instance.serialize(makeUnsigned(0 - offset));
+ assert SimpleDateType.instance.compare(d1, d2) > 0 :
+ String.format("Failed > comparison of %s and %s",
+ SimpleDateSerializer.instance.deserialize(d1),
+ SimpleDateSerializer.instance.deserialize(d2));
+ }
+ }
+
+ private Integer makeUnsigned(int input)
+ {
+ return input - Integer.MIN_VALUE;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/107545b3/test/unit/org/apache/cassandra/db/marshal/TimeTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/marshal/TimeTypeTest.java b/test/unit/org/apache/cassandra/db/marshal/TimeTypeTest.java
new file mode 100644
index 0000000..3057b9c
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/marshal/TimeTypeTest.java
@@ -0,0 +1,61 @@
+/**
+ * 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.db.marshal;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.apache.cassandra.serializers.TimeSerializer;
+
+public class TimeTypeTest
+{
+ @Test public void TestComparison()
+ {
+ Long t1 = TimeSerializer.timeStringToLong("01:00:00.123456789");
+ Long t2 = new Long((1L * 60L * 60L * 1000L * 1000L * 1000L) + 123456789);
+ ByteBuffer b1 = TimeSerializer.instance.serialize(t1);
+ ByteBuffer b2 = TimeSerializer.instance.serialize(t2);
+ assert TimeType.instance.compare(b1, b2) == 0 : "Failed == comparison";
+
+ b2 = TimeSerializer.instance.serialize(123456789L);
+ assert TimeType.instance.compare(b1, b2) > 0 : "Failed > comparison";
+
+ t2 = new Long(2L * 60L * 60L * 1000L * 1000L * 1000L + 123456789);
+ b2 = TimeSerializer.instance.serialize(t2);
+ assert TimeType.instance.compare(b1, b2) < 0 : "Failed < comparison";
+
+ b1 = TimeSerializer.instance.serialize(0L);
+ b2 = TimeSerializer.instance.serialize(0L);
+ assert TimeType.instance.compare(b1, b2) == 0 : "Failed == comparison on 0";
+
+ b1 = TimeSerializer.instance.serialize(0L);
+ b2 = TimeSerializer.instance.serialize(10000000L);
+ assert TimeType.instance.compare(b1, b2) == -1 : "Failed < comparison on 0";
+
+ b1 = TimeSerializer.instance.serialize(0L);
+ b2 = TimeSerializer.instance.serialize(TimeUnit.DAYS.toNanos(1));
+ assert TimeType.instance.compare(b1, b2) == -1 : "Failed < comparison against max range.";
+
+ b1 = TimeSerializer.instance.serialize(TimeUnit.DAYS.toNanos(1));
+ b2 = TimeSerializer.instance.serialize(0L);
+ assert TimeType.instance.compare(b1, b2) == 1 : "Failed > comparison against max range.";
+ }
+}