This is an automated email from the ASF dual-hosted git repository.
granthenke pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push:
new 62f6a30 [KUDU-2632] Add a DATE type backed by INT32 (Part 2, Java
client)
62f6a30 is described below
commit 62f6a300a5c092a6c61c91fe1546014eb8787b6e
Author: Volodymyr Verovkin <[email protected]>
AuthorDate: Sun Oct 20 23:51:50 2019 -0700
[KUDU-2632] Add a DATE type backed by INT32 (Part 2, Java client)
This adds a new DATE type, represented by an INT32 and that should
store the number of days from the Unix epoch, January 1, 1970.
Range:
from
LocalDate.parse("0001-01-01").toEpochDay()
to
LocalDate.parse("9999-12-31").toEpochDay()
Range validation is done in DateUtil::checkDateWithinRange() function.
Change-Id: I9a09f4f9c804c1b2965e78da78528225a9c769e2
Reviewed-on: http://gerrit.cloudera.org:8080/14517
Tested-by: Kudu Jenkins
Reviewed-by: Grant Henke <[email protected]>
Tested-by: Alexey Serbin <[email protected]>
---
.../org/apache/kudu/backup/TableMetadata.scala | 10 +++
.../kudu/mapreduce/tools/ExportCsvMapper.java | 3 +
.../src/main/java/org/apache/kudu/Type.java | 8 +-
.../apache/kudu/client/ColumnRangePredicate.java | 1 +
.../java/org/apache/kudu/client/KeyEncoder.java | 6 ++
.../java/org/apache/kudu/client/KuduPredicate.java | 26 ++++++-
.../java/org/apache/kudu/client/PartialRow.java | 87 ++++++++++++++++++++++
.../org/apache/kudu/client/ProtobufHelper.java | 6 ++
.../java/org/apache/kudu/client/RowResult.java | 38 +++++++++-
.../java/org/apache/kudu/util/DataGenerator.java | 14 ++++
.../main/java/org/apache/kudu/util/DateUtil.java | 77 +++++++++++++++++++
.../java/org/apache/kudu/util/SchemaGenerator.java | 5 ++
.../org/apache/kudu/client/TestKeyEncoding.java | 16 ++--
.../org/apache/kudu/client/TestKuduClient.java | 47 ++++++++++++
.../java/org/apache/kudu/client/TestOperation.java | 5 +-
.../org/apache/kudu/client/TestPartialRow.java | 39 +++++++++-
.../java/org/apache/kudu/client/TestRowResult.java | 7 ++
.../java/org/apache/kudu/util/TestDateUtil.java | 78 +++++++++++++++++++
.../spark/tools/DistributedDataGenerator.scala | 3 +
.../org/apache/kudu/spark/kudu/RowConverter.scala | 2 +
.../org/apache/kudu/spark/kudu/SparkUtil.scala | 2 +
.../apache/kudu/spark/kudu/DefaultSourceTest.scala | 2 +-
.../apache/kudu/spark/kudu/KuduContextTest.scala | 6 +-
.../org/apache/kudu/spark/kudu/KuduTestSuite.scala | 6 +-
.../java/org/apache/kudu/test/ClientTestUtil.java | 14 +++-
25 files changed, 490 insertions(+), 18 deletions(-)
diff --git
a/java/kudu-backup-common/src/main/scala/org/apache/kudu/backup/TableMetadata.scala
b/java/kudu-backup-common/src/main/scala/org/apache/kudu/backup/TableMetadata.scala
index 1d1f6d7..a859d92 100644
---
a/java/kudu-backup-common/src/main/scala/org/apache/kudu/backup/TableMetadata.scala
+++
b/java/kudu-backup-common/src/main/scala/org/apache/kudu/backup/TableMetadata.scala
@@ -18,6 +18,7 @@
package org.apache.kudu.backup
import java.math.BigDecimal
+import java.sql.Date
import java.util
import com.google.protobuf.ByteString
@@ -32,6 +33,7 @@ import org.apache.kudu.client.CreateTableOptions
import org.apache.kudu.client.KuduTable
import org.apache.kudu.client.PartialRow
import org.apache.kudu.client.PartitionSchema
+import org.apache.kudu.util.DateUtil
import org.apache.kudu.ColumnSchema
import org.apache.kudu.Schema
import org.apache.kudu.Type
@@ -241,6 +243,7 @@ object TableMetadata {
case Type.STRING => row.getString(columnName)
case Type.BINARY => row.getBinary(columnName)
case Type.DECIMAL => row.getDecimal(columnName)
+ case Type.DATE => row.getDate(columnName)
case _ =>
throw new IllegalArgumentException(s"Unsupported column type:
$colType")
}
@@ -262,11 +265,15 @@ object TableMetadata {
row.addBinary(columnName, value.asInstanceOf[Array[Byte]])
case Type.DECIMAL =>
row.addDecimal(columnName, value.asInstanceOf[BigDecimal])
+ case Type.DATE => row.addDate(columnName, value.asInstanceOf[Date])
case _ =>
throw new IllegalArgumentException(s"Unsupported column type:
$colType")
}
}
+ /**
+ * Returns the string value of serialized value according to the type of
column.
+ */
private def valueToString(value: Any, colType: Type): String = {
colType match {
case Type.BOOL =>
@@ -293,6 +300,8 @@ object TableMetadata {
value
.asInstanceOf[BigDecimal]
.toString // TODO: Explicitly control print format
+ case Type.DATE =>
+ value.asInstanceOf[Date].toString()
case _ =>
throw new IllegalArgumentException(s"Unsupported column type:
$colType")
}
@@ -311,6 +320,7 @@ object TableMetadata {
case Type.STRING => value
case Type.BINARY => Base64.decodeBase64(value)
case Type.DECIMAL => new BigDecimal(value) // TODO: Explicitly pass scale
+ case Type.DATE => Date.valueOf(value)
case _ =>
throw new IllegalArgumentException(s"Unsupported column type:
$colType")
}
diff --git
a/java/kudu-client-tools/src/main/java/org/apache/kudu/mapreduce/tools/ExportCsvMapper.java
b/java/kudu-client-tools/src/main/java/org/apache/kudu/mapreduce/tools/ExportCsvMapper.java
index d7b4800..9692fc6 100644
---
a/java/kudu-client-tools/src/main/java/org/apache/kudu/mapreduce/tools/ExportCsvMapper.java
+++
b/java/kudu-client-tools/src/main/java/org/apache/kudu/mapreduce/tools/ExportCsvMapper.java
@@ -104,6 +104,9 @@ public class ExportCsvMapper extends Mapper<NullWritable,
RowResult, NullWritabl
case BOOL:
buf.append(value.getBoolean(i));
break;
+ case DATE:
+ buf.append(value.getInt(i));
+ break;
default:
buf.append("<unknown type!>");
break;
diff --git a/java/kudu-client/src/main/java/org/apache/kudu/Type.java
b/java/kudu-client/src/main/java/org/apache/kudu/Type.java
index b1cb785..5a346c3 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/Type.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/Type.java
@@ -49,7 +49,8 @@ public enum Type {
DOUBLE(DataType.DOUBLE, "double"),
UNIXTIME_MICROS(DataType.UNIXTIME_MICROS, "unixtime_micros"),
DECIMAL(Arrays.asList(DataType.DECIMAL32, DataType.DECIMAL64,
DataType.DECIMAL128), "decimal"),
- VARCHAR(DataType.VARCHAR, "varchar");
+ VARCHAR(DataType.VARCHAR, "varchar"),
+ DATE(DataType.DATE, "date");
private final ImmutableList<DataType> dataTypes;
private final String name;
@@ -77,6 +78,7 @@ public enum Type {
* @return A DataType
* @deprecated use {@link #getDataType(ColumnTypeAttributes)}
*/
+ @Deprecated
public DataType getDataType() {
if (this == DECIMAL) {
throw new IllegalStateException("Please use the newer
getDataType(ColumnTypeAttributes) " +
@@ -110,6 +112,7 @@ public enum Type {
* @return A size
* @deprecated use {@link #getSize(ColumnTypeAttributes)}
*/
+ @Deprecated
public int getSize() {
if (this == DECIMAL) {
throw new IllegalStateException("Please use the newer
getSize(ColumnTypeAttributes) " +
@@ -153,6 +156,7 @@ public enum Type {
case INT16:
return Shorts.BYTES;
case INT32:
+ case DATE:
case FLOAT:
return Ints.BYTES;
case INT64:
@@ -194,6 +198,8 @@ public enum Type {
return FLOAT;
case DOUBLE:
return DOUBLE;
+ case DATE:
+ return DATE;
case DECIMAL32:
case DECIMAL64:
case DECIMAL128:
diff --git
a/java/kudu-client/src/main/java/org/apache/kudu/client/ColumnRangePredicate.java
b/java/kudu-client/src/main/java/org/apache/kudu/client/ColumnRangePredicate.java
index 8a8a26e..ec7e0b8 100644
---
a/java/kudu-client/src/main/java/org/apache/kudu/client/ColumnRangePredicate.java
+++
b/java/kudu-client/src/main/java/org/apache/kudu/client/ColumnRangePredicate.java
@@ -89,6 +89,7 @@ public class ColumnRangePredicate {
case INT16:
return KuduPredicate.newComparisonPredicate(column, op,
Bytes.getShort(bound));
case INT32:
+ case DATE:
return KuduPredicate.newComparisonPredicate(column, op,
Bytes.getInt(bound));
case INT64:
case UNIXTIME_MICROS:
diff --git
a/java/kudu-client/src/main/java/org/apache/kudu/client/KeyEncoder.java
b/java/kudu-client/src/main/java/org/apache/kudu/client/KeyEncoder.java
index de268b6..71a71ec 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/client/KeyEncoder.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/client/KeyEncoder.java
@@ -37,6 +37,7 @@ import org.apache.kudu.Schema;
import org.apache.kudu.Type;
import org.apache.kudu.client.PartitionSchema.HashBucketSchema;
import org.apache.kudu.util.ByteVec;
+import org.apache.kudu.util.DateUtil;
import org.apache.kudu.util.DecimalUtil;
import org.apache.kudu.util.Pair;
@@ -327,6 +328,11 @@ class KeyEncoder {
case INT16:
row.addShort(idx, (short) (buf.getShort() ^ Short.MIN_VALUE));
break;
+ case DATE: {
+ int days = buf.getInt() ^ Integer.MIN_VALUE;
+ row.addDate(idx, DateUtil.epochDaysToSqlDate(days));
+ break;
+ }
case INT32:
row.addInt(idx, buf.getInt() ^ Integer.MIN_VALUE);
break;
diff --git
a/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java
b/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java
index 5c9f706..49abfe8 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/client/KuduPredicate.java
@@ -22,7 +22,9 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.sql.Date;
import java.sql.Timestamp;
+import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -44,6 +46,7 @@ import org.apache.kudu.ColumnTypeAttributes;
import org.apache.kudu.Common;
import org.apache.kudu.Schema;
import org.apache.kudu.Type;
+import org.apache.kudu.util.DateUtil;
import org.apache.kudu.util.DecimalUtil;
import org.apache.kudu.util.TimestampUtil;
@@ -166,7 +169,8 @@ public class KuduPredicate {
public static KuduPredicate newComparisonPredicate(ColumnSchema column,
ComparisonOp op,
long value) {
- checkColumn(column, Type.INT8, Type.INT16, Type.INT32, Type.INT64,
Type.UNIXTIME_MICROS);
+ checkColumn(column, Type.INT8, Type.INT16, Type.INT32, Type.INT64,
Type.UNIXTIME_MICROS,
+ Type.DATE);
long minValue = minIntValue(column.getType());
long maxValue = maxIntValue(column.getType());
Preconditions.checkArgument(value <= maxValue && value >= minValue,
@@ -202,6 +206,7 @@ public class KuduPredicate {
bytes = Bytes.fromShort((short) value);
break;
}
+ case DATE:
case INT32: {
bytes = Bytes.fromInt((int) value);
break;
@@ -315,6 +320,20 @@ public class KuduPredicate {
}
/**
+ * Creates a new comparison predicate on a date column.
+ * @param column the column schema
+ * @param op the comparison operation
+ * @param value the value to compare against
+ */
+ public static KuduPredicate newComparisonPredicate(ColumnSchema column,
+ ComparisonOp op,
+ Date value) {
+ checkColumn(column, Type.DATE);
+ int days = DateUtil.sqlDateToEpochDays(value);
+ return newComparisonPredicate(column, op, days);
+ }
+
+ /**
* Creates a new comparison predicate on a float column.
* @param column the column schema
* @param op the comparison operation
@@ -573,7 +592,7 @@ public class KuduPredicate {
vals.add(Bytes.fromShort((Short) value));
}
} else if (t instanceof Integer) {
- checkColumn(column, Type.INT32);
+ checkColumn(column, Type.INT32, Type.DATE);
for (T value : values) {
vals.add(Bytes.fromInt((Integer) value));
}
@@ -974,6 +993,7 @@ public class KuduPredicate {
case INT16:
return Short.compare(Bytes.getShort(a), Bytes.getShort(b));
case INT32:
+ case DATE:
case DECIMAL32:
return Integer.compare(Bytes.getInt(a), Bytes.getInt(b));
case INT64:
@@ -1015,6 +1035,7 @@ public class KuduPredicate {
return m < n && m + 1 == n;
}
case INT32:
+ case DATE:
case DECIMAL32: {
int m = Bytes.getInt(a);
int n = Bytes.getInt(b);
@@ -1153,6 +1174,7 @@ public class KuduPredicate {
case INT16: return Short.toString(Bytes.getShort(value));
case INT32: return Integer.toString(Bytes.getInt(value));
case INT64: return Long.toString(Bytes.getLong(value));
+ case DATE: return DateUtil.epochDaysToDateString(Bytes.getInt(value));
case UNIXTIME_MICROS: return
TimestampUtil.timestampToString(Bytes.getLong(value));
case FLOAT: return Float.toString(Bytes.getFloat(value));
case DOUBLE: return Double.toString(Bytes.getDouble(value));
diff --git
a/java/kudu-client/src/main/java/org/apache/kudu/client/PartialRow.java
b/java/kudu-client/src/main/java/org/apache/kudu/client/PartialRow.java
index c118ed4..37de74f 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/client/PartialRow.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/client/PartialRow.java
@@ -20,6 +20,7 @@ package org.apache.kudu.client;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
@@ -36,6 +37,7 @@ import org.apache.kudu.ColumnSchema;
import org.apache.kudu.ColumnTypeAttributes;
import org.apache.kudu.Schema;
import org.apache.kudu.Type;
+import org.apache.kudu.util.DateUtil;
import org.apache.kudu.util.DecimalUtil;
import org.apache.kudu.util.StringUtil;
import org.apache.kudu.util.TimestampUtil;
@@ -610,6 +612,64 @@ public class PartialRow {
}
/**
+ * Add a sql.Date for the specified column.
+ *
+ * @param columnIndex the column's index in the schema
+ * @param val value to add
+ * @throws IllegalArgumentException if the value doesn't match the column's
type
+ * @throws IllegalStateException if the row was already applied
+ * @throws IndexOutOfBoundsException if the column doesn't exist
+ */
+ public void addDate(int columnIndex, Date val) {
+ checkNotFrozen();
+ checkColumn(schema.getColumnByIndex(columnIndex), Type.DATE);
+ int days = DateUtil.sqlDateToEpochDays(val);
+ Bytes.setInt(rowAlloc, days,
getPositionInRowAllocAndSetBitSet(columnIndex));
+ }
+
+ /**
+ * Add a Date for the specified column.
+ *
+ * @param columnName Name of the column
+ * @param val value to add
+ * @throws IllegalArgumentException if the column doesn't exist
+ * or if the value doesn't match the column's type
+ * @throws IllegalStateException if the row was already applied
+ */
+ public void addDate(String columnName, Date val) {
+ addDate(schema.getColumnIndex(columnName), val);
+ }
+
+ /**
+ * Get the specified column's Date.
+ *
+ * @param columnName name of the column to get data for
+ * @return a Date
+ * @throws IllegalArgumentException if the column doesn't exist,
+ * is null, is unset, or the type doesn't match the column's type
+ */
+ public Date getDate(String columnName) {
+ return getDate(this.schema.getColumnIndex(columnName));
+ }
+
+ /**
+ * Get the specified column's Date.
+ *
+ * @param columnIndex Column index in the schema
+ * @return a Date
+ * @throws IllegalArgumentException if the column is null, is unset,
+ * or if the type doesn't match the column's type
+ * @throws IndexOutOfBoundsException if the column doesn't exist
+ */
+ public Date getDate(int columnIndex) {
+ checkColumnExists(schema.getColumnByIndex(columnIndex));
+ checkColumn(schema.getColumnByIndex(columnIndex), Type.DATE);
+ checkValue(columnIndex);
+ int days = Bytes.getInt(rowAlloc, schema.getColumnOffset(columnIndex));
+ return DateUtil.epochDaysToSqlDate(days);
+ }
+
+ /**
* Add a String for the specified column.
* @param columnIndex the column's index in the schema
* @param val value to add
@@ -1001,6 +1061,7 @@ public class PartialRow {
* Type.VARCHAR -> java.lang.String
* Type.BINARY -> byte[] or java.lang.ByteBuffer
* Type.DECIMAL -> java.math.BigDecimal
+ * Type.DATE -> java.sql.Date
*
* @param columnIndex column index in the schema
* @param val the value to add as an Object
@@ -1051,6 +1112,9 @@ public class PartialRow {
case VARCHAR:
addVarchar(columnIndex, (String) val);
break;
+ case DATE:
+ addDate(columnIndex, (Date) val);
+ break;
case BINARY:
if (val instanceof byte[]) {
addBinary(columnIndex, (byte[]) val);
@@ -1090,6 +1154,7 @@ public class PartialRow {
* Type.VARCHAR -> java.lang.String
* Type.BINARY -> byte[]
* Type.DECIMAL -> java.math.BigDecimal
+ * Type.DATE -> java.sql.Date
*
* @param columnName name of the column in the schema
* @return the column's value as an Object, null if the column value is null
or unset
@@ -1135,6 +1200,7 @@ public class PartialRow {
case INT16: return getShort(columnIndex);
case INT32: return getInt(columnIndex);
case INT64: return getLong(columnIndex);
+ case DATE: return getDate(columnIndex);
case UNIXTIME_MICROS: return getTimestamp(columnIndex);
case FLOAT: return getFloat(columnIndex);
case DOUBLE: return getDouble(columnIndex);
@@ -1374,6 +1440,10 @@ public class PartialRow {
case INT64:
sb.append(Bytes.getLong(rowAlloc, schema.getColumnOffset(idx)));
return;
+ case DATE:
+ sb.append(DateUtil.epochDaysToDateString(
+ Bytes.getInt(rowAlloc, schema.getColumnOffset(idx))));
+ return;
case UNIXTIME_MICROS:
sb.append(TimestampUtil.timestampToString(
Bytes.getLong(rowAlloc, schema.getColumnOffset(idx))));
@@ -1429,6 +1499,9 @@ public class PartialRow {
case INT32:
addInt(index, Integer.MIN_VALUE);
break;
+ case DATE:
+ addDate(index, DateUtil.epochDaysToSqlDate(DateUtil.MIN_DATE_VALUE));
+ break;
case INT64:
case UNIXTIME_MICROS:
addLong(index, Long.MIN_VALUE);
@@ -1472,6 +1545,7 @@ public class PartialRow {
case INT16:
case INT32:
case INT64:
+ case DATE:
case UNIXTIME_MICROS:
case FLOAT:
case DOUBLE:
@@ -1536,6 +1610,14 @@ public class PartialRow {
Bytes.setInt(rowAlloc, existing + 1, offset);
return true;
}
+ case DATE: {
+ int existing = Bytes.getInt(rowAlloc, offset);
+ if (existing == DateUtil.MAX_DATE_VALUE) {
+ return false;
+ }
+ Bytes.setInt(rowAlloc, existing + 1, offset);
+ return true;
+ }
case INT64:
case UNIXTIME_MICROS: {
long existing = Bytes.getLong(rowAlloc, offset);
@@ -1648,6 +1730,7 @@ public class PartialRow {
return a.rowAlloc[offset] == b.rowAlloc[offset];
case INT16:
return Bytes.getShort(a.rowAlloc, offset) ==
Bytes.getShort(b.rowAlloc, offset);
+ case DATE:
case INT32:
return Bytes.getInt(a.rowAlloc, offset) == Bytes.getInt(b.rowAlloc,
offset);
case INT64:
@@ -1725,6 +1808,10 @@ public class PartialRow {
int val = Bytes.getInt(lower.rowAlloc, offset);
return val != Integer.MAX_VALUE && val + 1 ==
Bytes.getInt(upper.rowAlloc, offset);
}
+ case DATE: {
+ int val = Bytes.getInt(lower.rowAlloc, offset);
+ return val != DateUtil.MAX_DATE_VALUE && val + 1 ==
Bytes.getInt(upper.rowAlloc, offset);
+ }
case INT64:
case UNIXTIME_MICROS: {
long val = Bytes.getLong(lower.rowAlloc, offset);
diff --git
a/java/kudu-client/src/main/java/org/apache/kudu/client/ProtobufHelper.java
b/java/kudu-client/src/main/java/org/apache/kudu/client/ProtobufHelper.java
index e4dc71b..0d58bbc 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/client/ProtobufHelper.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/client/ProtobufHelper.java
@@ -22,6 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.sql.Date;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@@ -37,6 +38,7 @@ import org.apache.kudu.ColumnTypeAttributes;
import org.apache.kudu.Common;
import org.apache.kudu.Schema;
import org.apache.kudu.Type;
+import org.apache.kudu.util.DateUtil;
import org.apache.kudu.util.DecimalUtil;
@InterfaceAudience.Private
@@ -271,6 +273,8 @@ public class ProtobufHelper {
return new byte[] {(Byte) value};
case INT16:
return Bytes.fromShort((Short) value);
+ case DATE:
+ return Bytes.fromInt(DateUtil.sqlDateToEpochDays((Date) value));
case INT32:
return Bytes.fromInt((Integer) value);
case INT64:
@@ -304,6 +308,8 @@ public class ProtobufHelper {
return buf.get();
case INT16:
return buf.getShort();
+ case DATE:
+ return DateUtil.epochDaysToSqlDate(buf.getInt());
case INT32:
return buf.getInt();
case INT64:
diff --git
a/java/kudu-client/src/main/java/org/apache/kudu/client/RowResult.java
b/java/kudu-client/src/main/java/org/apache/kudu/client/RowResult.java
index e793092..6470e9d 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/client/RowResult.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/client/RowResult.java
@@ -19,6 +19,7 @@ package org.apache.kudu.client;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
+import java.sql.Date;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.BitSet;
@@ -30,6 +31,7 @@ import org.apache.kudu.ColumnSchema;
import org.apache.kudu.ColumnTypeAttributes;
import org.apache.kudu.Schema;
import org.apache.kudu.Type;
+import org.apache.kudu.util.DateUtil;
import org.apache.kudu.util.Slice;
import org.apache.kudu.util.TimestampUtil;
@@ -132,7 +134,7 @@ public class RowResult {
public int getInt(int columnIndex) {
checkValidColumn(columnIndex);
checkNull(columnIndex);
- checkType(columnIndex, Type.INT32);
+ checkType(columnIndex, Type.INT32, Type.DATE);
return Bytes.getInt(this.rowData.getRawArray(),
this.rowData.getRawOffset() +
getCurrentRowDataOffsetForColumn(columnIndex));
}
@@ -370,6 +372,35 @@ public class RowResult {
}
/**
+ * Get the specified column's Date.
+ *
+ * @param columnName name of the column to get data for
+ * @return a Date
+ * @throws IllegalArgumentException if the column doesn't exist,
+ * is null, is unset, or the type doesn't match the column's type
+ */
+ public Date getDate(String columnName) {
+ return getDate(this.schema.getColumnIndex(columnName));
+ }
+
+ /**
+ * Get the specified column's Date.
+ *
+ * @param columnIndex Column index in the schema
+ * @return a Date
+ * @throws IllegalArgumentException if the column is null, is unset,
+ * or if the type doesn't match the column's type
+ * @throws IndexOutOfBoundsException if the column doesn't exist
+ */
+ public Date getDate(int columnIndex) {
+ checkValidColumn(columnIndex);
+ checkNull(columnIndex);
+ checkType(columnIndex, Type.DATE);
+ int days = getInt(columnIndex);
+ return DateUtil.epochDaysToSqlDate(days);
+ }
+
+ /**
* Get the schema used for this scanner's column projection.
* @return a column projection as a schema.
*/
@@ -602,6 +633,7 @@ public class RowResult {
* Type.STRING -> java.lang.String
* Type.BINARY -> byte[]
* Type.DECIMAL -> java.math.BigDecimal
+ * Type.Date -> java.sql.Date
*
* @param columnIndex Column index in the schema
* @return the column's value as an Object, null if the value is null
@@ -619,6 +651,7 @@ public class RowResult {
case INT16: return getShort(columnIndex);
case INT32: return getInt(columnIndex);
case INT64: return getLong(columnIndex);
+ case DATE: return getDate(columnIndex);
case UNIXTIME_MICROS: return getTimestamp(columnIndex);
case FLOAT: return getFloat(columnIndex);
case DOUBLE: return getDouble(columnIndex);
@@ -752,6 +785,9 @@ public class RowResult {
case INT64:
buf.append(getLong(i));
break;
+ case DATE:
+ buf.append(DateUtil.epochDaysToDateString(getInt(i)));
+ break;
case UNIXTIME_MICROS: {
buf.append(TimestampUtil.timestampToString(getTimestamp(i)));
} break;
diff --git
a/java/kudu-client/src/main/java/org/apache/kudu/util/DataGenerator.java
b/java/kudu-client/src/main/java/org/apache/kudu/util/DataGenerator.java
index 0e281bc..07a11a2 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/util/DataGenerator.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/util/DataGenerator.java
@@ -19,6 +19,7 @@ package org.apache.kudu.util;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.sql.Date;
import java.util.Base64;
import java.util.List;
import java.util.Random;
@@ -103,6 +104,9 @@ public class DataGenerator {
case INT32:
row.addInt(i, random.nextInt());
break;
+ case DATE:
+ row.addDate(i, randomDate(random));
+ break;
case INT64:
case UNIXTIME_MICROS:
row.addLong(i, random.nextLong());
@@ -133,6 +137,16 @@ public class DataGenerator {
}
/**
+ * Utility method to return a random integer value which can be converted
into
+ * correct Kudu Date value
+ */
+ public static Date randomDate(Random random) {
+ final int bound = DateUtil.MAX_DATE_VALUE - DateUtil.MIN_DATE_VALUE + 1;
+ int days = random.nextInt(bound) + DateUtil.MIN_DATE_VALUE;
+ return DateUtil.epochDaysToSqlDate(days);
+ }
+
+ /**
* Utility method to return a random decimal value.
*/
public static BigDecimal randomDecimal(ColumnTypeAttributes attributes,
Random random) {
diff --git a/java/kudu-client/src/main/java/org/apache/kudu/util/DateUtil.java
b/java/kudu-client/src/main/java/org/apache/kudu/util/DateUtil.java
new file mode 100644
index 0000000..10bb20d
--- /dev/null
+++ b/java/kudu-client/src/main/java/org/apache/kudu/util/DateUtil.java
@@ -0,0 +1,77 @@
+// 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.kudu.util;
+
+import java.sql.Date;
+import java.time.LocalDate;
+
+import org.apache.yetus.audience.InterfaceAudience;
+
[email protected]
+public class DateUtil {
+ public static final int MIN_DATE_VALUE =
+ (int)LocalDate.parse("0001-01-01").toEpochDay(); // -719162
+ public static final int MAX_DATE_VALUE =
+ (int)LocalDate.parse("9999-12-31").toEpochDay(); // 2932896
+
+ /**
+ * Check whether the date is within the range '0001-01-01':'9999-12-31'
+ *
+ * @param the number days since the Unix epoch
+ */
+ public static void checkDateWithinRange(long days) {
+ if (days < MIN_DATE_VALUE || days > MAX_DATE_VALUE) {
+ throw new IllegalArgumentException(
+ "Date value <" + days + ">} is out of range
'0001-01-01':'9999-12-31'");
+ }
+ }
+
+ /**
+ * Converts a {@link java.sql.Date} to the number of days since the Unix
epoch
+ * (1970-01-01T00:00:00Z).
+ *
+ * @param date the date to convert to days
+ * @return the number days since the Unix epoch
+ */
+ public static int sqlDateToEpochDays(Date date) {
+ long days = date.toLocalDate().toEpochDay();
+ checkDateWithinRange(days);
+ return (int)days;
+ }
+
+ /**
+ * Converts a number of days since the Unix epoch to a {@link java.sql.Date}.
+ *
+ * @param the number of days since the Unix epoch
+ * @return the corresponding Date
+ */
+ public static Date epochDaysToSqlDate(int days) {
+ checkDateWithinRange(days);
+ return Date.valueOf(LocalDate.ofEpochDay(days));
+ }
+
+ /**
+ * Transforms a number of days since the Unix epoch into a string according
the ISO-8601 format.
+ *
+ * @param the number of days since the Unix epoch
+ * @return a string, in the format: YYYY-MM-DD
+ */
+ public static String epochDaysToDateString(int days) {
+ return LocalDate.ofEpochDay(days).toString();
+ }
+}
diff --git
a/java/kudu-client/src/main/java/org/apache/kudu/util/SchemaGenerator.java
b/java/kudu-client/src/main/java/org/apache/kudu/util/SchemaGenerator.java
index 78b7734..2da7bd2 100644
--- a/java/kudu-client/src/main/java/org/apache/kudu/util/SchemaGenerator.java
+++ b/java/kudu-client/src/main/java/org/apache/kudu/util/SchemaGenerator.java
@@ -18,6 +18,7 @@
package org.apache.kudu.util;
import static org.apache.kudu.util.DataGenerator.randomBinary;
+import static org.apache.kudu.util.DataGenerator.randomDate;
import static org.apache.kudu.util.DataGenerator.randomDecimal;
import static org.apache.kudu.util.DataGenerator.randomString;
@@ -149,6 +150,9 @@ public class SchemaGenerator {
case INT32:
builder.defaultValue(random.nextInt());
break;
+ case DATE:
+ builder.defaultValue(randomDate(random));
+ break;
case INT64:
case UNIXTIME_MICROS:
builder.defaultValue(random.nextLong());
@@ -204,6 +208,7 @@ public class SchemaGenerator {
case INT16:
case INT32:
case INT64:
+ case DATE:
case UNIXTIME_MICROS:
validEncodings.retainAll(Arrays.asList(
Encoding.AUTO_ENCODING,
diff --git
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestKeyEncoding.java
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKeyEncoding.java
index d7708d8..ade63ed 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestKeyEncoding.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKeyEncoding.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.math.BigDecimal;
+import java.sql.Date;
import java.util.ArrayList;
import java.util.List;
@@ -396,6 +397,7 @@ public class TestKeyEncoding {
.typeAttributes(DecimalUtil.typeAttributes(DecimalUtil.MAX_DECIMAL128_PRECISION,
0)),
new ColumnSchemaBuilder("varchar", Type.VARCHAR).key(true)
.typeAttributes(CharUtil.typeAttributes(10)),
+ new ColumnSchemaBuilder("date", Type.DATE).key(true),
new ColumnSchemaBuilder("bool", Type.BOOL), // not primary key
type
new ColumnSchemaBuilder("float", Type.FLOAT), // not primary key
type
new ColumnSchemaBuilder("double", Type.DOUBLE)); // not primary key
type
@@ -417,9 +419,10 @@ public class TestKeyEncoding {
row.addDecimal(8, BigDecimal.valueOf(DecimalUtil.MAX_UNSCALED_DECIMAL64));
row.addDecimal(9, new BigDecimal(DecimalUtil.MAX_UNSCALED_DECIMAL128));
row.addVarchar(10, "varchar bar");
- row.addBoolean(11, true);
- row.addFloat(12, 7.8f);
- row.addDouble(13, 9.9);
+ row.addDate(11, new Date(0));
+ row.addBoolean(12, true);
+ row.addFloat(13, 7.8f);
+ row.addDouble(14, 9.9);
session.apply(insert);
session.close();
@@ -442,9 +445,10 @@ public class TestKeyEncoding {
assertTrue(new BigDecimal(DecimalUtil.MAX_UNSCALED_DECIMAL128)
.compareTo(rr.getDecimal(9)) == 0);
assertEquals("varchar ba", rr.getVarchar(10));
- assertTrue(rr.getBoolean(11));
- assertEquals(7.8f, rr.getFloat(12), .001f);
- assertEquals(9.9, rr.getDouble(13), .001);
+ assertEquals(0, rr.getInt(11));
+ assertTrue(rr.getBoolean(12));
+ assertEquals(7.8f, rr.getFloat(13), .001f);
+ assertEquals(9.9, rr.getDouble(14), .001);
}
}
}
diff --git
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java
index 1792642..1fc7d99 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestKuduClient.java
@@ -27,6 +27,7 @@ import static
org.apache.kudu.test.ClientTestUtil.createBasicSchemaInsert;
import static org.apache.kudu.test.ClientTestUtil.createManyStringsSchema;
import static org.apache.kudu.test.ClientTestUtil.createManyVarcharsSchema;
import static
org.apache.kudu.test.ClientTestUtil.createSchemaWithBinaryColumns;
+import static org.apache.kudu.test.ClientTestUtil.createSchemaWithDateColumns;
import static
org.apache.kudu.test.ClientTestUtil.createSchemaWithDecimalColumns;
import static
org.apache.kudu.test.ClientTestUtil.createSchemaWithTimestampColumns;
import static org.apache.kudu.test.ClientTestUtil.getBasicCreateTableOptions;
@@ -46,6 +47,8 @@ import java.io.Closeable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
+import java.sql.Date;
+import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -74,6 +77,7 @@ import org.apache.kudu.test.KuduTestHarness;
import org.apache.kudu.test.KuduTestHarness.LocationConfig;
import org.apache.kudu.test.KuduTestHarness.TabletServerConfig;
import org.apache.kudu.test.RandomUtils;
+import org.apache.kudu.util.DateUtil;
import org.apache.kudu.util.DecimalUtil;
import org.apache.kudu.util.TimestampUtil;
@@ -649,6 +653,49 @@ public class TestKuduClient {
}
/**
+ * Test inserting and retrieving date columns.
+ */
+ @Test(timeout = 100000)
+ public void testDateColumns() throws Exception {
+ Schema schema = createSchemaWithDateColumns();
+ client.createTable(TABLE_NAME, schema, getBasicCreateTableOptions());
+
+ List<Integer> dates = new ArrayList<>();
+
+ KuduSession session = client.newSession();
+ KuduTable table = client.openTable(TABLE_NAME);
+ for (int i = 0; i < 100; i++) {
+ Insert insert = table.newInsert();
+ PartialRow row = insert.getRow();
+ dates.add(i);
+ Date date = DateUtil.epochDaysToSqlDate(i);
+ row.addDate("key", date);
+ if (i % 2 == 1) {
+ row.addDate("c1", date);
+ }
+ session.apply(insert);
+ if (i % 50 == 0) {
+ session.flush();
+ }
+ }
+ session.flush();
+
+ List<String> rowStrings = scanTableToStrings(table);
+ assertEquals(100, rowStrings.size());
+ for (int i = 0; i < rowStrings.size(); i++) {
+ String sdate = DateUtil.epochDaysToDateString(dates.get(i));
+ StringBuilder expectedRow = new StringBuilder();
+ expectedRow.append(String.format("DATE key=%s, DATE c1=", sdate));
+ if (i % 2 == 1) {
+ expectedRow.append(sdate);
+ } else {
+ expectedRow.append("NULL");
+ }
+ assertEquals(expectedRow.toString(), rowStrings.get(i));
+ }
+ }
+
+ /**
* Test inserting and retrieving decimal columns.
*/
@Test(timeout = 100000)
diff --git
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestOperation.java
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestOperation.java
index 7280c30..20e7640 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestOperation.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestOperation.java
@@ -35,6 +35,7 @@ import org.apache.kudu.WireProtocol.RowOperationsPB;
import org.apache.kudu.client.Operation.ChangeType;
import org.apache.kudu.test.junit.RetryRule;
import org.apache.kudu.tserver.Tserver.WriteRequestPBOrBuilder;
+import org.apache.kudu.util.DateUtil;
/**
* Unit tests for Operation
@@ -139,6 +140,7 @@ public class TestOperation {
columns.add(new ColumnSchema.ColumnSchemaBuilder("c4",
Type.UNIXTIME_MICROS).key(true).build());
columns.add(new ColumnSchema.ColumnSchemaBuilder("c5",
Type.STRING).key(true).build());
columns.add(new ColumnSchema.ColumnSchemaBuilder("c6",
Type.BINARY).key(true).build());
+ columns.add(new ColumnSchema.ColumnSchemaBuilder("c7",
Type.DATE).key(true).build());
return new Schema(columns);
}
@@ -155,10 +157,11 @@ public class TestOperation {
row.addLong("c4", 5);
row.addString("c5", "c5_val");
row.addBinary("c6", Bytes.fromString("c6_val"));
+ row.addDate("c7", DateUtil.epochDaysToSqlDate(0));
assertEquals("(int8 c0=1, int16 c1=2, int32 c2=3, int64 c3=4, " +
"unixtime_micros c4=1970-01-01T00:00:00.000005Z, string
c5=\"c5_val\", " +
- "binary c6=\"c6_val\")",
+ "binary c6=\"c6_val\", date c7=1970-01-01)",
insert.getRow().stringifyRowKey());
// Test an incomplete row key.
diff --git
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartialRow.java
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartialRow.java
index a7c5d53..417f096 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartialRow.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestPartialRow.java
@@ -28,7 +28,9 @@ import static org.junit.Assert.fail;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
+import java.sql.Date;
import java.sql.Timestamp;
+import java.time.LocalDate;
import org.junit.Rule;
import org.junit.Test;
@@ -37,6 +39,7 @@ import org.apache.kudu.ColumnSchema;
import org.apache.kudu.Schema;
import org.apache.kudu.Type;
import org.apache.kudu.test.junit.RetryRule;
+import org.apache.kudu.util.DateUtil;
public class TestPartialRow {
@@ -52,6 +55,7 @@ public class TestPartialRow {
assertEquals(44, partialRow.getInt("int32"));
assertEquals(45, partialRow.getLong("int64"));
assertEquals(new Timestamp(1234567890),
partialRow.getTimestamp("timestamp"));
+ assertEquals(Date.valueOf(LocalDate.ofEpochDay(0)),
partialRow.getDate("date"));
assertEquals(52.35F, partialRow.getFloat("float"), 0.0f);
assertEquals(53.35, partialRow.getDouble("double"), 0.0);
assertEquals("fun with ütf\0", partialRow.getString("string"));
@@ -84,6 +88,8 @@ public class TestPartialRow {
assertEquals((long) 45, partialRow.getObject("int64"));
assertTrue(partialRow.getObject("timestamp") instanceof Timestamp);
assertEquals(new Timestamp(1234567890), partialRow.getObject("timestamp"));
+ assertTrue(partialRow.getObject("date") instanceof Date);
+ assertEquals(Date.valueOf(LocalDate.ofEpochDay(0)),
partialRow.getObject("date"));
assertTrue(partialRow.getObject("float") instanceof Float);
assertEquals(52.35F, (float) partialRow.getObject("float"), 0.0f);
assertTrue(partialRow.getObject("double") instanceof Double);
@@ -108,7 +114,7 @@ public class TestPartialRow {
public void testAddObject() {
Schema schema = getSchemaWithAllTypes();
// Ensure we aren't missing any types
- assertEquals(14, schema.getColumnCount());
+ assertEquals(15, schema.getColumnCount());
PartialRow row = schema.newPartialRow();
row.addObject("int8", (byte) 42);
@@ -116,6 +122,7 @@ public class TestPartialRow {
row.addObject("int32", 44);
row.addObject("int64", 45L);
row.addObject("timestamp", new Timestamp(1234567890));
+ row.addObject("date", Date.valueOf(LocalDate.ofEpochDay(0)));
row.addObject("bool", true);
row.addObject("float", 52.35F);
row.addObject("double", 53.35);
@@ -310,6 +317,13 @@ public class TestPartialRow {
assertEquals("22.200", decimal.toString());
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testAddDateOutOfRange() {
+ PartialRow partialRow = getPartialRowWithAllTypes();
+ Date d = Date.valueOf(LocalDate.of(10000, 1, 1));
+ partialRow.addDate("date", d);
+ }
+
@Test
public void testToString() {
Schema schema = getSchemaWithAllTypes();
@@ -350,8 +364,12 @@ public class TestPartialRow {
assertEquals("(int8 int8=42, int32 int32=42, double double=52.35, " +
"string string=\"fun with ütf\\0\", binary binary-bytebuffer=[2, 3,
4], " +
"decimal(5, 3) decimal=12.345, varchar(10) varchar=\"árvíztűrő \")",
-
row.toString());
+
+ PartialRow row2 = schema.newPartialRow();
+ assertEquals("()", row2.toString());
+ row2.addDate("date", Date.valueOf(LocalDate.ofEpochDay(0)));
+ assertEquals("(date date=1970-01-01)", row2.toString());
}
@Test
@@ -432,6 +450,14 @@ public class TestPartialRow {
partialRow.addVarchar(varcharIndex, "hello");
assertTrue(partialRow.incrementColumn(varcharIndex));
assertEquals("hello\0", partialRow.getVarchar(varcharIndex));
+
+ // Date
+ int dateIndex = getColumnIndex(partialRow, "date");
+ partialRow.addDate(dateIndex,
DateUtil.epochDaysToSqlDate(DateUtil.MAX_DATE_VALUE - 1));
+ assertTrue(partialRow.incrementColumn(dateIndex));
+ Date maxDate = DateUtil.epochDaysToSqlDate(DateUtil.MAX_DATE_VALUE);
+ assertEquals(maxDate, partialRow.getDate(dateIndex));
+ assertFalse(partialRow.incrementColumn(dateIndex));
}
@Test
@@ -446,6 +472,7 @@ public class TestPartialRow {
assertEquals(Integer.MIN_VALUE, partialRow.getInt("int32"));
assertEquals(Long.MIN_VALUE, partialRow.getLong("int64"));
assertEquals(Long.MIN_VALUE, partialRow.getLong("timestamp"));
+ assertEquals(DateUtil.epochDaysToSqlDate(DateUtil.MIN_DATE_VALUE),
partialRow.getDate("date"));
assertEquals(-Float.MAX_VALUE, partialRow.getFloat("float"), 0.0f);
assertEquals(-Double.MAX_VALUE, partialRow.getDouble("double"), 0.0);
assertEquals("", partialRow.getString("string"));
@@ -474,6 +501,7 @@ public class TestPartialRow {
case INT16: return partialRow.getShort(columnName);
case INT32: return partialRow.getInt(columnName);
case INT64: return partialRow.getLong(columnName);
+ case DATE: return partialRow.getDate(columnName);
case UNIXTIME_MICROS: return partialRow.getTimestamp(columnName);
case VARCHAR: return partialRow.getVarchar(columnName);
case STRING: return partialRow.getString(columnName);
@@ -496,6 +524,7 @@ public class TestPartialRow {
case INT16: return partialRow.getShort(columnIndex);
case INT32: return partialRow.getInt(columnIndex);
case INT64: return partialRow.getLong(columnIndex);
+ case DATE: return partialRow.getDate(columnIndex);
case UNIXTIME_MICROS: return partialRow.getTimestamp(columnIndex);
case VARCHAR: return partialRow.getVarchar(columnIndex);
case STRING: return partialRow.getString(columnIndex);
@@ -547,6 +576,9 @@ public class TestPartialRow {
case DECIMAL:
partialRow.addDecimal(columnName, BigDecimal.valueOf(12345, 3));
break;
+ case DATE:
+ partialRow.addDate(columnName, new Date(0));
+ break;
default:
throw new UnsupportedOperationException();
}
@@ -590,6 +622,9 @@ public class TestPartialRow {
case DECIMAL:
partialRow.addDecimal(columnIndex, BigDecimal.valueOf(12345, 3));
break;
+ case DATE:
+ partialRow.addDate(columnIndex, new Date(0));
+ break;
default:
throw new UnsupportedOperationException();
}
diff --git
a/java/kudu-client/src/test/java/org/apache/kudu/client/TestRowResult.java
b/java/kudu-client/src/test/java/org/apache/kudu/client/TestRowResult.java
index 05a5106..6aa8910 100644
--- a/java/kudu-client/src/test/java/org/apache/kudu/client/TestRowResult.java
+++ b/java/kudu-client/src/test/java/org/apache/kudu/client/TestRowResult.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
+import java.sql.Date;
import java.sql.Timestamp;
import org.junit.Before;
@@ -77,6 +78,7 @@ public class TestRowResult {
row.addTimestamp(11, new Timestamp(11));
row.addDecimal(12, BigDecimal.valueOf(12345, 3));
row.addVarchar(13, "varcharval");
+ row.addDate(14, new Date(0));
KuduClient client = harness.getClient();
KuduSession session = client.newSession();
@@ -155,6 +157,10 @@ public class TestRowResult {
assertEquals("varcharval",
rr.getVarchar(allTypesSchema.getColumnByIndex(13).getName()));
+ assertEquals(new Date(0), rr.getDate(14));
+ assertEquals(new Date(0), rr.getObject(14));
+ assertEquals(new Date(0),
rr.getDate(allTypesSchema.getColumnByIndex(14).getName()));
+
// We test with the column name once since it's the same method for all
types, unlike above.
assertEquals(Type.INT8,
rr.getColumnType(allTypesSchema.getColumnByIndex(0).getName()));
assertEquals(Type.INT8, rr.getColumnType(0));
@@ -169,6 +175,7 @@ public class TestRowResult {
assertEquals(Type.UNIXTIME_MICROS, rr.getColumnType(11));
assertEquals(Type.DECIMAL, rr.getColumnType(12));
assertEquals(Type.VARCHAR, rr.getColumnType(13));
+ assertEquals(Type.DATE, rr.getColumnType(14));
}
}
}
diff --git
a/java/kudu-client/src/test/java/org/apache/kudu/util/TestDateUtil.java
b/java/kudu-client/src/test/java/org/apache/kudu/util/TestDateUtil.java
new file mode 100644
index 0000000..f0587d8
--- /dev/null
+++ b/java/kudu-client/src/test/java/org/apache/kudu/util/TestDateUtil.java
@@ -0,0 +1,78 @@
+// 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.kudu.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.sql.Date;
+import java.time.LocalDate;
+
+import org.junit.Test;
+
+public class TestDateUtil {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDateOutOfRange() {
+ DateUtil.checkDateWithinRange(LocalDate.of(10000, 1, 1).toEpochDay());
+ }
+
+ @Test
+ public void testSqlDateToEpochDays() {
+ Date b = Date.valueOf(LocalDate.of(1, 1, 1));
+ Date e = Date.valueOf(LocalDate.of(9999, 12, 31));
+ assertEquals(DateUtil.sqlDateToEpochDays(b), DateUtil.MIN_DATE_VALUE);
+ assertEquals(DateUtil.sqlDateToEpochDays(e), DateUtil.MAX_DATE_VALUE);
+ }
+
+ @Test
+ public void testEpochDaysToSqlDate() {
+ Date b = Date.valueOf(LocalDate.of(1, 1, 1));
+ Date e = Date.valueOf(LocalDate.of(9999, 12, 31));
+ assertEquals(DateUtil.epochDaysToSqlDate(DateUtil.MIN_DATE_VALUE), b);
+ assertEquals(DateUtil.epochDaysToSqlDate(DateUtil.MAX_DATE_VALUE), e);
+ }
+
+ @Test
+ public void testEpochDaysToDateString() {
+ assertEquals(DateUtil.epochDaysToDateString(DateUtil.MIN_DATE_VALUE),
"0001-01-01");
+ assertEquals(DateUtil.epochDaysToDateString(DateUtil.MAX_DATE_VALUE),
"9999-12-31");
+ assertEquals(DateUtil.epochDaysToDateString(0), "1970-01-01");
+ assertEquals(DateUtil.epochDaysToDateString(-10000), "1942-08-16");
+ assertEquals(DateUtil.epochDaysToDateString(10000), "1997-05-19");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMinInt() {
+ DateUtil.checkDateWithinRange(Integer.MIN_VALUE);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMaxInt() {
+ DateUtil.checkDateWithinRange(Integer.MAX_VALUE);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBeforeMinDate() {
+ DateUtil.checkDateWithinRange(DateUtil.MIN_DATE_VALUE - 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testAfterMaxDate() {
+ DateUtil.checkDateWithinRange(DateUtil.MAX_DATE_VALUE + 1);
+ }
+}
diff --git
a/java/kudu-spark-tools/src/main/scala/org/apache/kudu/spark/tools/DistributedDataGenerator.scala
b/java/kudu-spark-tools/src/main/scala/org/apache/kudu/spark/tools/DistributedDataGenerator.scala
index 6f8a664..294a6a7 100644
---
a/java/kudu-spark-tools/src/main/scala/org/apache/kudu/spark/tools/DistributedDataGenerator.scala
+++
b/java/kudu-spark-tools/src/main/scala/org/apache/kudu/spark/tools/DistributedDataGenerator.scala
@@ -31,6 +31,7 @@ import org.apache.kudu.spark.kudu.RowConverter
import org.apache.kudu.spark.kudu.SparkUtil
import org.apache.kudu.spark.tools.DistributedDataGeneratorOptions._
import org.apache.kudu.util.DataGenerator
+import org.apache.kudu.util.DateUtil
import org.apache.spark.sql.Row
import org.apache.spark.sql.SparkSession
import org.apache.spark.util.LongAccumulator
@@ -213,6 +214,8 @@ private class GeneratedRowIterator(
row.addLong(i, value)
case Type.UNIXTIME_MICROS =>
row.addLong(i, value)
+ case Type.DATE =>
+ row.addDate(i, DateUtil.epochDaysToSqlDate(value.toInt))
case Type.FLOAT =>
row.addFloat(i, value.toFloat)
case Type.DOUBLE =>
diff --git
a/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/RowConverter.scala
b/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/RowConverter.scala
index 03a5f74..f4c52c8 100644
---
a/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/RowConverter.scala
+++
b/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/RowConverter.scala
@@ -93,6 +93,8 @@ class RowConverter(kuduSchema: Schema, schema: StructType,
ignoreNull: Boolean)
partialRow.addDouble(kuduIdx, row.getDouble(sparkIdx))
case DataTypes.TimestampType =>
partialRow.addTimestamp(kuduIdx, row.getTimestamp(sparkIdx))
+ case DataTypes.DateType =>
+ partialRow.addDate(kuduIdx, row.getDate(sparkIdx))
case DecimalType() =>
partialRow.addDecimal(kuduIdx, row.getDecimal(sparkIdx))
case t =>
diff --git
a/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/SparkUtil.scala
b/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/SparkUtil.scala
index bac9193..bf34c90 100644
--- a/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/SparkUtil.scala
+++ b/java/kudu-spark/src/main/scala/org/apache/kudu/spark/kudu/SparkUtil.scala
@@ -50,6 +50,7 @@ object SparkUtil {
case Type.INT32 => IntegerType
case Type.INT64 => LongType
case Type.UNIXTIME_MICROS => TimestampType
+ case Type.DATE => DateType
case Type.FLOAT => FloatType
case Type.DOUBLE => DoubleType
case Type.VARCHAR => StringType
@@ -71,6 +72,7 @@ object SparkUtil {
case DataTypes.BooleanType => Type.BOOL
case DataTypes.StringType => Type.STRING
case DataTypes.TimestampType => Type.UNIXTIME_MICROS
+ case DataTypes.DateType => Type.DATE
case DataTypes.ByteType => Type.INT8
case DataTypes.ShortType => Type.INT16
case DataTypes.IntegerType => Type.INT32
diff --git
a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/DefaultSourceTest.scala
b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/DefaultSourceTest.scala
index 43cb10b..ebd11b8 100644
---
a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/DefaultSourceTest.scala
+++
b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/DefaultSourceTest.scala
@@ -567,7 +567,7 @@ class DefaultSourceTest extends KuduTestSuite with Matchers
{
))
val dfDefaultSchema =
sqlContext.read.options(kuduOptions).format("kudu").load
- assertEquals(15, dfDefaultSchema.schema.fields.length)
+ assertEquals(16, dfDefaultSchema.schema.fields.length)
val dfWithUserSchema =
sqlContext.read.options(kuduOptions).schema(userSchema).format("kudu").load
diff --git
a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/KuduContextTest.scala
b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/KuduContextTest.scala
index aa5c5e8..61eb3c0 100644
---
a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/KuduContextTest.scala
+++
b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/KuduContextTest.scala
@@ -23,8 +23,10 @@ import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.math.BigDecimal
import java.nio.charset.StandardCharsets.UTF_8
+import java.sql.Date
import java.sql.Timestamp
+import org.apache.kudu.util.DateUtil
import org.apache.kudu.util.TimestampUtil
import org.apache.spark.sql.functions.decode
import org.junit.Test
@@ -85,7 +87,8 @@ class KuduContextTest extends KuduTestSuite with Matchers {
"c11_decimal32",
"c12_decimal64",
"c13_decimal128",
- "c14_varchar"
+ "c14_varchar",
+ "c15_date"
)
)
.map(r => r.toSeq)
@@ -110,6 +113,7 @@ class KuduContextTest extends KuduTestSuite with Matchers {
assert(r.apply(12).asInstanceOf[BigDecimal] ==
BigDecimal.valueOf(rows.apply(index)._2))
assert(r.apply(13).asInstanceOf[BigDecimal] ==
BigDecimal.valueOf(rows.apply(index)._2))
assert(r.apply(14).asInstanceOf[String] == rows.apply(index)._3)
+ assert(r.apply(15).asInstanceOf[Date] ==
DateUtil.epochDaysToSqlDate(rows.apply(index)._2))
})
}
diff --git
a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/KuduTestSuite.scala
b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/KuduTestSuite.scala
index b45ef0b..d775977 100644
---
a/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/KuduTestSuite.scala
+++
b/java/kudu-spark/src/test/scala/org/apache/kudu/spark/kudu/KuduTestSuite.scala
@@ -33,6 +33,7 @@ import org.apache.kudu.Schema
import org.apache.kudu.Type
import org.apache.kudu.test.KuduTestHarness
import org.apache.kudu.util.CharUtil
+import org.apache.kudu.util.DateUtil
import org.apache.kudu.util.DecimalUtil
import org.apache.spark.sql.execution.datasources.LogicalRelation
import org.apache.spark.sql.DataFrame
@@ -91,7 +92,8 @@ trait KuduTestSuite extends JUnitSuite {
new ColumnSchemaBuilder("c14_varchar", Type.VARCHAR)
.typeAttributes(CharUtil.typeAttributes(CharUtil.MAX_VARCHAR_LENGTH))
.nullable(true)
- .build()
+ .build(),
+ new ColumnSchemaBuilder("c15_date", Type.DATE).build()
)
new Schema(columns.asJava)
}
@@ -184,6 +186,7 @@ trait KuduTestSuite extends JUnitSuite {
row.addDecimal(11, BigDecimal.valueOf(i))
row.addDecimal(12, BigDecimal.valueOf(i))
row.addDecimal(13, BigDecimal.valueOf(i))
+ row.addDate(15, DateUtil.epochDaysToSqlDate(i))
// Sprinkling some nulls so that queries see them.
val s = if (i % 2 == 0) {
@@ -226,6 +229,7 @@ trait KuduTestSuite extends JUnitSuite {
row.addDecimal(12, BigDecimal.valueOf(i))
row.addDecimal(13, BigDecimal.valueOf(i))
row.addVarchar(14, i.toString)
+ row.addDate(15, DateUtil.epochDaysToSqlDate(i))
// Sprinkling some nulls so that queries see them.
val s = if (i % 2 == 0) {
diff --git
a/java/kudu-test-utils/src/main/java/org/apache/kudu/test/ClientTestUtil.java
b/java/kudu-test-utils/src/main/java/org/apache/kudu/test/ClientTestUtil.java
index e57dbf8..1f21dcc 100644
---
a/java/kudu-test-utils/src/main/java/org/apache/kudu/test/ClientTestUtil.java
+++
b/java/kudu-test-utils/src/main/java/org/apache/kudu/test/ClientTestUtil.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
+import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
@@ -220,7 +221,8 @@ public abstract class ClientTestUtil {
new ColumnSchema.ColumnSchemaBuilder("decimal", Type.DECIMAL)
.typeAttributes(DecimalUtil.typeAttributes(5, 3)).build(),
new ColumnSchema.ColumnSchemaBuilder("varchar", Type.VARCHAR)
- .typeAttributes(CharUtil.typeAttributes(10)).build());
+ .typeAttributes(CharUtil.typeAttributes(10)).build(),
+ new ColumnSchema.ColumnSchemaBuilder("date", Type.DATE).build());
return new Schema(columns);
}
@@ -228,7 +230,7 @@ public abstract class ClientTestUtil {
public static PartialRow getPartialRowWithAllTypes() {
Schema schema = getSchemaWithAllTypes();
// Ensure we aren't missing any types
- assertEquals(14, schema.getColumnCount());
+ assertEquals(15, schema.getColumnCount());
PartialRow row = schema.newPartialRow();
row.addByte("int8", (byte) 42);
@@ -236,6 +238,7 @@ public abstract class ClientTestUtil {
row.addInt("int32", 44);
row.addLong("int64", 45);
row.addTimestamp("timestamp", new Timestamp(1234567890));
+ row.addDate("date", new Date(0));
row.addBoolean("bool", true);
row.addFloat("float", 52.35F);
row.addDouble("double", 53.35);
@@ -465,6 +468,13 @@ public abstract class ClientTestUtil {
return new Schema(columns);
}
+ public static Schema createSchemaWithDateColumns() {
+ ArrayList<ColumnSchema> columns = new ArrayList<ColumnSchema>();
+ columns.add(new ColumnSchema.ColumnSchemaBuilder("key",
Type.DATE).key(true).build());
+ columns.add(new ColumnSchema.ColumnSchemaBuilder("c1",
Type.DATE).nullable(true).build());
+ return new Schema(columns);
+ }
+
public static Schema createSchemaWithDecimalColumns() {
ArrayList<ColumnSchema> columns = new ArrayList<>();
columns.add(new ColumnSchema.ColumnSchemaBuilder("key",
Type.DECIMAL).key(true)