diff --git a/format/Schema.fbs b/format/Schema.fbs index e8e14b112a771..2f4d00133c1fe 100644 --- a/format/Schema.fbs +++ b/format/Schema.fbs @@ -61,8 +61,8 @@ enum MetadataVersion:short { /// forward compatibility guarantees). /// 2. A means of negotiating between a client and server /// what features a stream is allowed to use. The enums -/// values here are intented to represent higher level -/// features, additional details maybe negotiated +/// values here are intended to represent higher level +/// features, additional details may be negotiated /// with key-value pairs specific to the protocol. /// /// Enums added to this list should be assigned power-of-two values @@ -395,6 +395,12 @@ table Timestamp { timezone: string; } +table TimestampWithPrecision { + /// Total number of decimal digits + precision: int; + timezone: string; +} + enum IntervalUnit: short { YEAR_MONTH, DAY_TIME, MONTH_DAY_NANO} // A "calendar" interval which models types that don't necessarily // have a precise duration without the context of a base timestamp (e.g. @@ -421,7 +427,7 @@ table Interval { // An absolute length of time unrelated to any calendar artifacts. // // For the purposes of Arrow Implementations, adding this value to a Timestamp -// ("t1") naively (i.e. simply summing the two number) is acceptable even +// ("t1") naively (i.e. simply summing the two numbers) is acceptable even // though in some cases the resulting Timestamp (t2) would not account for // leap-seconds during the elapsed time between "t1" and "t2". Similarly, // representing the difference between two Unix timestamp is acceptable, but @@ -465,6 +471,7 @@ union Type { BinaryView, Utf8View, ListView, + TimestampWithPrecision LargeListView, } @@ -510,7 +517,7 @@ table DictionaryEncoding { /// nested type. table Field { - /// Name is not required, in i.e. a List + /// Name is not required (e.g., in a List) name: string; /// Whether or not this field can contain nulls. Should be true in general. diff --git a/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/binder/ColumnBinderArrowTypeVisitor.java b/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/binder/ColumnBinderArrowTypeVisitor.java index a3d615a7e1958..b1727febff539 100644 --- a/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/binder/ColumnBinderArrowTypeVisitor.java +++ b/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/binder/ColumnBinderArrowTypeVisitor.java @@ -39,6 +39,7 @@ import org.apache.arrow.vector.TimeNanoVector; import org.apache.arrow.vector.TimeSecVector; import org.apache.arrow.vector.TimeStampVector; +import org.apache.arrow.vector.TimeStampWithPrecisionVector; import org.apache.arrow.vector.TinyIntVector; import org.apache.arrow.vector.VarBinaryVector; import org.apache.arrow.vector.VarCharVector; @@ -294,4 +295,14 @@ public ColumnBinder visit(ArrowType.ListView type) { public ColumnBinder visit(ArrowType.LargeListView type) { throw new UnsupportedOperationException("No column binder implemented for type " + type); } + + @Override + public ColumnBinder visit(ArrowType.TimestampWithPrecision type) { + Calendar calendar = null; + final String timezone = type.getTimezone(); + if (timezone != null && !timezone.isEmpty()) { + calendar = Calendar.getInstance(TimeZone.getTimeZone(ZoneId.of(timezone))); + } + return new TimestampWithPrecisionBinder((TimeStampWithPrecisionVector) vector, calendar); + } } diff --git a/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/binder/TimestampWithPrecisionBinder.java b/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/binder/TimestampWithPrecisionBinder.java new file mode 100644 index 0000000000000..8f54cf69c74f9 --- /dev/null +++ b/java/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/binder/TimestampWithPrecisionBinder.java @@ -0,0 +1,129 @@ +/* + * 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.arrow.adapter.jdbc.binder; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; +import org.apache.arrow.vector.TimeStampVector; +import org.apache.arrow.vector.TimeStampWithPrecisionVector; +import org.apache.arrow.vector.types.pojo.ArrowType; + +/** A column binder for timestamps. */ +public class TimestampWithPrecisionBinder extends BaseColumnBinder { + private final Calendar calendar; + private final long unitsPerSecond; + private final long nanosPerUnit; + + /** Create a binder for a timestamp vector using the default JDBC type code. */ + public TimestampWithPrecisionBinder(TimeStampWithPrecisionVector vector, Calendar calendar) { + this( + vector, + calendar, + isZoned(vector.getField().getType()) ? Types.TIMESTAMP_WITH_TIMEZONE : Types.TIMESTAMP); + } + + /** + * Create a binder for a timestamp vector. + * + * @param vector The vector to pull values from. + * @param calendar Optionally, the calendar to pass to JDBC. + * @param jdbcType The JDBC type code to use for null values. + */ + public TimestampWithPrecisionBinder( + TimeStampWithPrecisionVector vector, Calendar calendar, int jdbcType) { + super(vector, jdbcType); + this.calendar = calendar; + + final ArrowType.TimestampWithPrecision type = + (ArrowType.TimestampWithPrecision) vector.getField().getType(); + switch (type.getPrecision()) { + case 0: + this.unitsPerSecond = 1; + this.nanosPerUnit = 1_000_000_000; + break; + case 1: + this.unitsPerSecond = 10; + this.nanosPerUnit = 1_000_000_00; + break; + case 2: + this.unitsPerSecond = 100; + this.nanosPerUnit = 1_000_000_0; + break; + case 3: + this.unitsPerSecond = 1_000; + this.nanosPerUnit = 1_000_000; + break; + case 4: + this.unitsPerSecond = 10_000; + this.nanosPerUnit = 1_000_00; + break; + case 5: + this.unitsPerSecond = 100_000; + this.nanosPerUnit = 1_000_0; + break; + case 6: + this.unitsPerSecond = 1_000_000; + this.nanosPerUnit = 1_000; + break; + case 7: + this.unitsPerSecond = 10_000_000; + this.nanosPerUnit = 1_00; + break; + case 8: + this.unitsPerSecond = 100_000_000; + this.nanosPerUnit = 1_0; + break; + case 9: + this.unitsPerSecond = 1_000_000_000; + this.nanosPerUnit = 1; + break; + default: + throw new IllegalArgumentException("Invalid time unit in " + type); + } + } + + @Override + public void bind(PreparedStatement statement, int parameterIndex, int rowIndex) + throws SQLException { + // TODO: option to throw on truncation (vendor Guava IntMath#multiply) or overflow + final long rawValue = + vector.getDataBuffer().getLong((long) rowIndex * TimeStampWithPrecisionVector.TYPE_WIDTH); + final long seconds = rawValue / unitsPerSecond; + final int nanos = (int) ((rawValue - (seconds * unitsPerSecond)) * nanosPerUnit); + final Timestamp value = new Timestamp(seconds * 1_000); + value.setNanos(nanos); + if (calendar != null) { + // Timestamp == Date == UTC timestamp (confusingly). Arrow's timestamp with timezone is a UTC + // value with a + // zone offset, so we don't need to do any conversion. + statement.setTimestamp(parameterIndex, value, calendar); + } else { + // Arrow timestamp without timezone isn't strictly convertible to any timezone. So this is + // technically wrong, + // but there is no 'correct' interpretation here. The application should provide a calendar. + statement.setTimestamp(parameterIndex, value); + } + } + + private static boolean isZoned(ArrowType type) { + final String timezone = ((ArrowType.TimestampWithPrecision) type).getTimezone(); + return timezone != null && !timezone.isEmpty(); + } +} diff --git a/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampPrecisionAvaticaParameterConverter.java b/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampPrecisionAvaticaParameterConverter.java new file mode 100644 index 0000000000000..013f8baf08962 --- /dev/null +++ b/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampPrecisionAvaticaParameterConverter.java @@ -0,0 +1,45 @@ +/* + * 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.arrow.driver.jdbc.converter.impl; + +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.TimeStampWithPrecisionVector; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.calcite.avatica.AvaticaParameter; +import org.apache.calcite.avatica.remote.TypedValue; + +/** AvaticaParameterConverter for Timestamp Arrow types. */ +public class TimestampPrecisionAvaticaParameterConverter extends BaseAvaticaParameterConverter { + + public TimestampPrecisionAvaticaParameterConverter(ArrowType.TimestampWithPrecision type) {} + + @Override + public boolean bindParameter(FieldVector vector, TypedValue typedValue, int index) { + long value = (long) typedValue.toLocal(); + if (vector instanceof TimeStampWithPrecisionVector) { + ((TimeStampWithPrecisionVector) vector).setSafe(index, value); + return true; + } + return false; + } + + @Override + public AvaticaParameter createParameter(Field field) { + return createParameter(field, false); + } +} diff --git a/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/AvaticaParameterBinder.java b/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/AvaticaParameterBinder.java index 4c2a9b865f141..299fc195d3d58 100644 --- a/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/AvaticaParameterBinder.java +++ b/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/AvaticaParameterBinder.java @@ -37,6 +37,7 @@ import org.apache.arrow.driver.jdbc.converter.impl.StructAvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.TimeAvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.TimestampAvaticaParameterConverter; +import org.apache.arrow.driver.jdbc.converter.impl.TimestampPrecisionAvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.UnionAvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.Utf8AvaticaParameterConverter; import org.apache.arrow.memory.BufferAllocator; @@ -287,5 +288,11 @@ public Boolean visit(ArrowType.RunEndEncoded type) { throw new UnsupportedOperationException( "No Avatica parameter binder implemented for type " + type); } + + @Override + public Boolean visit(ArrowType.TimestampWithPrecision type) { + return new TimestampPrecisionAvaticaParameterConverter(type) + .bindParameter(vector, typedValue, index); + } } } diff --git a/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java b/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java index 17b0f42dc7111..f2876f51dbae0 100644 --- a/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java +++ b/java/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java @@ -40,6 +40,7 @@ import org.apache.arrow.driver.jdbc.converter.impl.StructAvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.TimeAvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.TimestampAvaticaParameterConverter; +import org.apache.arrow.driver.jdbc.converter.impl.TimestampPrecisionAvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.UnionAvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.Utf8AvaticaParameterConverter; import org.apache.arrow.driver.jdbc.converter.impl.Utf8ViewAvaticaParameterConverter; @@ -290,5 +291,10 @@ public AvaticaParameter visit(ArrowType.RunEndEncoded type) { throw new UnsupportedOperationException( "No Avatica parameter binder implemented for type " + type); } + + @Override + public AvaticaParameter visit(ArrowType.TimestampWithPrecision type) { + return new TimestampPrecisionAvaticaParameterConverter(type).createParameter(field); + } } } diff --git a/java/format/src/main/java/org/apache/arrow/flatbuf/TimestampWithPrecision.java b/java/format/src/main/java/org/apache/arrow/flatbuf/TimestampWithPrecision.java new file mode 100644 index 0000000000000..aa801b1ffdf35 --- /dev/null +++ b/java/format/src/main/java/org/apache/arrow/flatbuf/TimestampWithPrecision.java @@ -0,0 +1,82 @@ +/* + * 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.arrow.flatbuf; + +import com.google.flatbuffers.BaseVector; +import com.google.flatbuffers.Constants; +import com.google.flatbuffers.FlatBufferBuilder; +import com.google.flatbuffers.Table; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public final class TimestampWithPrecision extends Table { + public static void ValidateVersion() { Constants.FLATBUFFERS_24_3_25(); } + public static TimestampWithPrecision getRootAsTimestampWithPrecision(ByteBuffer _bb) { + return getRootAsTimestampWithPrecision(_bb, new TimestampWithPrecision()); + } + public static TimestampWithPrecision getRootAsTimestampWithPrecision(ByteBuffer _bb, TimestampWithPrecision obj) { + _bb.order(ByteOrder.LITTLE_ENDIAN); + return obj.__init(_bb.getInt(_bb.position()) + _bb.position(), _bb); + } + + public TimestampWithPrecision __init(int _i, ByteBuffer _bb) { + this.bb_pos = _i; + this.bb = _bb; + return this; + } + public TimestampWithPrecision __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public int precision() { + int o = this.__offset(4); + return o != 0 ? this.bb.getInt(o + this.bb_pos) : 0; + } + public String timezone() { + int o = this.__offset(6); + return o != 0 ? this.__string(o + this.bb_pos) : null; + } + public ByteBuffer timezoneAsByteBuffer() { + return this.__vector_as_bytebuffer(6, 1); + } + public ByteBuffer timezoneInByteBuffer(ByteBuffer _bb) { return __vector_in_bytebuffer(_bb, 6, 1); } + + public static int createTimestampWithPrecision(FlatBufferBuilder builder, int precision, int timezone) { + builder.startTable(2); + TimestampWithPrecision.addTimezone(builder, timezone); + TimestampWithPrecision.addPrecision(builder, precision); + return endTimestampWithPrecision(builder); + } + public static void startTimestampWithPrecision(FlatBufferBuilder builder) { + builder.startTable(2); + } + public static void addPrecision(FlatBufferBuilder builder, int precision) { + builder.addInt(0, precision, 0); + } + public static void addTimezone(FlatBufferBuilder builder, int timezoneOffset) { + builder.addOffset(1, timezoneOffset, 0); + } + public static int endTimestampWithPrecision(FlatBufferBuilder builder) { + int o = builder.endTable(); + return o; + } + + public static final class Vector extends BaseVector { + public Vector __assign(int _vector, int _element_size, ByteBuffer _bb) { __reset(_vector, _element_size, _bb); return this; } + + public TimestampWithPrecision get(int j) { return get( new TimestampWithPrecision(), j); } + public TimestampWithPrecision get(TimestampWithPrecision obj, int j) { return obj.__assign(__indirect(__element(j), bb), bb); } + } +} diff --git a/java/format/src/main/java/org/apache/arrow/flatbuf/Type.java b/java/format/src/main/java/org/apache/arrow/flatbuf/Type.java index ba0f665f647d4..ef0df978124c4 100644 --- a/java/format/src/main/java/org/apache/arrow/flatbuf/Type.java +++ b/java/format/src/main/java/org/apache/arrow/flatbuf/Type.java @@ -51,8 +51,9 @@ private Type() { } public static final byte Utf8View = 24; public static final byte ListView = 25; public static final byte LargeListView = 26; + public static final byte TimestampWithPrecision = 27; - public static final String[] names = { "NONE", "Null", "Int", "FloatingPoint", "Binary", "Utf8", "Bool", "Decimal", "Date", "Time", "Timestamp", "Interval", "List", "Struct_", "Union", "FixedSizeBinary", "FixedSizeList", "Map", "Duration", "LargeBinary", "LargeUtf8", "LargeList", "RunEndEncoded", "BinaryView", "Utf8View", "ListView", "LargeListView", }; + public static final String[] names = { "NONE", "Null", "Int", "FloatingPoint", "Binary", "Utf8", "Bool", "Decimal", "Date", "Time", "Timestamp", "Interval", "List", "Struct_", "Union", "FixedSizeBinary", "FixedSizeList", "Map", "Duration", "LargeBinary", "LargeUtf8", "LargeList", "RunEndEncoded", "BinaryView", "Utf8View", "ListView", "LargeListView", "TimestampWithPrecision",}; public static String name(int e) { return names[e]; } } diff --git a/java/vector/src/main/codegen/data/ArrowTypes.tdd b/java/vector/src/main/codegen/data/ArrowTypes.tdd index 5a0b30e47ee52..d7a771d32f72c 100644 --- a/java/vector/src/main/codegen/data/ArrowTypes.tdd +++ b/java/vector/src/main/codegen/data/ArrowTypes.tdd @@ -120,6 +120,11 @@ fields: [{name: "unit", type: short, valueType: TimeUnit}, {name: "timezone", type: String}] complex: false }, + { + name: "TimestampWithPrecision", + fields: [{name: "precision", type: int}, {name: "timezone", type: String}], + complex: false + }, { name: "Interval", fields: [{name: "unit", type: short, valueType: IntervalUnit}], diff --git a/java/vector/src/main/codegen/data/ValueVectorTypes.tdd b/java/vector/src/main/codegen/data/ValueVectorTypes.tdd index ad1f1b93bb3aa..c0e3eba88bb6a 100644 --- a/java/vector/src/main/codegen/data/ValueVectorTypes.tdd +++ b/java/vector/src/main/codegen/data/ValueVectorTypes.tdd @@ -94,6 +94,11 @@ { class: "TimeStampMilli", javaType: "long", boxedType: "Long", friendlyType: "LocalDateTime" }, { class: "TimeStampMicro", javaType: "long", boxedType: "Long", friendlyType: "LocalDateTime" }, { class: "TimeStampNano", javaType: "long", boxedType: "Long", friendlyType: "LocalDateTime" }, + { class: "TimeStampWithPrecision", javaType: "long", + typeParams: [{ name: "precision", type: "int"}], + arrowType: "org.apache.arrow.vector.types.pojo.ArrowType.TimestampWithPrecision", + arrowTypeConstructorParams: ["precision", "null"], + boxedType: "Long", friendlyType: "LocalDateTime" }, { class: "TimeStampSecTZ", javaType: "long", boxedType: "Long", typeParams: [ {name: "timezone", type: "String"} ], arrowType: "org.apache.arrow.vector.types.pojo.ArrowType.Timestamp", diff --git a/java/vector/src/main/codegen/templates/AbstractFieldWriter.java b/java/vector/src/main/codegen/templates/AbstractFieldWriter.java index 5ebfb6877fc5b..412cfa533f8c2 100644 --- a/java/vector/src/main/codegen/templates/AbstractFieldWriter.java +++ b/java/vector/src/main/codegen/templates/AbstractFieldWriter.java @@ -164,6 +164,16 @@ public void write(${name}Holder holder) { fail("${name}"); } + + <#if minor.class?starts_with("TimestampWithPrecision")> + public void write${minor.class}(${friendlyType} value) { + fail("${name}"); + } + + public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, , ArrowType arrowType) { + fail("${name}"); + } + diff --git a/java/vector/src/main/codegen/templates/ComplexCopier.java b/java/vector/src/main/codegen/templates/ComplexCopier.java index 5adad523120da..cdfd12871de62 100644 --- a/java/vector/src/main/codegen/templates/ComplexCopier.java +++ b/java/vector/src/main/codegen/templates/ComplexCopier.java @@ -114,14 +114,14 @@ private static void writeValue(FieldReader reader, FieldWriter writer) { <#assign fields = minor.fields!type.fields /> <#assign uncappedName = name?uncap_first/> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") > + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || minor.class == "TimestampWithPrecision"> case ${name?upper_case}: if (reader.isSet()) { Nullable${name}Holder ${uncappedName}Holder = new Nullable${name}Holder(); reader.read(${uncappedName}Holder); if (${uncappedName}Holder.isSet == 1) { - writer.write${name}(<#list fields as field>${uncappedName}Holder.${field.name}<#if field_has_next>, <#if minor.class?starts_with("Decimal")>, new ArrowType.Decimal(${uncappedName}Holder.precision, ${uncappedName}Holder.scale, ${name}Holder.WIDTH * 8)); + writer.write${name}(<#list fields as field>${uncappedName}Holder.${field.name}<#if field_has_next>, <#if minor.class = "TimestampWithPrecision">, new ArrowType.TimeStampWithPrecision(${uncappedName}Holder.precision, ${uncappedName}Holder.scale, ${name}Holder.WIDTH * 8)<#if minor.class?starts_with("Decimal")>, new ArrowType.Decimal(${uncappedName}Holder.precision, ${uncappedName}Holder.scale, ${name}Holder.WIDTH * 8)); } } else { writer.writeNull(); diff --git a/java/vector/src/main/codegen/templates/ComplexWriters.java b/java/vector/src/main/codegen/templates/ComplexWriters.java index 2e3caae1f0f22..1e71ad5fec44d 100644 --- a/java/vector/src/main/codegen/templates/ComplexWriters.java +++ b/java/vector/src/main/codegen/templates/ComplexWriters.java @@ -107,7 +107,7 @@ public void setPosition(int idx) { <#else> - <#if !minor.class?starts_with("Decimal")> + <#if !minor.class?starts_with("Decimal") && !(minor.class == "TimestampWithPrecision")> public void write(${minor.class}Holder h) { vector.setSafe(idx(), h); vector.setValueCount(idx()+1); @@ -185,6 +185,24 @@ public void write(Nullable${minor.class}Holder h){ vector.setValueCount(idx() + 1); } + + <#if minor.class == "TimestampWithPrecision"> + + public void write(${minor.class}Holder h) { + vector.setSafe(idx(), h); + vector.setValueCount(idx()+1); + } + + public void write(Nullable${minor.class}Holder h) { + vector.setSafe(idx(), h); + vector.setValueCount(idx()+1); + } + + public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, , ArrowType arrowType){ + vector.setSafe(idx(), 1<#list fields as field><#if field.include!true >, ${field.name}); + vector.setValueCount(idx()+1); + } + public void writeNull() { @@ -273,6 +291,14 @@ public interface ${eName}Writer extends BaseWriter { public void write${minor.class}(String value); + +<#if minor.class == "TimestampWithPrecision"> + + public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, , ArrowType arrowType); + + public void write${minor.class}(${friendlyType} value); + + } diff --git a/java/vector/src/main/codegen/templates/HolderReaderImpl.java b/java/vector/src/main/codegen/templates/HolderReaderImpl.java index 1151ea5d39dda..fa379ef498e87 100644 --- a/java/vector/src/main/codegen/templates/HolderReaderImpl.java +++ b/java/vector/src/main/codegen/templates/HolderReaderImpl.java @@ -148,7 +148,7 @@ public void read(Nullable${name}Holder h) { return DateUtility.getLocalDateTimeFromEpochMilli(holder.value); <#elseif minor.class == "TimeStampMicro"> return DateUtility.getLocalDateTimeFromEpochMicro(holder.value); - <#elseif minor.class == "TimeStampNano"> + <#elseif minor.class == "TimeStampNano" || minor.class == "TimeStampWithPrecision"> return DateUtility.getLocalDateTimeFromEpochNano(holder.value); <#else> ${friendlyType} value = new ${friendlyType}(this.holder.value); diff --git a/java/vector/src/main/codegen/templates/PromotableWriter.java b/java/vector/src/main/codegen/templates/PromotableWriter.java index c0e686f3178a4..652715344e93b 100644 --- a/java/vector/src/main/codegen/templates/PromotableWriter.java +++ b/java/vector/src/main/codegen/templates/PromotableWriter.java @@ -316,6 +316,7 @@ protected boolean requiresArrowType(MinorType type) { || type == MinorType.MAP || type == MinorType.DURATION || type == MinorType.FIXEDSIZEBINARY + || type == MinorType.TIMESTAMPWITHPRECISION || (type.name().startsWith("TIMESTAMP") && type.name().endsWith("TZ")); } diff --git a/java/vector/src/main/codegen/templates/UnionReader.java b/java/vector/src/main/codegen/templates/UnionReader.java index 68e30ef48846b..b78c1afb07a42 100644 --- a/java/vector/src/main/codegen/templates/UnionReader.java +++ b/java/vector/src/main/codegen/templates/UnionReader.java @@ -39,7 +39,7 @@ @SuppressWarnings("unused") public class UnionReader extends AbstractFieldReader { - private static final int NUM_SUPPORTED_TYPES = 51; + private static final int NUM_SUPPORTED_TYPES = 52; private BaseReader[] readers = new BaseReader[NUM_SUPPORTED_TYPES]; public UnionVector data; @@ -99,7 +99,7 @@ private FieldReader getReaderForIndex(int index) { <#list type.minor as minor> <#assign name = minor.class?cap_first /> <#assign uncappedName = name?uncap_first/> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> case ${name?upper_case}: return (FieldReader) get${name}(); @@ -190,7 +190,7 @@ public int size() { <#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) /> <#assign safeType=friendlyType /> <#if safeType=="byte[]"><#assign safeType="ByteArray" /> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> private ${name}ReaderImpl ${uncappedName}Reader; diff --git a/java/vector/src/main/codegen/templates/UnionVector.java b/java/vector/src/main/codegen/templates/UnionVector.java index 67efdf60f79cf..59637863af262 100644 --- a/java/vector/src/main/codegen/templates/UnionVector.java +++ b/java/vector/src/main/codegen/templates/UnionVector.java @@ -273,11 +273,11 @@ public StructVector getStruct() { <#assign fields = minor.fields!type.fields /> <#assign uncappedName = name?uncap_first/> <#assign lowerCaseName = name?lower_case/> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> private ${name}Vector ${uncappedName}Vector; - <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> public ${name}Vector get${name}Vector() { if (${uncappedName}Vector == null) { ${uncappedName}Vector = internalStruct.getChild(fieldName(MinorType.${name?upper_case}), ${name}Vector.class); diff --git a/java/vector/src/main/codegen/templates/UnionViewWriter.java b/java/vector/src/main/codegen/templates/UnionViewWriter.java index 7b834d8b6cd86..f22ffed8d8ec5 100644 --- a/java/vector/src/main/codegen/templates/UnionViewWriter.java +++ b/java/vector/src/main/codegen/templates/UnionViewWriter.java @@ -57,11 +57,11 @@ public StructWriter struct() { <#assign fields = minor.fields!type.fields /> <#assign uncappedName = name?uncap_first/> <#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) /> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> private ${name}Writer ${name?uncap_first}Writer; - <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> private ${name}Writer get${name}Writer(ArrowType arrowType) { if (${uncappedName}Writer == null) { ${uncappedName}Writer = new ${name}WriterImpl(data.get${name}Vector(arrowType)); @@ -111,13 +111,17 @@ public void write(${name}Holder holder) { ArrowType arrowType = new ArrowType.FixedSizeBinary(holder.byteWidth); get${name}Writer(arrowType).setPosition(idx()); get${name}Writer(arrowType).write(holder); + <#elseif minor.class == "TimestampWithPrecision"> + ArrowType arrowType = new ArrowType.TimeStampWithPrecision(holder.precision, null); + get${name}Writer(arrowType).setPosition(idx()); + get${name}Writer(arrowType).write(holder); <#else> get${name}Writer().setPosition(idx()); get${name}Writer().write${name}(<#list fields as field>holder.${field.name}<#if field_has_next>, ); } - public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, <#if minor.class?starts_with("Decimal")>, ArrowType arrowType) { + public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, <#if minor.class?starts_with("Decimal") || minor.class == "TimestampWithPrecision">, ArrowType arrowType) { data.setType(idx(), MinorType.${name?upper_case}); <#if minor.class?starts_with("Decimal")> get${name}Writer(arrowType).setPosition(idx()); @@ -133,6 +137,9 @@ public void write(${name}Holder holder) { ArrowType arrowType = MinorType.${name?upper_case}.getType(); get${name}Writer(arrowType).setPosition(idx()); get${name}Writer(arrowType).write${name}(<#list fields as field>${field.name}<#if field_has_next>, ); + <#elseif minor.class == "TimestampWithPrecision"> + get${name}Writer(arrowType).setPosition(idx()); + get${name}Writer(arrowType).write${name}(<#list fields as field>${field.name}<#if field_has_next>, ); <#else> get${name}Writer().setPosition(idx()); get${name}Writer().write${name}(<#list fields as field>${field.name}<#if field_has_next>, ); @@ -187,6 +194,13 @@ public void write(${name}Holder holder) { get${name}Writer().setPosition(idx()); get${name}Writer().write${minor.class}(value); } + <#elseif minor.class == "TimestampWithPrecision"> + public void write${name}(${friendlyType} value, int precision) { + data.setType(idx(), MinorType.${name?upper_case}); + ArrowType arrowType = new ArrowType.TimeStampWithPrecision(precision, null); + get${name}Writer(arrowType).setPosition(idx()); + get${name}Writer(arrowType).write${name}(value); + } @@ -197,7 +211,7 @@ public void write(${name}Holder holder) { <#if lowerName == "int" ><#assign lowerName = "integer" /> <#assign upperName = minor.class?upper_case /> <#assign capName = minor.class?cap_first /> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> @Override public ${capName}Writer ${lowerName}() { diff --git a/java/vector/src/main/codegen/templates/UnionWriter.java b/java/vector/src/main/codegen/templates/UnionWriter.java index bfe97e2770553..6b3a76f5aa6fa 100644 --- a/java/vector/src/main/codegen/templates/UnionWriter.java +++ b/java/vector/src/main/codegen/templates/UnionWriter.java @@ -232,9 +232,9 @@ BaseWriter getWriter(MinorType minorType, ArrowType arrowType) { <#assign name = minor.class?cap_first /> <#assign fields = minor.fields!type.fields /> <#assign uncappedName = name?uncap_first/> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> case ${name?upper_case}: - <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> return get${name}Writer(arrowType); <#else> return get${name}Writer(); @@ -252,11 +252,11 @@ BaseWriter getWriter(MinorType minorType, ArrowType arrowType) { <#assign fields = minor.fields!type.fields /> <#assign uncappedName = name?uncap_first/> <#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) /> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> private ${name}Writer ${name?uncap_first}Writer; - <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> private ${name}Writer get${name}Writer(ArrowType arrowType) { if (${uncappedName}Writer == null) { ${uncappedName}Writer = new ${name}WriterImpl(data.get${name}Vector(arrowType)); @@ -306,13 +306,17 @@ public void write(${name}Holder holder) { ArrowType arrowType = new ArrowType.FixedSizeBinary(holder.byteWidth); get${name}Writer(arrowType).setPosition(idx()); get${name}Writer(arrowType).write(holder); + <#elseif minor.class == "TimestampWithPrecision"> + ArrowType arrowType = new ArrowType.TimeStampWithPrecision(holder.precision, null); + get${name}Writer(arrowType).setPosition(idx()); + get${name}Writer(arrowType).write(holder); <#else> get${name}Writer().setPosition(idx()); get${name}Writer().write${name}(<#list fields as field>holder.${field.name}<#if field_has_next>, ); } - public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, <#if minor.class?starts_with("Decimal")>, ArrowType arrowType) { + public void write${minor.class}(<#list fields as field>${field.type} ${field.name}<#if field_has_next>, <#if minor.class?starts_with("Decimal") || minor.class == "TimestampWithPrecision">, ArrowType arrowType) { data.setType(idx(), MinorType.${name?upper_case}); <#if minor.class?starts_with("Decimal")> get${name}Writer(arrowType).setPosition(idx()); @@ -328,6 +332,9 @@ public void write(${name}Holder holder) { ArrowType arrowType = MinorType.${name?upper_case}.getType(); get${name}Writer(arrowType).setPosition(idx()); get${name}Writer(arrowType).write${name}(<#list fields as field>${field.name}<#if field_has_next>, ); + <#elseif minor.class == "TimestampWithPrecision"> + get${name}Writer(arrowType).setPosition(idx()); + get${name}Writer(arrowType).write${name}(<#list fields as field>${field.name}<#if field_has_next>, , arrowType); <#else> get${name}Writer().setPosition(idx()); get${name}Writer().write${name}(<#list fields as field>${field.name}<#if field_has_next>, ); @@ -346,6 +353,13 @@ public void write(${name}Holder holder) { get${name}Writer(arrowType).setPosition(idx()); get${name}Writer(arrowType).writeBigEndianBytesTo${name}(value, arrowType); } + <#elseif minor.class == "TimestampWithPrecision"> + public void write${name}(${friendlyType} value, int precision) { + data.setType(idx(), MinorType.${name?upper_case}); + ArrowType arrowType = new ArrowType.TimeStampWithPrecision(precision, null); + get${name}Writer(arrowType).setPosition(idx()); + get${name}Writer(arrowType).write${name}(value); + } <#elseif minor.class?ends_with("VarBinary")> @Override public void write${minor.class}(byte[] value) { @@ -465,7 +479,7 @@ public MapWriter map(String name, boolean keysSorted) { <#if lowerName == "int" ><#assign lowerName = "integer" /> <#assign upperName = minor.class?upper_case /> <#assign capName = minor.class?cap_first /> - <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if !minor.typeParams?? || minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> @Override public ${capName}Writer ${lowerName}(String name) { data.setType(idx(), MinorType.STRUCT); @@ -480,7 +494,7 @@ public MapWriter map(String name, boolean keysSorted) { return getListWriter().${lowerName}(); } - <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary"> + <#if minor.class?starts_with("Decimal") || is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary" || minor.class == "TimestampWithPrecision"> @Override public ${capName}Writer ${lowerName}(String name<#list minor.typeParams as typeParam>, ${typeParam.type} ${typeParam.name}) { data.setType(idx(), MinorType.STRUCT); diff --git a/java/vector/src/main/java/org/apache/arrow/vector/TimeStampWithPrecisionVector.java b/java/vector/src/main/java/org/apache/arrow/vector/TimeStampWithPrecisionVector.java new file mode 100644 index 0000000000000..1dc6cfd28b855 --- /dev/null +++ b/java/vector/src/main/java/org/apache/arrow/vector/TimeStampWithPrecisionVector.java @@ -0,0 +1,240 @@ +/* + * 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.arrow.vector; + +import static org.apache.arrow.vector.NullCheckingForGet.NULL_CHECKING_ENABLED; + +import java.time.LocalDateTime; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.vector.complex.impl.TimeStampWithPrecisionReaderImpl; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.holders.NullableTimeStampWithPrecisionHolder; +import org.apache.arrow.vector.holders.TimeStampWithPrecisionHolder; +import org.apache.arrow.vector.types.Types.MinorType; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.FieldType; +import org.apache.arrow.vector.util.DateUtility; +import org.apache.arrow.vector.util.TransferPair; + +/** + * TimeStampWithPrecisionVector implements a fixed width vector (8 bytes) of timestamp (nanosecond + * resolution) values which could be null. A validity buffer (bit vector) is maintained to track + * which elements in the vector are null. + */ +public final class TimeStampWithPrecisionVector extends TimeStampVector + implements ValueIterableVector { + private final int precision; + + /** + * Instantiate a TimeStampWithPrecisionVector. This doesn't allocate any memory for the data in + * vector. + * + * @param name name of the vector + * @param allocator allocator for memory management. + */ + public TimeStampWithPrecisionVector(String name, BufferAllocator allocator, int precision) { + this( + name, FieldType.nullable(new ArrowType.TimestampWithPrecision(precision, null)), allocator); + } + + /** + * Instantiate a TimeStampWithPrecisionVector. This doesn't allocate any memory for the data in + * vector. + * + * @param name name of the vector + * @param fieldType type of Field materialized by this vector + * @param allocator allocator for memory management. + */ + public TimeStampWithPrecisionVector(String name, FieldType fieldType, BufferAllocator allocator) { + super(name, fieldType, allocator); + org.apache.arrow.vector.types.pojo.ArrowType.TimestampWithPrecision arrowType = + (org.apache.arrow.vector.types.pojo.ArrowType.TimestampWithPrecision) fieldType.getType(); + this.precision = arrowType.getPrecision(); + } + + /** + * Instantiate a TimeStampWithPrecisionVector. This doesn't allocate any memory for the data in + * vector. + * + * @param field Field materialized by this vector + * @param allocator allocator for memory management. + */ + public TimeStampWithPrecisionVector(Field field, BufferAllocator allocator) { + super(field, allocator); + org.apache.arrow.vector.types.pojo.ArrowType.TimestampWithPrecision arrowType = + (org.apache.arrow.vector.types.pojo.ArrowType.TimestampWithPrecision) field.getType(); + this.precision = arrowType.getPrecision(); + } + + @Override + protected FieldReader getReaderImpl() { + return new TimeStampWithPrecisionReaderImpl(TimeStampWithPrecisionVector.this); + } + + /** + * Get minor type for this vector. The vector holds values belonging to a particular type. + * + * @return {@link MinorType} + */ + @Override + public MinorType getMinorType() { + return MinorType.TIMESTAMPWITHPRECISION; + } + + /*----------------------------------------------------------------* + | | + | vector value retrieval methods | + | | + *----------------------------------------------------------------*/ + + /** + * Get the element at the given index from the vector and sets the state in holder. If element at + * given index is null, holder.isSet will be zero. + * + * @param index position of element + */ + public void get(int index, NullableTimeStampWithPrecisionHolder holder) { + if (NULL_CHECKING_ENABLED && isSet(index) == 0) { + holder.isSet = 0; + return; + } + holder.isSet = 1; + holder.value = valueBuffer.getLong((long) index * TYPE_WIDTH); + } + + /** + * Same as {@link #get(int)}. + * + * @param index position of element + * @return element at given index + */ + @Override + public LocalDateTime getObject(int index) { + if (isSet(index) == 0) { + return null; + } else { + final long nanos = valueBuffer.getLong((long) index * TYPE_WIDTH); + return DateUtility.getLocalDateTimeFromEpochNano(nanos); + } + } + + /*----------------------------------------------------------------* + | | + | vector value setter methods | + | | + *----------------------------------------------------------------*/ + + /** + * Set the element at the given index to the value set in data holder. If the value in holder is + * not indicated as set, element in the at the given index will be null. + * + * @param index position of element + * @param holder nullable data holder for value of element + */ + public void set(int index, NullableTimeStampWithPrecisionHolder holder) + throws IllegalArgumentException { + if (holder.isSet < 0) { + throw new IllegalArgumentException(); + } else if (holder.isSet > 0) { + BitVectorHelper.setBit(validityBuffer, index); + setValue(index, holder.value); + } else { + BitVectorHelper.unsetBit(validityBuffer, index); + } + } + + /** + * Set the element at the given index to the value set in data holder. + * + * @param index position of element + * @param holder data holder for value of element + */ + public void set(int index, TimeStampWithPrecisionHolder holder) { + BitVectorHelper.setBit(validityBuffer, index); + setValue(index, holder.value); + } + + /** + * Same as {@link #set(int, NullableTimeStampWithPrecisionHolder)} except that it handles the case + * when index is greater than or equal to existing value capacity {@link #getValueCapacity()}. + * + * @param index position of element + * @param holder nullable data holder for value of element + */ + public void setSafe(int index, NullableTimeStampWithPrecisionHolder holder) + throws IllegalArgumentException { + handleSafe(index); + set(index, holder); + } + + /** + * Same as {@link #set(int, TimeStampWithPrecisionHolder)} except that it handles the case when + * index is greater than or equal to existing value capacity {@link #getValueCapacity()}. + * + * @param index position of element + * @param holder data holder for value of element + */ + public void setSafe(int index, TimeStampWithPrecisionHolder holder) { + handleSafe(index); + set(index, holder); + } + + /*----------------------------------------------------------------* + | | + | vector transfer | + | | + *----------------------------------------------------------------*/ + + /** + * Construct a TransferPair comprising this and a target vector of the same type. + * + * @param ref name of the target vector + * @param allocator allocator for the target vector + * @return {@link TransferPair} + */ + @Override + public TransferPair getTransferPair(String ref, BufferAllocator allocator) { + TimeStampWithPrecisionVector to = + new TimeStampWithPrecisionVector(ref, field.getFieldType(), allocator); + return new TransferImpl(to); + } + + /** + * Construct a TransferPair comprising this and a target vector of the same type. + * + * @param field Field object used by the target vector + * @param allocator allocator for the target vector + * @return {@link TransferPair} + */ + @Override + public TransferPair getTransferPair(Field field, BufferAllocator allocator) { + TimeStampWithPrecisionVector to = new TimeStampWithPrecisionVector(field, allocator); + return new TransferImpl(to); + } + + /** + * Construct a TransferPair with a desired target vector of the same type. + * + * @param to target vector + * @return {@link TransferPair} + */ + @Override + public TransferPair makeTransferPair(ValueVector to) { + return new TransferImpl((TimeStampWithPrecisionVector) to); + } +} diff --git a/java/vector/src/main/java/org/apache/arrow/vector/TypeLayout.java b/java/vector/src/main/java/org/apache/arrow/vector/TypeLayout.java index fa75ef04577a3..7482c6a20df29 100644 --- a/java/vector/src/main/java/org/apache/arrow/vector/TypeLayout.java +++ b/java/vector/src/main/java/org/apache/arrow/vector/TypeLayout.java @@ -44,6 +44,7 @@ import org.apache.arrow.vector.types.pojo.ArrowType.Struct; import org.apache.arrow.vector.types.pojo.ArrowType.Time; import org.apache.arrow.vector.types.pojo.ArrowType.Timestamp; +import org.apache.arrow.vector.types.pojo.ArrowType.TimestampWithPrecision; import org.apache.arrow.vector.types.pojo.ArrowType.Union; import org.apache.arrow.vector.types.pojo.ArrowType.Utf8; import org.apache.arrow.vector.types.pojo.ArrowType.Utf8View; @@ -100,6 +101,11 @@ public TypeLayout visit(Timestamp type) { return newFixedWidthTypeLayout(BufferLayout.dataBuffer(64)); } + @Override + public TypeLayout visit(TimestampWithPrecision type) { + return newFixedWidthTypeLayout(BufferLayout.dataBuffer(64)); + } + @Override public TypeLayout visit(ArrowType.List type) { List vectors = @@ -455,6 +461,11 @@ public Integer visit(Duration type) { public Integer visit(RunEndEncoded type) { return 0; } + + @Override + public Integer visit(TimestampWithPrecision type) { + return FIXED_WIDTH_BUFFER_COUNT; + } }); } diff --git a/java/vector/src/main/java/org/apache/arrow/vector/extension/OpaqueType.java b/java/vector/src/main/java/org/apache/arrow/vector/extension/OpaqueType.java index ca56214fdac77..e820c3ae8e6b2 100644 --- a/java/vector/src/main/java/org/apache/arrow/vector/extension/OpaqueType.java +++ b/java/vector/src/main/java/org/apache/arrow/vector/extension/OpaqueType.java @@ -54,6 +54,7 @@ import org.apache.arrow.vector.TimeStampNanoVector; import org.apache.arrow.vector.TimeStampSecTZVector; import org.apache.arrow.vector.TimeStampSecVector; +import org.apache.arrow.vector.TimeStampWithPrecisionVector; import org.apache.arrow.vector.VarBinaryVector; import org.apache.arrow.vector.VarCharVector; import org.apache.arrow.vector.ViewVarBinaryVector; @@ -399,5 +400,10 @@ public FieldVector visit(LargeListView type) { public FieldVector visit(RunEndEncoded type) { throw unsupported(type); } + + @Override + public FieldVector visit(TimestampWithPrecision type) { + return new TimeStampWithPrecisionVector(Field.nullable(name, type), allocator); + } } } diff --git a/java/vector/src/main/java/org/apache/arrow/vector/types/Types.java b/java/vector/src/main/java/org/apache/arrow/vector/types/Types.java index e9b963b62c13b..e6864eb659621 100644 --- a/java/vector/src/main/java/org/apache/arrow/vector/types/Types.java +++ b/java/vector/src/main/java/org/apache/arrow/vector/types/Types.java @@ -56,6 +56,7 @@ import org.apache.arrow.vector.TimeStampNanoVector; import org.apache.arrow.vector.TimeStampSecTZVector; import org.apache.arrow.vector.TimeStampSecVector; +import org.apache.arrow.vector.TimeStampWithPrecisionVector; import org.apache.arrow.vector.TinyIntVector; import org.apache.arrow.vector.UInt1Vector; import org.apache.arrow.vector.UInt2Vector; @@ -108,6 +109,7 @@ import org.apache.arrow.vector.complex.impl.TimeStampNanoWriterImpl; import org.apache.arrow.vector.complex.impl.TimeStampSecTZWriterImpl; import org.apache.arrow.vector.complex.impl.TimeStampSecWriterImpl; +import org.apache.arrow.vector.complex.impl.TimeStampWithPrecisionWriterImpl; import org.apache.arrow.vector.complex.impl.TinyIntWriterImpl; import org.apache.arrow.vector.complex.impl.UInt1WriterImpl; import org.apache.arrow.vector.complex.impl.UInt2WriterImpl; @@ -147,6 +149,7 @@ import org.apache.arrow.vector.types.pojo.ArrowType.Struct; import org.apache.arrow.vector.types.pojo.ArrowType.Time; import org.apache.arrow.vector.types.pojo.ArrowType.Timestamp; +import org.apache.arrow.vector.types.pojo.ArrowType.TimestampWithPrecision; import org.apache.arrow.vector.types.pojo.ArrowType.Union; import org.apache.arrow.vector.types.pojo.ArrowType.Utf8; import org.apache.arrow.vector.types.pojo.ArrowType.Utf8View; @@ -356,6 +359,18 @@ public FieldWriter getNewFieldWriter(ValueVector vector) { return new TimeStampNanoWriterImpl((TimeStampNanoVector) vector); } }, + TIMESTAMPWITHPRECISION(null) { + @Override + public FieldVector getNewVector( + Field field, BufferAllocator allocator, CallBack schemaChangeCallback) { + return new TimeStampWithPrecisionVector(field, allocator); + } + + @Override + public FieldWriter getNewFieldWriter(ValueVector vector) { + return new TimeStampWithPrecisionWriterImpl((TimeStampWithPrecisionVector) vector); + } + }, INTERVALDAY(new Interval(IntervalUnit.DAY_TIME)) { @Override public FieldVector getNewVector( @@ -1041,6 +1056,11 @@ public MinorType visit(ExtensionType type) { public MinorType visit(RunEndEncoded type) { return MinorType.RUNENDENCODED; } + + @Override + public MinorType visit(TimestampWithPrecision type) { + return MinorType.TIMESTAMPWITHPRECISION; + } }); } }