From c29fcfc72bed84c1dc9926bc28294b5ff193937f Mon Sep 17 00:00:00 2001
From: Vishal Boddu <9461605+bodduv@users.noreply.github.com>
Date: Thu, 3 Apr 2025 17:25:44 +0200
Subject: [PATCH 001/271] GH-692: Preserve nullability information while
transfering DecimalVector and Decimal256Vector (#693)
## What's Changed
This PR proposes to use the "from" `ValueVector`'s field while
transferring `DecimalVector` and `Decimal256Vector` to preserve
nullability information. This is to have similar behavior with all the
other primitive value vectors. Note that the `FieldType` of the value
vector has nullability information.
Closes #692 .
---
.../apache/arrow/vector/Decimal256Vector.java | 7 +++--
.../apache/arrow/vector/DecimalVector.java | 5 +++-
.../arrow/vector/TestDecimal256Vector.java | 28 +++++++++++++++++++
.../arrow/vector/TestDecimalVector.java | 26 +++++++++++++++++
4 files changed, 63 insertions(+), 3 deletions(-)
diff --git a/vector/src/main/java/org/apache/arrow/vector/Decimal256Vector.java b/vector/src/main/java/org/apache/arrow/vector/Decimal256Vector.java
index 90f67798dd..f9d7e5cb9e 100644
--- a/vector/src/main/java/org/apache/arrow/vector/Decimal256Vector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/Decimal256Vector.java
@@ -567,8 +567,11 @@ private class TransferImpl implements TransferPair {
public TransferImpl(String ref, BufferAllocator allocator) {
to =
- new Decimal256Vector(
- ref, allocator, Decimal256Vector.this.precision, Decimal256Vector.this.scale);
+ (Decimal256Vector.this.field != null
+ && Decimal256Vector.this.field.getFieldType() != null)
+ ? new Decimal256Vector(ref, Decimal256Vector.this.field.getFieldType(), allocator)
+ : new Decimal256Vector(
+ ref, allocator, Decimal256Vector.this.precision, Decimal256Vector.this.scale);
}
public TransferImpl(Field field, BufferAllocator allocator) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/DecimalVector.java b/vector/src/main/java/org/apache/arrow/vector/DecimalVector.java
index b4c55680b7..9bf1812cc6 100644
--- a/vector/src/main/java/org/apache/arrow/vector/DecimalVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/DecimalVector.java
@@ -565,7 +565,10 @@ private class TransferImpl implements TransferPair {
public TransferImpl(String ref, BufferAllocator allocator) {
to =
- new DecimalVector(ref, allocator, DecimalVector.this.precision, DecimalVector.this.scale);
+ (DecimalVector.this.field != null && DecimalVector.this.field.getFieldType() != null)
+ ? new DecimalVector(ref, DecimalVector.this.field.getFieldType(), allocator)
+ : new DecimalVector(
+ ref, allocator, DecimalVector.this.precision, DecimalVector.this.scale);
}
public TransferImpl(Field field, BufferAllocator allocator) {
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestDecimal256Vector.java b/vector/src/test/java/org/apache/arrow/vector/TestDecimal256Vector.java
index c155ab98fa..b995dc5d92 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestDecimal256Vector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestDecimal256Vector.java
@@ -26,6 +26,7 @@
import org.apache.arrow.memory.ArrowBuf;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.FieldType;
import org.apache.arrow.vector.util.TransferPair;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -375,6 +376,33 @@ public void testGetTransferPairWithField() {
assertSame(fromVector.getField(), toVector.getField());
}
+ @Test
+ public void testGetTransferPairWithoutField() {
+ final Decimal256Vector fromVector = new Decimal256Vector("decimal", allocator, 10, scale);
+ final TransferPair transferPair =
+ fromVector.getTransferPair(fromVector.getField().getName(), allocator);
+ final Decimal256Vector toVector = (Decimal256Vector) transferPair.getTo();
+ // A new Field created inside a new vector should reuse the field type (should be the same in
+ // memory as the original Field's field type).
+ assertSame(fromVector.getField().getFieldType(), toVector.getField().getFieldType());
+ }
+
+ @Test
+ public void testGetTransferPairWithoutFieldNonNullable() {
+ final FieldType decimal256NonNullableType =
+ new FieldType(
+ false, new ArrowType.Decimal(10, scale, Decimal256Vector.TYPE_WIDTH * 8), null);
+ final Decimal256Vector fromVector =
+ new Decimal256Vector("decimal", decimal256NonNullableType, allocator);
+ final TransferPair transferPair =
+ fromVector.getTransferPair(fromVector.getField().getName(), allocator);
+ final Decimal256Vector toVector = (Decimal256Vector) transferPair.getTo();
+ // A new Field created inside a new vector should reuse the field type (should be the same in
+ // memory as the original Field's field type).
+ assertSame(fromVector.getField().getFieldType(), toVector.getField().getFieldType());
+ assertSame(decimal256NonNullableType, toVector.getField().getFieldType());
+ }
+
private void verifyWritingArrowBufWithBigEndianBytes(
Decimal256Vector decimalVector, ArrowBuf buf, BigDecimal[] expectedValues, int length) {
decimalVector.allocateNew();
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestDecimalVector.java b/vector/src/test/java/org/apache/arrow/vector/TestDecimalVector.java
index d5310bad0e..85c11e8f3d 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestDecimalVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestDecimalVector.java
@@ -26,6 +26,7 @@
import org.apache.arrow.memory.ArrowBuf;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.FieldType;
import org.apache.arrow.vector.util.TransferPair;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -371,6 +372,31 @@ public void testGetTransferPairWithField() {
assertSame(fromVector.getField(), toVector.getField());
}
+ @Test
+ public void testGetTransferPairWithoutField() {
+ final DecimalVector fromVector = new DecimalVector("decimal", allocator, 10, scale);
+ final TransferPair transferPair =
+ fromVector.getTransferPair(fromVector.getField().getName(), allocator);
+ final DecimalVector toVector = (DecimalVector) transferPair.getTo();
+ // A new Field created inside a new vector should reuse the field type (should be the same in
+ // memory as the original Field's field type).
+ assertSame(fromVector.getField().getFieldType(), toVector.getField().getFieldType());
+ }
+
+ @Test
+ public void testGetTransferPairWithoutFieldNonNullable() {
+ final FieldType decimalNonNullableType =
+ new FieldType(false, new ArrowType.Decimal(10, scale), null);
+ final DecimalVector fromVector =
+ new DecimalVector("decimal", decimalNonNullableType, allocator);
+ final TransferPair transferPair =
+ fromVector.getTransferPair(fromVector.getField().getName(), allocator);
+ final DecimalVector toVector = (DecimalVector) transferPair.getTo();
+ // A new Field created inside a new vector should reuse the field type (should be the same in
+ // memory as the original Field's field type).
+ assertSame(fromVector.getField().getFieldType(), toVector.getField().getFieldType());
+ }
+
private void verifyWritingArrowBufWithBigEndianBytes(
DecimalVector decimalVector, ArrowBuf buf, BigDecimal[] expectedValues, int length) {
decimalVector.allocateNew();
From 8ffc1d3eb58bbe4fc023d2222cc4439dace7e8ea Mon Sep 17 00:00:00 2001
From: David Li
Date: Tue, 8 Apr 2025 12:17:11 +0900
Subject: [PATCH 002/271] MINOR: Fix JNI code after upstream DCHECK change
(#706)
## What's Changed
Upstream renamed the public DCHECK macros to ARROW_DCHECK
(https://github.com/apache/arrow/pull/46015)
---
.github/workflows/rc.yml | 5 +++++
dataset/src/main/cpp/jni_util.cc | 4 ++--
gandiva/src/main/cpp/expression_registry_helper.cc | 2 +-
gandiva/src/main/cpp/jni_common.cc | 2 +-
4 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/rc.yml b/.github/workflows/rc.yml
index 5b78cc9395..e039306ec3 100644
--- a/.github/workflows/rc.yml
+++ b/.github/workflows/rc.yml
@@ -261,6 +261,11 @@ jobs:
# bundled Protobuf.
brew uninstall protobuf
+ # We need Flatbuffers 24, not the latest version
+ # Homebrew does not offer older versions, so remove the Homebrew
+ # package and rely on Arrow using a bundled version instead
+ brew uninstall flatbuffers
+
brew bundle --file=Brewfile
- name: Prepare ccache
run: |
diff --git a/dataset/src/main/cpp/jni_util.cc b/dataset/src/main/cpp/jni_util.cc
index 1fd15696e6..35bfb328f0 100644
--- a/dataset/src/main/cpp/jni_util.cc
+++ b/dataset/src/main/cpp/jni_util.cc
@@ -187,7 +187,7 @@ ReservationListenableMemoryPool::~ReservationListenableMemoryPool() {}
std::string Describe(JNIEnv* env, jthrowable t) {
jclass describer_class =
env->FindClass("org/apache/arrow/dataset/jni/JniExceptionDescriber");
- DCHECK_NE(describer_class, nullptr);
+ ARROW_DCHECK_NE(describer_class, nullptr);
jmethodID describe_method = env->GetStaticMethodID(
describer_class, "describe", "(Ljava/lang/Throwable;)Ljava/lang/String;");
std::string description = JStringToCString(
@@ -197,7 +197,7 @@ std::string Describe(JNIEnv* env, jthrowable t) {
bool IsErrorInstanceOf(JNIEnv* env, jthrowable t, std::string class_name) {
jclass java_class = env->FindClass(class_name.c_str());
- DCHECK_NE(java_class, nullptr) << "Could not find Java class " << class_name;
+ ARROW_DCHECK_NE(java_class, nullptr) << "Could not find Java class " << class_name;
return env->IsInstanceOf(t, java_class);
}
diff --git a/gandiva/src/main/cpp/expression_registry_helper.cc b/gandiva/src/main/cpp/expression_registry_helper.cc
index 66b97c8b9e..21077ff1db 100644
--- a/gandiva/src/main/cpp/expression_registry_helper.cc
+++ b/gandiva/src/main/cpp/expression_registry_helper.cc
@@ -138,7 +138,7 @@ void ArrowToProtobuf(DataTypePtr type, gandiva::types::ExtGandivaType* gandiva_d
default:
// un-supported types. test ensures that
// when one of these are added build breaks.
- DCHECK(false);
+ ARROW_DCHECK(false);
}
}
diff --git a/gandiva/src/main/cpp/jni_common.cc b/gandiva/src/main/cpp/jni_common.cc
index ec1bb76234..2851250072 100644
--- a/gandiva/src/main/cpp/jni_common.cc
+++ b/gandiva/src/main/cpp/jni_common.cc
@@ -751,7 +751,7 @@ Status JavaResizableBuffer::Resize(const int64_t new_size, bool shrink_to_fit) {
}
RETURN_NOT_OK(Reserve(new_size));
- DCHECK_GE(capacity_, new_size);
+ ARROW_DCHECK_GE(capacity_, new_size);
size_ = new_size;
return Status::OK();
}
From 7bd2b0d3853b6ef1066aa2e9819743f441082dd8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 8 Apr 2025 01:21:37 -0400
Subject: [PATCH 003/271] MINOR: Bump checker.framework.version from 3.49.1 to
3.49.2 (#703)
Bumps `checker.framework.version` from 3.49.1 to 3.49.2.
Updates `org.checkerframework:checker-qual` from 3.49.1 to 3.49.2
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index df32105804..5462d1416e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -109,7 +109,7 @@ under the License.
10.22.0true2.37.0
- 3.49.1
+ 3.49.21.5.18none-Xdoclint:none
From 8d55fd1b52863bc0d2606c623d34e3eaa3be7b3d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 8 Apr 2025 01:26:19 -0400
Subject: [PATCH 004/271] MINOR: Bump org.mockito:mockito-bom from 5.16.1 to
5.17.0 (#701)
Bumps [org.mockito:mockito-bom](https://github.com/mockito/mockito) from
5.16.1 to 5.17.0.
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 5462d1416e..9e6c8b782e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,7 +103,7 @@ under the License.
3.4.125.2.101.12.0
- 5.16.1
+ 5.17.0210.22.0
From 42a126fd88a1430b474fb4571b0c5b99c83ee770 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 8 Apr 2025 01:47:44 -0400
Subject: [PATCH 005/271] MINOR: Bump com.puppycrawl.tools:checkstyle from
10.22.0 to 10.23.0 (#702)
Bumps
[com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle)
from 10.22.0 to 10.23.0.
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 9e6c8b782e..cb189dc8f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,7 +106,7 @@ under the License.
5.17.02
- 10.22.0
+ 10.23.0true2.37.03.49.2
From f92585c272ea4b2db5b1717f6642f3edc8f99903 Mon Sep 17 00:00:00 2001
From: Kristin Cowalcijk
Date: Tue, 8 Apr 2025 14:11:32 +0800
Subject: [PATCH 006/271] GH-704: Fix initialization of offset buffer when
exporting VarChar vectors through C Data Interface (#705)
## What's Changed
This patch fixes the initialization of offset buffers when exporting
variable width arrays through Arrow C Data Interface. The original code
incorrectly mess up with the member `this.offsetBuffer` while we should
actually initialize the newly allocated offsetBuffer. I think the diff
itself will be quite self-explanatory.
Closes #704 and probably #88 .
---
.../org/apache/arrow/c/RoundtripTest.java | 23 +++++++++++++++++++
.../vector/BaseLargeVariableWidthVector.java | 2 +-
.../arrow/vector/BaseVariableWidthVector.java | 2 +-
3 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/c/src/test/java/org/apache/arrow/c/RoundtripTest.java b/c/src/test/java/org/apache/arrow/c/RoundtripTest.java
index 67ab282de5..6d68449c0b 100644
--- a/c/src/test/java/org/apache/arrow/c/RoundtripTest.java
+++ b/c/src/test/java/org/apache/arrow/c/RoundtripTest.java
@@ -38,6 +38,8 @@
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.memory.util.hash.ArrowBufHasher;
+import org.apache.arrow.vector.BaseLargeVariableWidthVector;
+import org.apache.arrow.vector.BaseVariableWidthVector;
import org.apache.arrow.vector.BigIntVector;
import org.apache.arrow.vector.BitVector;
import org.apache.arrow.vector.DateDayVector;
@@ -181,6 +183,13 @@ boolean roundtrip(FieldVector vector, Class> clazz) {
clazz.isInstance(imported),
String.format("expected %s but was %s", clazz, imported.getClass()));
result = VectorEqualsVisitor.vectorEquals(vector, imported);
+
+ if (imported instanceof BaseVariableWidthVector
+ || imported instanceof BaseLargeVariableWidthVector) {
+ ArrowBuf offsetBuffer = imported.getOffsetBuffer();
+ assertTrue(offsetBuffer.capacity() > 0);
+ assertEquals(0, offsetBuffer.getInt(0));
+ }
}
// Check that the ref counts of the buffers are the same after the roundtrip
@@ -602,6 +611,13 @@ public void testVarCharVector() {
}
}
+ @Test
+ public void testEmptyVarCharVector() {
+ try (final VarCharVector vector = new VarCharVector("v", allocator)) {
+ assertTrue(roundtrip(vector, VarCharVector.class));
+ }
+ }
+
@Test
public void testLargeVarBinaryVector() {
try (final LargeVarBinaryVector vector = new LargeVarBinaryVector("", allocator)) {
@@ -635,6 +651,13 @@ public void testLargeVarCharVector() {
}
}
+ @Test
+ public void testEmptyLargeVarCharVector() {
+ try (final LargeVarCharVector vector = new LargeVarCharVector("v", allocator)) {
+ assertTrue(roundtrip(vector, LargeVarCharVector.class));
+ }
+ }
+
@Test
public void testListVector() {
try (final ListVector vector = ListVector.empty("v", allocator)) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
index 552a896ea8..7e0d0affc6 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
@@ -496,7 +496,7 @@ private void allocateBytes(final long valueBufferSize, final int valueCount) {
private ArrowBuf allocateOffsetBuffer(final long size) {
ArrowBuf offsetBuffer = allocator.buffer(size);
offsetBuffer.readerIndex(0);
- initOffsetBuffer();
+ offsetBuffer.setZero(0, offsetBuffer.capacity());
return offsetBuffer;
}
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
index aaccec602f..7b8d2cdfda 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
@@ -514,7 +514,7 @@ private ArrowBuf allocateOffsetBuffer(final long size) {
final int curSize = (int) size;
ArrowBuf offsetBuffer = allocator.buffer(curSize);
offsetBuffer.readerIndex(0);
- initOffsetBuffer();
+ offsetBuffer.setZero(0, offsetBuffer.capacity());
return offsetBuffer;
}
From 222f30e75d079ae5005cfc5764f4c905eaa2ca5c Mon Sep 17 00:00:00 2001
From: David Li
Date: Tue, 8 Apr 2025 15:58:08 +0900
Subject: [PATCH 007/271] GH-494: [Flight] Allow configuring connect timeout in
JDBC (#495)
Allow configuring the connect timeout via `connectTimeoutMs` so that the
driver doesn't wait so long before giving up on unreachable locations.
Fixes #494.
---
flight/flight-sql-jdbc-core/pom.xml | 15 ++
.../driver/jdbc/ArrowFlightConnection.java | 1 +
.../client/ArrowFlightSqlClientHandler.java | 52 +++++--
.../ArrowFlightConnectionConfigImpl.java | 15 +-
.../arrow/driver/jdbc/ResultSetTest.java | 140 +++++++++++++++++-
...rrowFlightSqlClientHandlerBuilderTest.java | 1 +
.../ArrowFlightConnectionConfigImplTest.java | 32 +++-
.../jdbc/utils/FallbackFlightSqlProducer.java | 10 ++
8 files changed, 240 insertions(+), 26 deletions(-)
diff --git a/flight/flight-sql-jdbc-core/pom.xml b/flight/flight-sql-jdbc-core/pom.xml
index 15368e918d..3362b60a8e 100644
--- a/flight/flight-sql-jdbc-core/pom.xml
+++ b/flight/flight-sql-jdbc-core/pom.xml
@@ -47,6 +47,21 @@ under the License.
+
+ io.grpc
+ grpc-api
+
+
+
+ io.grpc
+ grpc-netty
+
+
+
+ io.netty
+ netty-transport
+
+
org.apache.arrowarrow-memory-core
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightConnection.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightConnection.java
index c1b1c8f8e6..cf9804d68b 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightConnection.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowFlightConnection.java
@@ -113,6 +113,7 @@ private static ArrowFlightSqlClientHandler createNewClientHandler(
.withRetainCookies(config.retainCookies())
.withRetainAuth(config.retainAuth())
.withCatalog(config.getCatalog())
+ .withConnectTimeout(config.getConnectTimeout())
.build();
} catch (final SQLException e) {
try {
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandler.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandler.java
index 0e9c79a090..cbbe223eb8 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandler.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandler.java
@@ -17,10 +17,13 @@
package org.apache.arrow.driver.jdbc.client;
import com.google.common.collect.ImmutableMap;
+import io.grpc.netty.NettyChannelBuilder;
+import io.netty.channel.ChannelOption;
import java.io.IOException;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.sql.SQLException;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -36,6 +39,7 @@
import org.apache.arrow.flight.FlightClient;
import org.apache.arrow.flight.FlightClientMiddleware;
import org.apache.arrow.flight.FlightEndpoint;
+import org.apache.arrow.flight.FlightGrpcUtils;
import org.apache.arrow.flight.FlightInfo;
import org.apache.arrow.flight.FlightRuntimeException;
import org.apache.arrow.flight.FlightStatusCode;
@@ -50,6 +54,7 @@
import org.apache.arrow.flight.auth2.ClientIncomingAuthHeaderMiddleware;
import org.apache.arrow.flight.client.ClientCookieMiddleware;
import org.apache.arrow.flight.grpc.CredentialCallOption;
+import org.apache.arrow.flight.grpc.NettyClientBuilder;
import org.apache.arrow.flight.sql.FlightSqlClient;
import org.apache.arrow.flight.sql.impl.FlightSql.SqlInfo;
import org.apache.arrow.flight.sql.util.TableRef;
@@ -138,12 +143,11 @@ public List getStreams(final FlightInfo flightInfo)
// Clone the builder and then set the new endpoint on it.
// GH-38574: Currently a new FlightClient will be made for each partition that returns a
- // non-empty Location
- // then disposed of. It may be better to cache clients because a server may report the
- // same Locations.
- // It would also be good to identify when the reported location is the same as the
- // original connection's
- // Location and skip creating a FlightClient in that scenario.
+ // non-empty Location then disposed of. It may be better to cache clients because a server
+ // may report the same Locations. It would also be good to identify when the reported
+ // location
+ // is the same as the original connection's Location and skip creating a FlightClient in
+ // that scenario.
List exceptions = new ArrayList<>();
CloseableEndpointStreamPair stream = null;
for (Location location : endpoint.getLocations()) {
@@ -158,7 +162,8 @@ public List getStreams(final FlightInfo flightInfo)
new Builder(ArrowFlightSqlClientHandler.this.builder)
.withHost(endpointUri.getHost())
.withPort(endpointUri.getPort())
- .withEncryption(endpointUri.getScheme().equals(LocationSchemes.GRPC_TLS));
+ .withEncryption(endpointUri.getScheme().equals(LocationSchemes.GRPC_TLS))
+ .withConnectTimeout(builder.connectTimeout);
ArrowFlightSqlClientHandler endpointHandler = null;
try {
@@ -177,6 +182,7 @@ public List getStreams(final FlightInfo flightInfo)
exceptions.add(ex);
continue;
}
+
break;
}
if (stream != null) {
@@ -543,6 +549,8 @@ public static final class Builder {
@VisibleForTesting Optional catalog = Optional.empty();
+ @VisibleForTesting @Nullable Duration connectTimeout;
+
// These two middleware are for internal use within build() and should not be exposed by builder
// APIs.
// Note that these middleware may not necessarily be registered.
@@ -825,6 +833,19 @@ public Builder withCatalog(@Nullable final String catalog) {
return this;
}
+ public Builder withConnectTimeout(Duration connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ return this;
+ }
+
+ /** Get the location that this client will connect to. */
+ public Location getLocation() {
+ if (useEncryption) {
+ return Location.forGrpcTls(host, port);
+ }
+ return Location.forGrpcInsecure(host, port);
+ }
+
/**
* Builds a new {@link ArrowFlightSqlClientHandler} from the provided fields.
*
@@ -845,17 +866,15 @@ public ArrowFlightSqlClientHandler build() throws SQLException {
if (isUsingUserPasswordAuth) {
buildTimeMiddlewareFactories.add(authFactory);
}
- final FlightClient.Builder clientBuilder = FlightClient.builder().allocator(allocator);
+ final NettyClientBuilder clientBuilder = new NettyClientBuilder();
+ clientBuilder.allocator(allocator);
buildTimeMiddlewareFactories.add(new ClientCookieMiddleware.Factory());
buildTimeMiddlewareFactories.forEach(clientBuilder::intercept);
- Location location;
if (useEncryption) {
- location = Location.forGrpcTls(host, port);
clientBuilder.useTls();
- } else {
- location = Location.forGrpcInsecure(host, port);
}
+ Location location = getLocation();
clientBuilder.location(location);
if (useEncryption) {
@@ -883,7 +902,14 @@ public ArrowFlightSqlClientHandler build() throws SQLException {
}
}
- client = clientBuilder.build();
+ NettyChannelBuilder channelBuilder = clientBuilder.build();
+ if (connectTimeout != null) {
+ channelBuilder.withOption(
+ ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectTimeout.toMillis());
+ }
+ client =
+ FlightGrpcUtils.createFlightClient(
+ allocator, channelBuilder.build(), clientBuilder.middleware());
final ArrayList credentialOptions = new ArrayList<>();
if (isUsingUserPasswordAuth) {
// If the authFactory has already been used for a handshake, use the existing token.
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ArrowFlightConnectionConfigImpl.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ArrowFlightConnectionConfigImpl.java
index e8bae2a207..ab6a5898b7 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ArrowFlightConnectionConfigImpl.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ArrowFlightConnectionConfigImpl.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.driver.jdbc.utils;
+import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -163,6 +164,16 @@ public String getCatalog() {
return ArrowFlightConnectionProperty.CATALOG.getString(properties);
}
+ /** The initial connect timeout. */
+ public Duration getConnectTimeout() {
+ Integer timeout = ArrowFlightConnectionProperty.CONNECT_TIMEOUT_MILLIS.getInteger(properties);
+ if (timeout == null) {
+ return Duration.ofMillis(
+ (int) ArrowFlightConnectionProperty.CONNECT_TIMEOUT_MILLIS.defaultValue());
+ }
+ return Duration.ofMillis(timeout);
+ }
+
/**
* Gets the {@link CallOption}s from this {@link ConnectionConfig}.
*
@@ -213,7 +224,9 @@ public enum ArrowFlightConnectionProperty implements ConnectionProperty {
TOKEN("token", null, Type.STRING, false),
RETAIN_COOKIES("retainCookies", true, Type.BOOLEAN, false),
RETAIN_AUTH("retainAuth", true, Type.BOOLEAN, false),
- CATALOG("catalog", null, Type.STRING, false);
+ CATALOG("catalog", null, Type.STRING, false),
+ CONNECT_TIMEOUT_MILLIS("connectTimeoutMs", 10000, Type.NUMBER, false),
+ ;
private final String camelName;
private final Object defaultValue;
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetTest.java
index a8d04dfc83..cd47408f52 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetTest.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ResultSetTest.java
@@ -25,12 +25,7 @@
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.*;
import com.google.common.collect.ImmutableSet;
import java.nio.charset.StandardCharsets;
@@ -645,6 +640,139 @@ public void testFallbackSecondFlightServer() throws Exception {
}
}
+ @Test
+ public void testFallbackUnresolvableFlightServer() throws Exception {
+ final Schema schema =
+ new Schema(
+ Collections.singletonList(Field.nullable("int_column", Types.MinorType.INT.getType())));
+ try (BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE);
+ VectorSchemaRoot resultData = VectorSchemaRoot.create(schema, allocator)) {
+ resultData.setRowCount(1);
+ ((IntVector) resultData.getVector(0)).set(0, 1);
+
+ try (final FallbackFlightSqlProducer rootProducer =
+ new FallbackFlightSqlProducer(resultData);
+ FlightServer rootServer =
+ FlightServer.builder(allocator, forGrpcInsecure("localhost", 0), rootProducer)
+ .build()
+ .start();
+ Connection newConnection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:arrow-flight-sql://%s:%d/?useEncryption=false",
+ rootServer.getLocation().getUri().getHost(), rootServer.getPort()))) {
+ // This first attempt should take a measurable amount of time.
+ long start = System.nanoTime();
+ try (Statement newStatement = newConnection.createStatement()) {
+ try (ResultSet result = newStatement.executeQuery("fallback with unresolvable")) {
+ List actualData = new ArrayList<>();
+ while (result.next()) {
+ actualData.add(result.getInt(1));
+ }
+
+ // Assert
+ assertEquals(resultData.getRowCount(), actualData.size());
+ assertTrue(actualData.contains(((IntVector) resultData.getVector(0)).get(0)));
+ }
+ }
+ long attempt1 = System.nanoTime();
+ double elapsedMs = (attempt1 - start) / 1_000_000.;
+ assertTrue(
+ elapsedMs >= 5000.,
+ String.format(
+ "Expected first attempt to hit the timeout, but only %f ms elapsed", elapsedMs));
+
+ // Once the client cache is implemented (GH-661), this second attempt should take less time,
+ // since the failure from before should be cached.
+ start = System.nanoTime();
+ try (Statement newStatement = newConnection.createStatement()) {
+ try (ResultSet result = newStatement.executeQuery("fallback with unresolvable")) {
+ List actualData = new ArrayList<>();
+ while (result.next()) {
+ actualData.add(result.getInt(1));
+ }
+
+ // Assert
+ assertEquals(resultData.getRowCount(), actualData.size());
+ assertTrue(actualData.contains(((IntVector) resultData.getVector(0)).get(0)));
+ }
+ }
+ attempt1 = System.nanoTime();
+ elapsedMs = (attempt1 - start) / 1_000_000.;
+ // TODO(GH-661): this assertion should be flipped to assertTrue.
+ assertFalse(
+ elapsedMs < 5000.,
+ String.format("Expected second attempt to be the same, but %f ms elapsed", elapsedMs));
+ }
+ }
+ }
+
+ @Test
+ public void testFallbackUnresolvableFlightServerDisableCache() throws Exception {
+ final Schema schema =
+ new Schema(
+ Collections.singletonList(Field.nullable("int_column", Types.MinorType.INT.getType())));
+ try (BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE);
+ VectorSchemaRoot resultData = VectorSchemaRoot.create(schema, allocator)) {
+ resultData.setRowCount(1);
+ ((IntVector) resultData.getVector(0)).set(0, 1);
+
+ try (final FallbackFlightSqlProducer rootProducer =
+ new FallbackFlightSqlProducer(resultData);
+ FlightServer rootServer =
+ FlightServer.builder(allocator, forGrpcInsecure("localhost", 0), rootProducer)
+ .build()
+ .start();
+ Connection newConnection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:arrow-flight-sql://%s:%d/?useEncryption=false&useClientCache=false",
+ rootServer.getLocation().getUri().getHost(), rootServer.getPort()))) {
+ // This first attempt should take a measurable amount of time.
+ long start = System.nanoTime();
+ try (Statement newStatement = newConnection.createStatement()) {
+ try (ResultSet result = newStatement.executeQuery("fallback with unresolvable")) {
+ List actualData = new ArrayList<>();
+ while (result.next()) {
+ actualData.add(result.getInt(1));
+ }
+
+ // Assert
+ assertEquals(resultData.getRowCount(), actualData.size());
+ assertTrue(actualData.contains(((IntVector) resultData.getVector(0)).get(0)));
+ }
+ }
+ long attempt1 = System.nanoTime();
+ double elapsedMs = (attempt1 - start) / 1_000_000.;
+ assertTrue(
+ elapsedMs >= 5000.,
+ String.format(
+ "Expected first attempt to hit the timeout, but only %f ms elapsed", elapsedMs));
+
+ // This second attempt should take a long time still, since we disabled the cache.
+ start = System.nanoTime();
+ try (Statement newStatement = newConnection.createStatement()) {
+ try (ResultSet result = newStatement.executeQuery("fallback with unresolvable")) {
+ List actualData = new ArrayList<>();
+ while (result.next()) {
+ actualData.add(result.getInt(1));
+ }
+
+ // Assert
+ assertEquals(resultData.getRowCount(), actualData.size());
+ assertTrue(actualData.contains(((IntVector) resultData.getVector(0)).get(0)));
+ }
+ }
+ attempt1 = System.nanoTime();
+ elapsedMs = (attempt1 - start) / 1_000_000.;
+ assertTrue(
+ elapsedMs >= 5000.,
+ String.format(
+ "Expected second attempt to hit the timeout, but only %f ms elapsed", elapsedMs));
+ }
+ }
+ }
+
@Test
public void testShouldRunSelectQueryWithEmptyVectorsEmbedded() throws Exception {
try (Statement statement = connection.createStatement();
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandlerBuilderTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandlerBuilderTest.java
index 6beaba8236..7b416638e1 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandlerBuilderTest.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/client/ArrowFlightSqlClientHandlerBuilderTest.java
@@ -147,6 +147,7 @@ public void testDefaults() {
assertNull(builder.clientCertificatePath);
assertNull(builder.clientKeyPath);
assertEquals(Optional.empty(), builder.catalog);
+ assertNull(builder.connectTimeout);
}
@Test
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ArrowFlightConnectionConfigImplTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ArrowFlightConnectionConfigImplTest.java
index 4a46b5f5be..c780d53fab 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ArrowFlightConnectionConfigImplTest.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ArrowFlightConnectionConfigImplTest.java
@@ -18,6 +18,7 @@
import static java.lang.Runtime.getRuntime;
import static org.apache.arrow.driver.jdbc.utils.ArrowFlightConnectionConfigImpl.ArrowFlightConnectionProperty.CATALOG;
+import static org.apache.arrow.driver.jdbc.utils.ArrowFlightConnectionConfigImpl.ArrowFlightConnectionProperty.CONNECT_TIMEOUT_MILLIS;
import static org.apache.arrow.driver.jdbc.utils.ArrowFlightConnectionConfigImpl.ArrowFlightConnectionProperty.HOST;
import static org.apache.arrow.driver.jdbc.utils.ArrowFlightConnectionConfigImpl.ArrowFlightConnectionProperty.PASSWORD;
import static org.apache.arrow.driver.jdbc.utils.ArrowFlightConnectionConfigImpl.ArrowFlightConnectionProperty.PORT;
@@ -27,6 +28,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
+import java.time.Duration;
import java.util.Properties;
import java.util.Random;
import java.util.function.Function;
@@ -59,49 +61,67 @@ public void setUp() {
public void testGetProperty(
ArrowFlightConnectionProperty property,
Object value,
+ Object expected,
Function configFunction) {
properties.put(property.camelName(), value);
arrowFlightConnectionConfigFunction = configFunction;
- assertThat(configFunction.apply(arrowFlightConnectionConfig), is(value));
- assertThat(arrowFlightConnectionConfigFunction.apply(arrowFlightConnectionConfig), is(value));
+ assertThat(configFunction.apply(arrowFlightConnectionConfig), is(expected));
+ assertThat(
+ arrowFlightConnectionConfigFunction.apply(arrowFlightConnectionConfig), is(expected));
}
public static Stream provideParameters() {
+ int port = RANDOM.nextInt(Short.toUnsignedInt(Short.MAX_VALUE));
+ boolean useEncryption = RANDOM.nextBoolean();
+ int threadPoolSize = RANDOM.nextInt(getRuntime().availableProcessors());
return Stream.of(
Arguments.of(
HOST,
"host",
+ "host",
(Function)
ArrowFlightConnectionConfigImpl::getHost),
Arguments.of(
PORT,
- RANDOM.nextInt(Short.toUnsignedInt(Short.MAX_VALUE)),
+ port,
+ port,
(Function)
ArrowFlightConnectionConfigImpl::getPort),
Arguments.of(
USER,
"user",
+ "user",
(Function)
ArrowFlightConnectionConfigImpl::getUser),
Arguments.of(
PASSWORD,
"password",
+ "password",
(Function)
ArrowFlightConnectionConfigImpl::getPassword),
Arguments.of(
USE_ENCRYPTION,
- RANDOM.nextBoolean(),
+ useEncryption,
+ useEncryption,
(Function)
ArrowFlightConnectionConfigImpl::useEncryption),
Arguments.of(
THREAD_POOL_SIZE,
- RANDOM.nextInt(getRuntime().availableProcessors()),
+ threadPoolSize,
+ threadPoolSize,
(Function)
ArrowFlightConnectionConfigImpl::threadPoolSize),
Arguments.of(
CATALOG,
"catalog",
+ "catalog",
+ (Function)
+ ArrowFlightConnectionConfigImpl::getCatalog),
+ Arguments.of(
+ CONNECT_TIMEOUT_MILLIS,
+ 5000,
+ Duration.ofMillis(5000),
(Function)
- ArrowFlightConnectionConfigImpl::getCatalog));
+ ArrowFlightConnectionConfigImpl::getConnectTimeout));
}
}
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/FallbackFlightSqlProducer.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/FallbackFlightSqlProducer.java
index 9aa257172c..670b9e3be0 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/FallbackFlightSqlProducer.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/FallbackFlightSqlProducer.java
@@ -109,6 +109,16 @@ private FlightInfo getFlightInfo(FlightDescriptor descriptor, String query) {
Location.forGrpcInsecure("localhost", 9999),
Location.reuseConnection())
.build());
+ } else if (query.equals("fallback with unresolvable")) {
+ endpoints =
+ Collections.singletonList(
+ FlightEndpoint.builder(
+ ticket,
+ // Inaccessible IP
+ // https://stackoverflow.com/questions/10456044/what-is-a-good-invalid-ip-address-to-use-for-unit-tests
+ Location.forGrpcInsecure("203.0.113.0", 9999),
+ Location.reuseConnection())
+ .build());
} else {
throw CallStatus.UNIMPLEMENTED.withDescription(query).toRuntimeException();
}
From 437612c07e04fb0283010961a836de22521fa759 Mon Sep 17 00:00:00 2001
From: Ivan Chesnov
Date: Wed, 9 Apr 2025 15:19:25 +0300
Subject: [PATCH 008/271] GH-87: [Vector] Add ExtensionWriter (#697)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Based on changes from https://github.com/apache/arrow/pull/41731.
## What's Changed
Added writer ExtensionWriter with 3 methods:
- write method for writing values from Extension holders;
- writeExtensionType method for writing values (arguments is Object
because we don't know exact type);
- addExtensionTypeFactory method - because the exact vector and value
type are unknown, the user should create their own extension type
vector, write for it, and ExtensionTypeFactory, which should map the
vector and writer.
Closes #87.
Co-authored-by: Finn Völkel
---
.../templates/AbstractFieldWriter.java | 22 ++++
.../AbstractPromotableFieldWriter.java | 10 ++
.../main/codegen/templates/BaseWriter.java | 31 +++++
.../codegen/templates/PromotableWriter.java | 14 +++
.../main/codegen/templates/StructWriters.java | 26 ++++
.../codegen/templates/UnionListWriter.java | 23 ++++
.../codegen/templates/UnionMapWriter.java | 12 ++
.../main/codegen/templates/UnionWriter.java | 20 +++
.../impl/AbstractExtensionTypeWriter.java | 66 ++++++++++
.../impl/ExtensionTypeWriterFactory.java | 38 ++++++
.../complex/impl/UnionExtensionWriter.java | 79 ++++++++++++
.../vector/complex/writer/FieldWriter.java | 4 +-
.../arrow/vector/holders/ExtensionHolder.java | 22 ++++
.../apache/arrow/vector/TestStructVector.java | 37 ++++++
.../org/apache/arrow/vector/UuidVector.java | 114 ++++++++++++++++++
.../complex/impl/TestPromotableWriter.java | 29 +++++
.../complex/impl/UuidWriterFactory.java | 31 +++++
.../vector/complex/impl/UuidWriterImpl.java | 47 ++++++++
.../complex/writer/TestSimpleWriter.java | 20 +++
.../arrow/vector/holder/UuidHolder.java | 23 ++++
.../vector/types/pojo/TestExtensionType.java | 70 +----------
.../arrow/vector/types/pojo/UuidType.java | 60 +++++++++
22 files changed, 728 insertions(+), 70 deletions(-)
create mode 100644 vector/src/main/java/org/apache/arrow/vector/complex/impl/AbstractExtensionTypeWriter.java
create mode 100644 vector/src/main/java/org/apache/arrow/vector/complex/impl/ExtensionTypeWriterFactory.java
create mode 100644 vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionExtensionWriter.java
create mode 100644 vector/src/main/java/org/apache/arrow/vector/holders/ExtensionHolder.java
create mode 100644 vector/src/test/java/org/apache/arrow/vector/UuidVector.java
create mode 100644 vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java
create mode 100644 vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
create mode 100644 vector/src/test/java/org/apache/arrow/vector/holder/UuidHolder.java
create mode 100644 vector/src/test/java/org/apache/arrow/vector/types/pojo/UuidType.java
diff --git a/vector/src/main/codegen/templates/AbstractFieldWriter.java b/vector/src/main/codegen/templates/AbstractFieldWriter.java
index cc2cc618d8..ae5b97faef 100644
--- a/vector/src/main/codegen/templates/AbstractFieldWriter.java
+++ b/vector/src/main/codegen/templates/AbstractFieldWriter.java
@@ -107,6 +107,16 @@ public void endEntry() {
throw new IllegalStateException(String.format("You tried to end a map entry when you are using a ValueWriter of type %s.", this.getClass().getSimpleName()));
}
+ public void write(ExtensionHolder var1) {
+ this.fail("ExtensionType");
+ }
+ public void writeExtension(Object var1) {
+ this.fail("ExtensionType");
+ }
+ public void addExtensionTypeWriterFactory(ExtensionTypeWriterFactory var1) {
+ this.fail("ExtensionType");
+ }
+
<#list vv.types as type><#list type.minor as minor><#assign name = minor.class?cap_first />
<#assign fields = minor.fields!type.fields />
<#assign friendlyType = (minor.friendlyType!minor.boxedType!type.boxedType) />
@@ -241,6 +251,18 @@ public MapWriter map(String name, boolean keysSorted) {
fail("Map");
return null;
}
+
+ @Override
+ public ExtensionWriter extension(String name, ArrowType arrowType) {
+ fail("Extension");
+ return null;
+ }
+
+ @Override
+ public ExtensionWriter extension(ArrowType arrowType) {
+ fail("Extension");
+ return null;
+ }
<#list vv.types as type><#list type.minor as minor>
<#assign lowerName = minor.class?uncap_first />
<#if lowerName == "int" ><#assign lowerName = "integer" />#if>
diff --git a/vector/src/main/codegen/templates/AbstractPromotableFieldWriter.java b/vector/src/main/codegen/templates/AbstractPromotableFieldWriter.java
index 06cb235f7d..951edd5eee 100644
--- a/vector/src/main/codegen/templates/AbstractPromotableFieldWriter.java
+++ b/vector/src/main/codegen/templates/AbstractPromotableFieldWriter.java
@@ -293,6 +293,11 @@ public MapWriter map(boolean keysSorted) {
return getWriter(MinorType.MAP, new ArrowType.Map(keysSorted));
}
+ @Override
+ public ExtensionWriter extension(ArrowType arrowType) {
+ return getWriter(MinorType.EXTENSIONTYPE).extension(arrowType);
+ }
+
@Override
public StructWriter struct(String name) {
return getWriter(MinorType.STRUCT).struct(name);
@@ -318,6 +323,11 @@ public MapWriter map(String name, boolean keysSorted) {
return getWriter(MinorType.STRUCT).map(name, keysSorted);
}
+ @Override
+ public ExtensionWriter extension(String name, ArrowType arrowType) {
+ return getWriter(MinorType.EXTENSIONTYPE).extension(name, arrowType);
+ }
+
<#list vv.types as type><#list type.minor as minor>
<#assign lowerName = minor.class?uncap_first />
<#if lowerName == "int" ><#assign lowerName = "integer" />#if>
diff --git a/vector/src/main/codegen/templates/BaseWriter.java b/vector/src/main/codegen/templates/BaseWriter.java
index e952d46f1f..78da7fddc3 100644
--- a/vector/src/main/codegen/templates/BaseWriter.java
+++ b/vector/src/main/codegen/templates/BaseWriter.java
@@ -61,6 +61,7 @@ public interface StructWriter extends BaseWriter {
void copyReaderToField(String name, FieldReader reader);
StructWriter struct(String name);
+ ExtensionWriter extension(String name, ArrowType arrowType);
ListWriter list(String name);
ListWriter listView(String name);
MapWriter map(String name);
@@ -79,6 +80,7 @@ public interface ListWriter extends BaseWriter {
ListWriter listView();
MapWriter map();
MapWriter map(boolean keysSorted);
+ ExtensionWriter extension(ArrowType arrowType);
void copyReader(FieldReader reader);
<#list vv.types as type><#list type.minor as minor>
@@ -101,6 +103,35 @@ public interface MapWriter extends ListWriter {
MapWriter value();
}
+ public interface ExtensionWriter extends BaseWriter {
+
+ /**
+ * Writes a null value.
+ */
+ void writeNull();
+
+ /**
+ * Writes value from the given extension holder.
+ *
+ * @param holder the extension holder to write
+ */
+ void write(ExtensionHolder holder);
+
+ /**
+ * Writes the given extension type value.
+ *
+ * @param value the extension type value to write
+ */
+ void writeExtension(Object value);
+
+ /**
+ * Adds the given extension type factory. This factory allows configuring writer implementations for specific ExtensionTypeVector.
+ *
+ * @param factory the extension type factory to add
+ */
+ void addExtensionTypeWriterFactory(ExtensionTypeWriterFactory factory);
+ }
+
public interface ScalarWriter extends
<#list vv.types as type><#list type.minor as minor><#assign name = minor.class?cap_first /> ${name}Writer, #list>#list> BaseWriter {}
diff --git a/vector/src/main/codegen/templates/PromotableWriter.java b/vector/src/main/codegen/templates/PromotableWriter.java
index c0e686f317..8d7d57bb9d 100644
--- a/vector/src/main/codegen/templates/PromotableWriter.java
+++ b/vector/src/main/codegen/templates/PromotableWriter.java
@@ -285,6 +285,9 @@ protected void setWriter(ValueVector v) {
case UNION:
writer = new UnionWriter((UnionVector) vector, nullableStructWriterFactory);
break;
+ case EXTENSIONTYPE:
+ writer = new UnionExtensionWriter((ExtensionTypeVector) vector);
+ break;
default:
writer = type.getNewFieldWriter(vector);
break;
@@ -316,6 +319,7 @@ protected boolean requiresArrowType(MinorType type) {
|| type == MinorType.MAP
|| type == MinorType.DURATION
|| type == MinorType.FIXEDSIZEBINARY
+ || type == MinorType.EXTENSIONTYPE
|| (type.name().startsWith("TIMESTAMP") && type.name().endsWith("TZ"));
}
@@ -536,6 +540,16 @@ public void writeLargeVarChar(String value) {
getWriter(MinorType.LARGEVARCHAR).writeLargeVarChar(value);
}
+ @Override
+ public void writeExtension(Object value) {
+ getWriter(MinorType.EXTENSIONTYPE).writeExtension(value);
+ }
+
+ @Override
+ public void addExtensionTypeWriterFactory(ExtensionTypeWriterFactory factory) {
+ getWriter(MinorType.EXTENSIONTYPE).addExtensionTypeWriterFactory(factory);
+ }
+
@Override
public void allocate() {
getWriter().allocate();
diff --git a/vector/src/main/codegen/templates/StructWriters.java b/vector/src/main/codegen/templates/StructWriters.java
index 3e6b9fd773..413f707c70 100644
--- a/vector/src/main/codegen/templates/StructWriters.java
+++ b/vector/src/main/codegen/templates/StructWriters.java
@@ -83,6 +83,9 @@ public class ${mode}StructWriter extends AbstractFieldWriter {
fields.put(handleCase(child.getName()), writer);
break;
}
+ case EXTENSIONTYPE:
+ extension(child.getName(), child.getType());
+ break;
case UNION:
FieldType fieldType = new FieldType(addVectorAsNullable, MinorType.UNION.getType(), null, null);
UnionWriter writer = new UnionWriter(container.addOrGet(child.getName(), fieldType, UnionVector.class), getNullableStructWriterFactory());
@@ -159,6 +162,29 @@ public StructWriter struct(String name) {
return writer;
}
+ @Override
+ public ExtensionWriter extension(String name, ArrowType arrowType) {
+ String finalName = handleCase(name);
+ FieldWriter writer = fields.get(finalName);
+ if(writer == null){
+ int vectorCount=container.size();
+ FieldType fieldType = new FieldType(addVectorAsNullable, arrowType, null, null);
+ ExtensionTypeVector vector = container.addOrGet(name, fieldType, ExtensionTypeVector.class);
+ writer = new PromotableWriter(vector, container, getNullableStructWriterFactory());
+ if(vectorCount != container.size()) {
+ writer.allocate();
+ }
+ writer.setPosition(idx());
+ fields.put(finalName, writer);
+ } else {
+ if (writer instanceof PromotableWriter) {
+ // ensure writers are initialized
+ ((PromotableWriter)writer).getWriter(MinorType.EXTENSIONTYPE, arrowType);
+ }
+ }
+ return (ExtensionWriter) writer;
+ }
+
@Override
public void close() throws Exception {
clear();
diff --git a/vector/src/main/codegen/templates/UnionListWriter.java b/vector/src/main/codegen/templates/UnionListWriter.java
index 3962e1d073..9424533f29 100644
--- a/vector/src/main/codegen/templates/UnionListWriter.java
+++ b/vector/src/main/codegen/templates/UnionListWriter.java
@@ -201,6 +201,17 @@ public MapWriter map(String name, boolean keysSorted) {
return mapWriter;
}
+ @Override
+ public ExtensionWriter extension(ArrowType arrowType) {
+ writer.extension(arrowType);
+ return writer;
+ }
+ @Override
+ public ExtensionWriter extension(String name, ArrowType arrowType) {
+ ExtensionWriter extensionWriter = writer.extension(name, arrowType);
+ return extensionWriter;
+ }
+
<#if listName == "LargeList">
@Override
public void startList() {
@@ -323,6 +334,18 @@ public void writeNull() {
}
}
+ @Override
+ public void writeExtension(Object value) {
+ writer.writeExtension(value);
+ }
+ @Override
+ public void addExtensionTypeWriterFactory(ExtensionTypeWriterFactory var1) {
+ writer.addExtensionTypeWriterFactory(var1);
+ }
+ public void write(ExtensionHolder var1) {
+ writer.write(var1);
+ }
+
<#list vv.types as type>
<#list type.minor as minor>
<#assign name = minor.class?cap_first />
diff --git a/vector/src/main/codegen/templates/UnionMapWriter.java b/vector/src/main/codegen/templates/UnionMapWriter.java
index 90b55cb65e..8b2f091215 100644
--- a/vector/src/main/codegen/templates/UnionMapWriter.java
+++ b/vector/src/main/codegen/templates/UnionMapWriter.java
@@ -231,4 +231,16 @@ public MapWriter map() {
return super.map();
}
}
+
+ @Override
+ public ExtensionWriter extension(ArrowType type) {
+ switch (mode) {
+ case KEY:
+ return entryWriter.extension(MapVector.KEY_NAME, type);
+ case VALUE:
+ return entryWriter.extension(MapVector.VALUE_NAME, type);
+ default:
+ return super.extension(type);
+ }
+ }
}
diff --git a/vector/src/main/codegen/templates/UnionWriter.java b/vector/src/main/codegen/templates/UnionWriter.java
index bfe97e2770..272edab17c 100644
--- a/vector/src/main/codegen/templates/UnionWriter.java
+++ b/vector/src/main/codegen/templates/UnionWriter.java
@@ -213,6 +213,10 @@ public MapWriter asMap(ArrowType arrowType) {
return getMapWriter(arrowType);
}
+ private ExtensionWriter getExtensionWriter(ArrowType arrowType) {
+ throw new UnsupportedOperationException("ExtensionTypes are not supported yet.");
+ }
+
BaseWriter getWriter(MinorType minorType) {
return getWriter(minorType, null);
}
@@ -227,6 +231,8 @@ BaseWriter getWriter(MinorType minorType, ArrowType arrowType) {
return getListViewWriter();
case MAP:
return getMapWriter(arrowType);
+ case EXTENSIONTYPE:
+ return getExtensionWriter(arrowType);
<#list vv.types as type>
<#list type.minor as minor>
<#assign name = minor.class?cap_first />
@@ -460,6 +466,20 @@ public MapWriter map(String name, boolean keysSorted) {
return getStructWriter().map(name, keysSorted);
}
+ @Override
+ public ExtensionWriter extension(ArrowType arrowType) {
+ data.setType(idx(), MinorType.EXTENSIONTYPE);
+ getListWriter().setPosition(idx());
+ return getListWriter().extension(arrowType);
+ }
+
+ @Override
+ public ExtensionWriter extension(String name, ArrowType arrowType) {
+ data.setType(idx(), MinorType.EXTENSIONTYPE);
+ getStructWriter().setPosition(idx());
+ return getStructWriter().extension(name, arrowType);
+ }
+
<#list vv.types as type><#list type.minor as minor>
<#assign lowerName = minor.class?uncap_first />
<#if lowerName == "int" ><#assign lowerName = "integer" />#if>
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/AbstractExtensionTypeWriter.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/AbstractExtensionTypeWriter.java
new file mode 100644
index 0000000000..fccff6c21f
--- /dev/null
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/AbstractExtensionTypeWriter.java
@@ -0,0 +1,66 @@
+/*
+ * 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.complex.impl;
+
+import org.apache.arrow.vector.ExtensionTypeVector;
+import org.apache.arrow.vector.types.pojo.Field;
+
+/**
+ * Base {@link AbstractFieldWriter} class for an {@link
+ * org.apache.arrow.vector.ExtensionTypeVector}.
+ *
+ * @param a specific {@link ExtensionTypeVector}.
+ */
+public class AbstractExtensionTypeWriter
+ extends AbstractFieldWriter {
+ protected final T vector;
+
+ public AbstractExtensionTypeWriter(T vector) {
+ this.vector = vector;
+ }
+
+ @Override
+ public Field getField() {
+ return this.vector.getField();
+ }
+
+ @Override
+ public int getValueCapacity() {
+ return this.vector.getValueCapacity();
+ }
+
+ @Override
+ public void allocate() {
+ this.vector.allocateNew();
+ }
+
+ @Override
+ public void close() {
+ this.vector.close();
+ }
+
+ @Override
+ public void clear() {
+ this.vector.clear();
+ }
+
+ @Override
+ public void writeNull() {
+ this.vector.setNull(getPosition());
+ this.vector.setValueCount(getPosition() + 1);
+ }
+}
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/ExtensionTypeWriterFactory.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/ExtensionTypeWriterFactory.java
new file mode 100644
index 0000000000..09f0314c5f
--- /dev/null
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/ExtensionTypeWriterFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.complex.impl;
+
+import org.apache.arrow.vector.ExtensionTypeVector;
+import org.apache.arrow.vector.complex.writer.FieldWriter;
+
+/**
+ * A factory interface for creating instances of {@link ExtensionTypeWriter}. This factory allows
+ * configuring writer implementations for specific {@link ExtensionTypeVector}.
+ *
+ * @param the type of writer implementation for a specific {@link ExtensionTypeVector}.
+ */
+public interface ExtensionTypeWriterFactory {
+
+ /**
+ * Returns an instance of the writer implementation for the given {@link ExtensionTypeVector}.
+ *
+ * @param vector the {@link ExtensionTypeVector} for which the writer implementation is to be
+ * returned.
+ * @return an instance of the writer implementation for the given {@link ExtensionTypeVector}.
+ */
+ T getWriterImpl(ExtensionTypeVector vector);
+}
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionExtensionWriter.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionExtensionWriter.java
new file mode 100644
index 0000000000..d341384bd9
--- /dev/null
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UnionExtensionWriter.java
@@ -0,0 +1,79 @@
+/*
+ * 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.complex.impl;
+
+import org.apache.arrow.vector.ExtensionTypeVector;
+import org.apache.arrow.vector.complex.writer.FieldWriter;
+import org.apache.arrow.vector.holders.ExtensionHolder;
+import org.apache.arrow.vector.types.pojo.Field;
+
+public class UnionExtensionWriter extends AbstractFieldWriter {
+ protected ExtensionTypeVector vector;
+ protected FieldWriter writer;
+
+ public UnionExtensionWriter(ExtensionTypeVector vector) {
+ this.vector = vector;
+ }
+
+ @Override
+ public void allocate() {
+ vector.allocateNew();
+ }
+
+ @Override
+ public void clear() {
+ vector.clear();
+ }
+
+ @Override
+ public int getValueCapacity() {
+ return vector.getValueCapacity();
+ }
+
+ @Override
+ public Field getField() {
+ return vector.getField();
+ }
+
+ @Override
+ public void close() throws Exception {
+ vector.close();
+ }
+
+ @Override
+ public void writeExtension(Object var1) {
+ this.writer.writeExtension(var1);
+ }
+
+ @Override
+ public void addExtensionTypeWriterFactory(ExtensionTypeWriterFactory factory) {
+ this.writer = factory.getWriterImpl(vector);
+ this.writer.setPosition(idx());
+ }
+
+ public void write(ExtensionHolder holder) {
+ this.writer.write(holder);
+ }
+
+ @Override
+ public void setPosition(int index) {
+ super.setPosition(index);
+ if (this.writer != null) {
+ this.writer.setPosition(index);
+ }
+ }
+}
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/writer/FieldWriter.java b/vector/src/main/java/org/apache/arrow/vector/complex/writer/FieldWriter.java
index 949eb35d8e..51bf106685 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/writer/FieldWriter.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/writer/FieldWriter.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.vector.complex.writer;
+import org.apache.arrow.vector.complex.writer.BaseWriter.ExtensionWriter;
import org.apache.arrow.vector.complex.writer.BaseWriter.ListWriter;
import org.apache.arrow.vector.complex.writer.BaseWriter.MapWriter;
import org.apache.arrow.vector.complex.writer.BaseWriter.ScalarWriter;
@@ -25,7 +26,8 @@
* Composite of all writer types. Writers are convenience classes for incrementally adding values to
* {@linkplain org.apache.arrow.vector.ValueVector}s.
*/
-public interface FieldWriter extends StructWriter, ListWriter, MapWriter, ScalarWriter {
+public interface FieldWriter
+ extends StructWriter, ListWriter, MapWriter, ScalarWriter, ExtensionWriter {
void allocate();
void clear();
diff --git a/vector/src/main/java/org/apache/arrow/vector/holders/ExtensionHolder.java b/vector/src/main/java/org/apache/arrow/vector/holders/ExtensionHolder.java
new file mode 100644
index 0000000000..fc7ed85878
--- /dev/null
+++ b/vector/src/main/java/org/apache/arrow/vector/holders/ExtensionHolder.java
@@ -0,0 +1,22 @@
+/*
+ * 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.holders;
+
+/** Base {@link ValueHolder} class for a {@link org.apache.arrow.vector.ExtensionTypeVector}. */
+public abstract class ExtensionHolder implements ValueHolder {
+ public int isSet;
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestStructVector.java b/vector/src/test/java/org/apache/arrow/vector/TestStructVector.java
index 4ef0fbe2d9..d40af9ae89 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestStructVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestStructVector.java
@@ -26,6 +26,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.complex.AbstractStructVector;
import org.apache.arrow.vector.complex.ListVector;
@@ -37,9 +38,11 @@
import org.apache.arrow.vector.holders.ComplexHolder;
import org.apache.arrow.vector.types.Types;
import org.apache.arrow.vector.types.Types.MinorType;
+import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.ArrowType.Struct;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.arrow.vector.types.pojo.UuidType;
import org.apache.arrow.vector.util.TransferPair;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -336,6 +339,40 @@ public void testGetTransferPairWithFieldAndCallBack() {
}
}
+ @Test
+ public void testStructVectorWithExtensionTypes() {
+ UuidType uuidType = new UuidType();
+ Field uuidField = new Field("struct_child", FieldType.nullable(uuidType), null);
+ Field structField =
+ new Field("struct", FieldType.nullable(new ArrowType.Struct()), List.of(uuidField));
+ StructVector s1 = new StructVector(structField, allocator, null);
+ StructVector s2 = (StructVector) structField.createVector(allocator);
+ s1.close();
+ s2.close();
+ }
+
+ @Test
+ public void testStructVectorTransferPairWithExtensionType() {
+ UuidType uuidType = new UuidType();
+ Field uuidField = new Field("uuid_child", FieldType.nullable(uuidType), null);
+ Field structField =
+ new Field("struct", FieldType.nullable(new ArrowType.Struct()), List.of(uuidField));
+
+ StructVector s1 = (StructVector) structField.createVector(allocator);
+ UuidVector uuidVector =
+ s1.addOrGet("uuid_child", FieldType.nullable(uuidType), UuidVector.class);
+ s1.setValueCount(1);
+ uuidVector.set(0, new UUID(1, 2));
+ s1.setIndexDefined(0);
+
+ TransferPair tp = s1.getTransferPair(structField, allocator);
+ final StructVector toVector = (StructVector) tp.getTo();
+ assertEquals(s1.getField(), toVector.getField());
+
+ s1.close();
+ toVector.close();
+ }
+
private StructVector simpleStructVector(String name, BufferAllocator allocator) {
final String INT_COL = "struct_int_child";
final String FLT_COL = "struct_flt_child";
diff --git a/vector/src/test/java/org/apache/arrow/vector/UuidVector.java b/vector/src/test/java/org/apache/arrow/vector/UuidVector.java
new file mode 100644
index 0000000000..5c90d45f60
--- /dev/null
+++ b/vector/src/test/java/org/apache/arrow/vector/UuidVector.java
@@ -0,0 +1,114 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import java.util.UUID;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.util.hash.ArrowBufHasher;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.arrow.vector.types.pojo.UuidType;
+import org.apache.arrow.vector.util.TransferPair;
+
+public class UuidVector extends ExtensionTypeVector
+ implements ValueIterableVector {
+ private final Field field;
+
+ public UuidVector(
+ String name, BufferAllocator allocator, FixedSizeBinaryVector underlyingVector) {
+ super(name, allocator, underlyingVector);
+ this.field = new Field(name, FieldType.nullable(new UuidType()), null);
+ }
+
+ public UuidVector(String name, BufferAllocator allocator) {
+ super(name, allocator, new FixedSizeBinaryVector(name, allocator, 16));
+ this.field = new Field(name, FieldType.nullable(new UuidType()), null);
+ }
+
+ @Override
+ public UUID getObject(int index) {
+ final ByteBuffer bb = ByteBuffer.wrap(getUnderlyingVector().getObject(index));
+ return new UUID(bb.getLong(), bb.getLong());
+ }
+
+ @Override
+ public int hashCode(int index) {
+ return hashCode(index, null);
+ }
+
+ @Override
+ public int hashCode(int index, ArrowBufHasher hasher) {
+ return getUnderlyingVector().hashCode(index, hasher);
+ }
+
+ public void set(int index, UUID uuid) {
+ ByteBuffer bb = ByteBuffer.allocate(16);
+ bb.putLong(uuid.getMostSignificantBits());
+ bb.putLong(uuid.getLeastSignificantBits());
+ getUnderlyingVector().set(index, bb.array());
+ }
+
+ @Override
+ public void copyFromSafe(int fromIndex, int thisIndex, ValueVector from) {
+ getUnderlyingVector()
+ .copyFromSafe(fromIndex, thisIndex, ((UuidVector) from).getUnderlyingVector());
+ }
+
+ @Override
+ public Field getField() {
+ return field;
+ }
+
+ @Override
+ public TransferPair makeTransferPair(ValueVector to) {
+ return new TransferImpl((UuidVector) to);
+ }
+
+ public void setSafe(int index, byte[] value) {
+ getUnderlyingVector().setIndexDefined(index);
+ getUnderlyingVector().setSafe(index, value);
+ }
+
+ public class TransferImpl implements TransferPair {
+ UuidVector to;
+ ValueVector targetUnderlyingVector;
+ TransferPair tp;
+
+ public TransferImpl(UuidVector to) {
+ this.to = to;
+ targetUnderlyingVector = this.to.getUnderlyingVector();
+ tp = getUnderlyingVector().makeTransferPair(targetUnderlyingVector);
+ }
+
+ public UuidVector getTo() {
+ return this.to;
+ }
+
+ public void transfer() {
+ tp.transfer();
+ }
+
+ public void splitAndTransfer(int startIndex, int length) {
+ tp.splitAndTransfer(startIndex, length);
+ }
+
+ public void copyValueSafe(int fromIndex, int toIndex) {
+ tp.copyValueSafe(fromIndex, toIndex);
+ }
+ }
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestPromotableWriter.java b/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestPromotableWriter.java
index a791e55135..1556852c5a 100644
--- a/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestPromotableWriter.java
+++ b/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestPromotableWriter.java
@@ -26,12 +26,14 @@
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
+import java.util.UUID;
import org.apache.arrow.memory.ArrowBuf;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.DecimalVector;
import org.apache.arrow.vector.DirtyRootAllocator;
import org.apache.arrow.vector.LargeVarBinaryVector;
import org.apache.arrow.vector.LargeVarCharVector;
+import org.apache.arrow.vector.UuidVector;
import org.apache.arrow.vector.VarBinaryVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.complex.ListVector;
@@ -52,6 +54,7 @@
import org.apache.arrow.vector.types.pojo.ArrowType.ArrowTypeID;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.arrow.vector.types.pojo.UuidType;
import org.apache.arrow.vector.util.DecimalUtility;
import org.apache.arrow.vector.util.Text;
import org.junit.jupiter.api.AfterEach;
@@ -776,4 +779,30 @@ public void testPromoteToUnionFromDecimal() throws Exception {
assertEquals(1, intHolder.value);
}
}
+
+ @Test
+ public void testExtensionType() throws Exception {
+ try (final NonNullableStructVector container =
+ NonNullableStructVector.empty(EMPTY_SCHEMA_PATH, allocator);
+ final UuidVector v =
+ container.addOrGet("uuid", FieldType.nullable(new UuidType()), UuidVector.class);
+ final PromotableWriter writer = new PromotableWriter(v, container)) {
+ UUID u1 = UUID.randomUUID();
+ UUID u2 = UUID.randomUUID();
+ container.allocateNew();
+ container.setValueCount(1);
+ writer.addExtensionTypeWriterFactory(new UuidWriterFactory());
+
+ writer.setPosition(0);
+ writer.writeExtension(u1);
+ writer.setPosition(1);
+ writer.writeExtension(u2);
+
+ container.setValueCount(2);
+
+ UuidVector uuidVector = (UuidVector) container.getChild("uuid");
+ assertEquals(u1, uuidVector.getObject(0));
+ assertEquals(u2, uuidVector.getObject(1));
+ }
+ }
}
diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java b/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java
new file mode 100644
index 0000000000..1b1bf4e6e4
--- /dev/null
+++ b/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.complex.impl;
+
+import org.apache.arrow.vector.ExtensionTypeVector;
+import org.apache.arrow.vector.UuidVector;
+
+public class UuidWriterFactory implements ExtensionTypeWriterFactory {
+
+ @Override
+ public AbstractFieldWriter getWriterImpl(ExtensionTypeVector extensionTypeVector) {
+ if (extensionTypeVector instanceof UuidVector) {
+ return new UuidWriterImpl((UuidVector) extensionTypeVector);
+ }
+ return null;
+ }
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java b/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
new file mode 100644
index 0000000000..68029b1df5
--- /dev/null
+++ b/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
@@ -0,0 +1,47 @@
+/*
+ * 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.complex.impl;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+import org.apache.arrow.vector.UuidVector;
+import org.apache.arrow.vector.holder.UuidHolder;
+import org.apache.arrow.vector.holders.ExtensionHolder;
+
+public class UuidWriterImpl extends AbstractExtensionTypeWriter {
+
+ public UuidWriterImpl(UuidVector vector) {
+ super(vector);
+ }
+
+ @Override
+ public void writeExtension(Object value) {
+ UUID uuid = (UUID) value;
+ ByteBuffer bb = ByteBuffer.allocate(16);
+ bb.putLong(uuid.getMostSignificantBits());
+ bb.putLong(uuid.getLeastSignificantBits());
+ vector.setSafe(getPosition(), bb.array());
+ vector.setValueCount(getPosition() + 1);
+ }
+
+ @Override
+ public void write(ExtensionHolder holder) {
+ UuidHolder uuidHolder = (UuidHolder) holder;
+ vector.setSafe(getPosition(), uuidHolder.value);
+ vector.setValueCount(getPosition() + 1);
+ }
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestSimpleWriter.java b/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestSimpleWriter.java
index 5bb5962704..bf1b9b0dfa 100644
--- a/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestSimpleWriter.java
+++ b/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestSimpleWriter.java
@@ -20,16 +20,20 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.nio.ByteBuffer;
+import java.util.UUID;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.LargeVarBinaryVector;
import org.apache.arrow.vector.LargeVarCharVector;
+import org.apache.arrow.vector.UuidVector;
import org.apache.arrow.vector.VarBinaryVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.complex.impl.LargeVarBinaryWriterImpl;
import org.apache.arrow.vector.complex.impl.LargeVarCharWriterImpl;
+import org.apache.arrow.vector.complex.impl.UuidWriterImpl;
import org.apache.arrow.vector.complex.impl.VarBinaryWriterImpl;
import org.apache.arrow.vector.complex.impl.VarCharWriterImpl;
+import org.apache.arrow.vector.holder.UuidHolder;
import org.apache.arrow.vector.util.Text;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -184,4 +188,20 @@ public void testWriteTextToLargeVarChar() throws Exception {
assertEquals(input, result);
}
}
+
+ @Test
+ public void testWriteToExtensionVector() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector)) {
+ UUID uuid = UUID.randomUUID();
+ ByteBuffer bb = ByteBuffer.allocate(16);
+ bb.putLong(uuid.getMostSignificantBits());
+ bb.putLong(uuid.getLeastSignificantBits());
+ UuidHolder holder = new UuidHolder();
+ holder.value = bb.array();
+ writer.write(holder);
+ UUID result = vector.getObject(0);
+ assertEquals(uuid, result);
+ }
+ }
}
diff --git a/vector/src/test/java/org/apache/arrow/vector/holder/UuidHolder.java b/vector/src/test/java/org/apache/arrow/vector/holder/UuidHolder.java
new file mode 100644
index 0000000000..207b0951a7
--- /dev/null
+++ b/vector/src/test/java/org/apache/arrow/vector/holder/UuidHolder.java
@@ -0,0 +1,23 @@
+/*
+ * 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.holder;
+
+import org.apache.arrow.vector.holders.ExtensionHolder;
+
+public class UuidHolder extends ExtensionHolder {
+ public byte[] value;
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/types/pojo/TestExtensionType.java b/vector/src/test/java/org/apache/arrow/vector/types/pojo/TestExtensionType.java
index 8f54a6e5d7..d24708d66c 100644
--- a/vector/src/test/java/org/apache/arrow/vector/types/pojo/TestExtensionType.java
+++ b/vector/src/test/java/org/apache/arrow/vector/types/pojo/TestExtensionType.java
@@ -41,6 +41,7 @@
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.FixedSizeBinaryVector;
import org.apache.arrow.vector.Float4Vector;
+import org.apache.arrow.vector.UuidVector;
import org.apache.arrow.vector.ValueIterableVector;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.compare.Range;
@@ -295,75 +296,6 @@ public void testVectorCompare() {
}
}
- static class UuidType extends ExtensionType {
-
- @Override
- public ArrowType storageType() {
- return new ArrowType.FixedSizeBinary(16);
- }
-
- @Override
- public String extensionName() {
- return "uuid";
- }
-
- @Override
- public boolean extensionEquals(ExtensionType other) {
- return other instanceof UuidType;
- }
-
- @Override
- public ArrowType deserialize(ArrowType storageType, String serializedData) {
- if (!storageType.equals(storageType())) {
- throw new UnsupportedOperationException(
- "Cannot construct UuidType from underlying type " + storageType);
- }
- return new UuidType();
- }
-
- @Override
- public String serialize() {
- return "";
- }
-
- @Override
- public FieldVector getNewVector(String name, FieldType fieldType, BufferAllocator allocator) {
- return new UuidVector(name, allocator, new FixedSizeBinaryVector(name, allocator, 16));
- }
- }
-
- static class UuidVector extends ExtensionTypeVector
- implements ValueIterableVector {
-
- public UuidVector(
- String name, BufferAllocator allocator, FixedSizeBinaryVector underlyingVector) {
- super(name, allocator, underlyingVector);
- }
-
- @Override
- public UUID getObject(int index) {
- final ByteBuffer bb = ByteBuffer.wrap(getUnderlyingVector().getObject(index));
- return new UUID(bb.getLong(), bb.getLong());
- }
-
- @Override
- public int hashCode(int index) {
- return hashCode(index, null);
- }
-
- @Override
- public int hashCode(int index, ArrowBufHasher hasher) {
- return getUnderlyingVector().hashCode(index, hasher);
- }
-
- public void set(int index, UUID uuid) {
- ByteBuffer bb = ByteBuffer.allocate(16);
- bb.putLong(uuid.getMostSignificantBits());
- bb.putLong(uuid.getLeastSignificantBits());
- getUnderlyingVector().set(index, bb.array());
- }
- }
-
static class LocationType extends ExtensionType {
@Override
diff --git a/vector/src/test/java/org/apache/arrow/vector/types/pojo/UuidType.java b/vector/src/test/java/org/apache/arrow/vector/types/pojo/UuidType.java
new file mode 100644
index 0000000000..5e2bd8881b
--- /dev/null
+++ b/vector/src/test/java/org/apache/arrow/vector/types/pojo/UuidType.java
@@ -0,0 +1,60 @@
+/*
+ * 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.types.pojo;
+
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.vector.FieldVector;
+import org.apache.arrow.vector.FixedSizeBinaryVector;
+import org.apache.arrow.vector.UuidVector;
+import org.apache.arrow.vector.types.pojo.ArrowType.ExtensionType;
+
+public class UuidType extends ExtensionType {
+
+ @Override
+ public ArrowType storageType() {
+ return new ArrowType.FixedSizeBinary(16);
+ }
+
+ @Override
+ public String extensionName() {
+ return "uuid";
+ }
+
+ @Override
+ public boolean extensionEquals(ExtensionType other) {
+ return other instanceof UuidType;
+ }
+
+ @Override
+ public ArrowType deserialize(ArrowType storageType, String serializedData) {
+ if (!storageType.equals(storageType())) {
+ throw new UnsupportedOperationException(
+ "Cannot construct UuidType from underlying type " + storageType);
+ }
+ return new UuidType();
+ }
+
+ @Override
+ public String serialize() {
+ return "";
+ }
+
+ @Override
+ public FieldVector getNewVector(String name, FieldType fieldType, BufferAllocator allocator) {
+ return new UuidVector(name, allocator, new FixedSizeBinaryVector(name, allocator, 16));
+ }
+}
From 3e135f4f24551cd932c7ac0646ca58aa99c07a90 Mon Sep 17 00:00:00 2001
From: David Li
Date: Mon, 14 Apr 2025 11:37:26 +0900
Subject: [PATCH 009/271] GH-708: Enable GitHub discussions (#710)
## What's Changed
Enable GitHub Discussions like other Arrow repositories.
Fixes #708.
---------
Co-authored-by: Sutou Kouhei
---
.asf.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.asf.yaml b/.asf.yaml
index ead2581149..13bd59c224 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -29,12 +29,14 @@ github:
rebase: false
squash: true
features:
+ discussions: true
issues: true
protected_branches:
main:
required_linear_history: true
notifications:
commits: commits@arrow.apache.org
+ discussions: user@arrow.apache.org
issues_status: issues@arrow.apache.org
issues_comment: github@arrow.apache.org
pullrequests: github@arrow.apache.org
From 74e8981d5ba0646f2ee1dbc99364766650ad084f Mon Sep 17 00:00:00 2001
From: Pepijn Van Eeckhoudt
Date: Mon, 14 Apr 2025 07:46:56 +0200
Subject: [PATCH 010/271] GH-709: Correct length calculation of value buffers
of variable-sized arrays (#707)
## What's Changed
For variable-size binary layout arrays, BufferImportTypeVisitor
currently derives the length of the value buffer by calculating the
difference between the last and first offset. When the first offset is
not zero, this is actually incorrect and leads to out of bounds errors
when attempting to read values from the imported array.
Instead, BufferImportTypeVisitor should simply use the last offset value
as the length of the value buffer. This PR makes that change.
Just FYI, I bumped into this issue when attempting to import an array
originating from DataFusion. A test query of the form `SELECT column1
FROM VALUES ('a'), ('b'), ('c'), ('d') LIMIT 2 OFFSET 1;` returns a
slice of the full set of values. The values buffer contains all the
original values, and the offsets buffer contains 1 and 2 as values to
handle the offset from the query.
Closes #709.
---
.../org/apache/arrow/c/BufferImportTypeVisitor.java | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/c/src/main/java/org/apache/arrow/c/BufferImportTypeVisitor.java b/c/src/main/java/org/apache/arrow/c/BufferImportTypeVisitor.java
index 5ca398c9f9..10f690fc87 100644
--- a/c/src/main/java/org/apache/arrow/c/BufferImportTypeVisitor.java
+++ b/c/src/main/java/org/apache/arrow/c/BufferImportTypeVisitor.java
@@ -228,9 +228,8 @@ public List visit(ArrowType.Utf8 type) {
type,
start,
end);
- final int len = end - start;
offsets.getReferenceManager().retain();
- return Arrays.asList(maybeImportBitmap(type), offsets, importData(type, len));
+ return Arrays.asList(maybeImportBitmap(type), offsets, importData(type, end));
}
}
@@ -279,9 +278,8 @@ public List visit(ArrowType.LargeUtf8 type) {
type,
start,
end);
- final long len = end - start;
offsets.getReferenceManager().retain();
- return Arrays.asList(maybeImportBitmap(type), offsets, importData(type, len));
+ return Arrays.asList(maybeImportBitmap(type), offsets, importData(type, end));
}
}
@@ -296,9 +294,8 @@ public List visit(ArrowType.Binary type) {
type,
start,
end);
- final int len = end - start;
offsets.getReferenceManager().retain();
- return Arrays.asList(maybeImportBitmap(type), offsets, importData(type, len));
+ return Arrays.asList(maybeImportBitmap(type), offsets, importData(type, end));
}
}
@@ -320,9 +317,8 @@ public List visit(ArrowType.LargeBinary type) {
type,
start,
end);
- final long len = end - start;
offsets.getReferenceManager().retain();
- return Arrays.asList(maybeImportBitmap(type), offsets, importData(type, len));
+ return Arrays.asList(maybeImportBitmap(type), offsets, importData(type, end));
}
}
From 4b4d92851c8ecba1876a8f452279122e88e5ff8e Mon Sep 17 00:00:00 2001
From: David Li
Date: Mon, 14 Apr 2025 21:47:30 +0900
Subject: [PATCH 011/271] MINOR: Don't uninstall Flatbuffers in CI (#711)
## What's Changed
There is no more need after
https://github.com/apache/arrow/commit/32641ecfd97e302ea8119b1147d77aced96d097b
Also, disable building tests to avoid issues with Boost/Homebrew. They aren't executed anyways.
---
.github/workflows/rc.yml | 5 -----
ci/scripts/jni_macos_build.sh | 2 +-
ci/scripts/jni_manylinux_build.sh | 2 +-
3 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/rc.yml b/.github/workflows/rc.yml
index e039306ec3..5b78cc9395 100644
--- a/.github/workflows/rc.yml
+++ b/.github/workflows/rc.yml
@@ -261,11 +261,6 @@ jobs:
# bundled Protobuf.
brew uninstall protobuf
- # We need Flatbuffers 24, not the latest version
- # Homebrew does not offer older versions, so remove the Homebrew
- # package and rely on Arrow using a bundled version instead
- brew uninstall flatbuffers
-
brew bundle --file=Brewfile
- name: Prepare ccache
run: |
diff --git a/ci/scripts/jni_macos_build.sh b/ci/scripts/jni_macos_build.sh
index 65b255e2dc..f7543b6f7a 100755
--- a/ci/scripts/jni_macos_build.sh
+++ b/ci/scripts/jni_macos_build.sh
@@ -61,7 +61,7 @@ github_actions_group_begin "Building Arrow C++ libraries"
install_dir="${build_dir}/cpp-install"
: "${ARROW_ACERO:=ON}"
export ARROW_ACERO
-: "${ARROW_BUILD_TESTS:=ON}"
+: "${ARROW_BUILD_TESTS:=OFF}"
export ARROW_BUILD_TESTS
: "${ARROW_DATASET:=ON}"
export ARROW_DATASET
diff --git a/ci/scripts/jni_manylinux_build.sh b/ci/scripts/jni_manylinux_build.sh
index 148d2e02f6..a34ec0f420 100755
--- a/ci/scripts/jni_manylinux_build.sh
+++ b/ci/scripts/jni_manylinux_build.sh
@@ -57,7 +57,7 @@ devtoolset_version="$(rpm -qa "devtoolset-*-gcc" --queryformat '%{VERSION}' | gr
devtoolset_include_cpp="/opt/rh/devtoolset-${devtoolset_version}/root/usr/include/c++/${devtoolset_version}"
: "${ARROW_ACERO:=ON}"
export ARROW_ACERO
-: "${ARROW_BUILD_TESTS:=ON}"
+: "${ARROW_BUILD_TESTS:=OFF}"
export ARROW_BUILD_TESTS
: "${ARROW_DATASET:=ON}"
export ARROW_DATASET
From 4af464cc80361fdbfcb2024d3ff848b76c27524f Mon Sep 17 00:00:00 2001
From: Gabor Szadovszky
Date: Wed, 23 Apr 2025 01:38:35 +0200
Subject: [PATCH 012/271] GH-721: Allow using 1GB+ data buffers in variable
width vectors (#722)
## What's Changed
Allow actually reaching MAX_BUFFER_SIZE at reallocating variable width
vectors instead of exceeding it calculating the next power of 2.
For unit testing the maximum allocation size has be increased to 2MB -
1byte to simulate the default maximum behavior. Due to this change
needed some updates in existing unit tests because of the round ups used
at calculating the required buffer sizes.
Closes #721.
---
pom.xml | 4 ++--
.../arrow/vector/BaseVariableWidthVector.java | 7 +++++--
.../arrow/vector/BaseVariableWidthViewVector.java | 14 ++++++++++----
.../org/apache/arrow/vector/TestValueVector.java | 2 +-
.../org/apache/arrow/vector/TestVectorReAlloc.java | 12 ++++++++++++
.../arrow/vector/util/TestVectorAppender.java | 11 ++++++++++-
6 files changed, 40 insertions(+), 10 deletions(-)
diff --git a/pom.xml b/pom.xml
index cb189dc8f9..ab988c7d05 100644
--- a/pom.xml
+++ b/pom.xml
@@ -327,8 +327,8 @@ under the License.
trueUTC
- 1048576
+ which in turn can cause OOM. Using 2MB - 1byte to simulate the defaul limit of 2^31 - 1 bytes. -->
+ 2097151false
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
index 7b8d2cdfda..1609e64ca5 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
@@ -571,10 +571,13 @@ public void reallocDataBuffer(long desiredAllocSize) {
return;
}
- final long newAllocationSize = CommonUtil.nextPowerOfTwo(desiredAllocSize);
+ final long newAllocationSize =
+ Math.min(CommonUtil.nextPowerOfTwo(desiredAllocSize), MAX_BUFFER_SIZE);
assert newAllocationSize >= 1;
- checkDataBufferSize(newAllocationSize);
+ if (newAllocationSize < desiredAllocSize) {
+ checkDataBufferSize(desiredAllocSize);
+ }
final ArrowBuf newBuf = allocator.buffer(newAllocationSize);
newBuf.setBytes(0, valueBuffer, 0, valueBuffer.capacity());
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
index e0e16762f2..beda91dc3f 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
@@ -550,15 +550,18 @@ public void reallocViewBuffer(long desiredAllocSize) {
if (desiredAllocSize == 0) {
return;
}
- long newAllocationSize = CommonUtil.nextPowerOfTwo(desiredAllocSize);
+ long newAllocationSize = Math.min(CommonUtil.nextPowerOfTwo(desiredAllocSize), MAX_BUFFER_SIZE);
assert newAllocationSize >= 1;
- checkDataBufferSize(newAllocationSize);
// for each set operation, we have to allocate 16 bytes
// here we are adjusting the desired allocation-based allocation size
// to align with the 16bytes requirement.
newAllocationSize = roundUpToMultipleOf16(newAllocationSize);
+ if (newAllocationSize < desiredAllocSize) {
+ checkDataBufferSize(desiredAllocSize);
+ }
+
final ArrowBuf newBuf = allocator.buffer(newAllocationSize);
newBuf.setBytes(0, viewBuffer, 0, viewBuffer.capacity());
@@ -587,10 +590,13 @@ public void reallocViewDataBuffer(long desiredAllocSize) {
return;
}
- final long newAllocationSize = CommonUtil.nextPowerOfTwo(desiredAllocSize);
+ final long newAllocationSize =
+ Math.min(CommonUtil.nextPowerOfTwo(desiredAllocSize), MAX_BUFFER_SIZE);
assert newAllocationSize >= 1;
- checkDataBufferSize(newAllocationSize);
+ if (newAllocationSize < desiredAllocSize) {
+ checkDataBufferSize(desiredAllocSize);
+ }
final ArrowBuf newBuf = allocator.buffer(newAllocationSize);
dataBuffers.add(newBuf);
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java b/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
index 83e470ae25..daec331831 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
@@ -95,7 +95,7 @@ public void init() {
private static final byte[] STR5 = "EEE5".getBytes(utf8Charset);
private static final byte[] STR6 = "FFFFF6".getBytes(utf8Charset);
private static final int MAX_VALUE_COUNT =
- (int) (Integer.getInteger("arrow.vector.max_allocation_bytes", Integer.MAX_VALUE) / 7);
+ (int) (Integer.getInteger("arrow.vector.max_allocation_bytes", Integer.MAX_VALUE) / 9);
private static final int MAX_VALUE_COUNT_8BYTE = (int) (MAX_VALUE_COUNT / 2);
@AfterEach
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestVectorReAlloc.java b/vector/src/test/java/org/apache/arrow/vector/TestVectorReAlloc.java
index f5ec42c71c..bc47150376 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestVectorReAlloc.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestVectorReAlloc.java
@@ -24,6 +24,7 @@
import java.nio.charset.StandardCharsets;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.memory.util.CommonUtil;
import org.apache.arrow.vector.complex.DenseUnionVector;
import org.apache.arrow.vector.complex.FixedSizeListVector;
import org.apache.arrow.vector.complex.ListVector;
@@ -222,6 +223,17 @@ public void testVariableAllocateAfterReAlloc() throws Exception {
}
}
+ @Test
+ public void testVariableReAllocAbove1GB() throws Exception {
+ try (final VarCharVector vector = new VarCharVector("", allocator)) {
+ long desiredSizeAboveLastPowerOf2 =
+ CommonUtil.nextPowerOfTwo(BaseVariableWidthVector.MAX_ALLOCATION_SIZE) / 2 + 1;
+ vector.reallocDataBuffer(desiredSizeAboveLastPowerOf2);
+
+ assertTrue(vector.getDataBuffer().capacity() >= desiredSizeAboveLastPowerOf2);
+ }
+ }
+
@Test
public void testLargeVariableAllocateAfterReAlloc() throws Exception {
try (final LargeVarCharVector vector = new LargeVarCharVector("", allocator)) {
diff --git a/vector/src/test/java/org/apache/arrow/vector/util/TestVectorAppender.java b/vector/src/test/java/org/apache/arrow/vector/util/TestVectorAppender.java
index e1b3889d85..4ee9630a4d 100644
--- a/vector/src/test/java/org/apache/arrow/vector/util/TestVectorAppender.java
+++ b/vector/src/test/java/org/apache/arrow/vector/util/TestVectorAppender.java
@@ -28,6 +28,7 @@
import java.util.stream.Stream;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.memory.util.CommonUtil;
import org.apache.arrow.vector.BaseValueVector;
import org.apache.arrow.vector.BaseVariableWidthViewVector;
import org.apache.arrow.vector.BigIntVector;
@@ -309,7 +310,15 @@ public void testAppendEmptyVariableWidthVector() {
@Test
public void testAppendLargeAndSmallVariableVectorsWithinLimit() {
- int sixteenthOfMaxAllocation = Math.toIntExact(BaseValueVector.MAX_ALLOCATION_SIZE / 16);
+ // Using the max power of 2 allocation size to avoid hitting the max limit at round ups
+ long maxPowerOfTwoAllocationSize =
+ CommonUtil.nextPowerOfTwo(BaseValueVector.MAX_ALLOCATION_SIZE);
+ if (maxPowerOfTwoAllocationSize > BaseValueVector.MAX_ALLOCATION_SIZE) {
+ maxPowerOfTwoAllocationSize =
+ CommonUtil.nextPowerOfTwo(BaseValueVector.MAX_ALLOCATION_SIZE / 2);
+ }
+
+ int sixteenthOfMaxAllocation = Math.toIntExact(maxPowerOfTwoAllocationSize / 16);
try (VarCharVector target = makeVarCharVec(1, sixteenthOfMaxAllocation);
VarCharVector delta = makeVarCharVec(sixteenthOfMaxAllocation, 1)) {
new VectorAppender(delta).visit(target, null);
From d2465c3fec94ff25a966490827b57b5eafb1b637 Mon Sep 17 00:00:00 2001
From: Martin Traverse
Date: Wed, 23 Apr 2025 02:19:24 +0100
Subject: [PATCH 013/271] GH-698: Improve and fix Avro read consumers (#718)
## What's Changed
This PR relates to #698 and is the second in a series intended to
provide full Avro read / write support in native Java. It adds
round-trip tests for both schemas (Arrow schema -> Avro -> Arrow) and
data (Arrow VSR -> Avro block -> Arrow VSR). It also adds a number of
fixes and improvements to the Avro Consumers so that data arrives back
in its original form after a round trip. The main changes are:
* Added a top level method in AvroToArrow to convert Avro schema
directly to Arrow schema (this may exist elsewhere, but is needed to
provide an API that matches the logic of this implementation)
* Avro unions of [ type, null ] or [ null, type ] now have special
handling, these are interpreted as a single nullable type rather than a
union. Setting legacyMode = false in the AvroToArrowConfig object is
required to enable this behaviour, otherwise unions are interpreted
literally. Unions with more than 2 elements are always interpreted
literally (but, per #108, in practice Java's current Union
implementation is probably not usable with Avro atm).
* Added support for new logical types (decimal 256, timestamp nano and 3
local timestamp types)
* Existing timestamp-mills and timestamp-micros times now interpreted as
zone-aware (previously they were interpreted as local, but now the local
timestamp types are interpreted as local - I think this is correct per
the [Avro
spec](https://avro.apache.org/docs/1.12.0/specification/#timestamps)).
Requires setting legacyMode = false.
* Removed namespaces from generated Arrow field names in complex types.
E.g. the Avro field myNamepsace.outerRecord.structField.intField should
be called just "intField" inside the Arrow struct. This doesn't affect
the skip field logic, which still works using the qualified names. This
requires setting legacyMode = false.
* Remove unexpected metadata in generated Arrow fields (empty alias
lists and attributes interpreted as part of the field schema). This
requires setting legacyMode = false.
* Use the expected child vector names for Arrow LIST and MAP types when
reading. For LIST, the default child vector is called "$data$" which is
illegal in Avro, so the child field name is also changed to "item" in
the producers. This requires setting legacyMode = false.
Breaking changes have been removed from this PR.
Per discussion below, all breaking changes are now behind a "legacyMode"
flag in the AvroToArrowConfig object, which is enabled by default in all
the original code paths.
Closes #698 .
This change is meant to allow for round trip of schemas and individual
Avro data blocks (one Avro data block -> one VSR). File-level
capabilities are not included. I have not included anything to recycle
the VSR as part of the read API, this feels like it belongs with the
file-level piece. Also I have not done anything specific for enums /
dict encoding as of yet.
---
.../arrow/adapter/avro/ArrowToAvroUtils.java | 12 +-
.../arrow/adapter/avro/AvroToArrow.java | 19 +
.../arrow/adapter/avro/AvroToArrowConfig.java | 41 +
.../arrow/adapter/avro/AvroToArrowUtils.java | 414 ++++-
.../avro/consumers/AvroNullableConsumer.java | 82 +
.../logical/AvroDecimal256Consumer.java | 74 +
.../logical/AvroTimestampMicrosConsumer.java | 2 +-
.../AvroTimestampMicrosTzConsumer.java | 39 +
.../logical/AvroTimestampMillisConsumer.java | 2 +-
.../AvroTimestampMillisTzConsumer.java | 39 +
.../logical/AvroTimestampNanosConsumer.java | 39 +
.../logical/AvroTimestampNanosTzConsumer.java | 39 +
.../avro/producers/AvroNullableProducer.java | 4 +-
.../adapter/avro/ArrowToAvroSchemaTest.java | 24 +-
.../adapter/avro/AvroLogicalTypesTest.java | 2 +-
.../arrow/adapter/avro/RoundTripDataTest.java | 1606 +++++++++++++++++
.../adapter/avro/RoundTripSchemaTest.java | 443 +++++
.../schema/logical/test_decimal_invalid1.avsc | 2 +-
.../logical/test_local_timestamp_micros.avsc | 23 +
.../logical/test_local_timestamp_millis.avsc | 23 +
.../logical/test_local_timestamp_nanos.avsc | 23 +
.../schema/logical/test_timestamp_nanos.avsc | 23 +
22 files changed, 2870 insertions(+), 105 deletions(-)
create mode 100644 adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/AvroNullableConsumer.java
create mode 100644 adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroDecimal256Consumer.java
create mode 100644 adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMicrosTzConsumer.java
create mode 100644 adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMillisTzConsumer.java
create mode 100644 adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampNanosConsumer.java
create mode 100644 adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampNanosTzConsumer.java
create mode 100644 adapter/avro/src/test/java/org/apache/arrow/adapter/avro/RoundTripDataTest.java
create mode 100644 adapter/avro/src/test/java/org/apache/arrow/adapter/avro/RoundTripSchemaTest.java
create mode 100644 adapter/avro/src/test/resources/schema/logical/test_local_timestamp_micros.avsc
create mode 100644 adapter/avro/src/test/resources/schema/logical/test_local_timestamp_millis.avsc
create mode 100644 adapter/avro/src/test/resources/schema/logical/test_local_timestamp_nanos.avsc
create mode 100644 adapter/avro/src/test/resources/schema/logical/test_timestamp_nanos.avsc
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/ArrowToAvroUtils.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/ArrowToAvroUtils.java
index 6f0cb5cffc..87b594af9e 100644
--- a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/ArrowToAvroUtils.java
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/ArrowToAvroUtils.java
@@ -332,7 +332,17 @@ private static T buildBaseTypeSchema(
case List:
case FixedSizeList:
- return buildArraySchema(builder.array(), field, namespace);
+ // Arrow uses "$data$" as the field name for list items, that is not a valid Avro name
+ Field itemField = field.getChildren().get(0);
+ if (ListVector.DATA_VECTOR_NAME.equals(itemField.getName())) {
+ Field safeItemField =
+ new Field("item", itemField.getFieldType(), itemField.getChildren());
+ Field safeListField =
+ new Field(field.getName(), field.getFieldType(), List.of(safeItemField));
+ return buildArraySchema(builder.array(), safeListField, namespace);
+ } else {
+ return buildArraySchema(builder.array(), field, namespace);
+ }
case Map:
return buildMapSchema(builder.map(), field, namespace);
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrow.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrow.java
index 2392c36f94..2a28ad393b 100644
--- a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrow.java
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrow.java
@@ -59,4 +59,23 @@ public static AvroToArrowVectorIterator avroToArrowIterator(
return AvroToArrowVectorIterator.create(decoder, schema, config);
}
+
+ /**
+ * Convert an Avro schema to its Arrow equivalent.
+ *
+ *
The resulting set of Arrow fields matches what would be set in the VSR after calling
+ * avroToArrow() or avroToArrowIterator(), respecting the configuration in the config parameter.
+ *
+ * @param schema The Avro schema to convert
+ * @param config Configuration options for conversion
+ * @return The equivalent Arrow schema
+ */
+ public static org.apache.arrow.vector.types.pojo.Schema avroToAvroSchema(
+ Schema schema, AvroToArrowConfig config) {
+
+ Preconditions.checkNotNull(schema, "Avro schema object cannot be null");
+ Preconditions.checkNotNull(config, "config cannot be null");
+
+ return AvroToArrowUtils.createArrowSchema(schema, config);
+ }
}
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrowConfig.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrowConfig.java
index bd70c2b8ba..5596138586 100644
--- a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrowConfig.java
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrowConfig.java
@@ -41,6 +41,12 @@ public class AvroToArrowConfig {
/** The field names which to skip when reading decoder values. */
private final Set skipFieldNames;
+ /**
+ * Use legacy-mode to keep compatibility with old behavior (pre-2025), enabled by default. This
+ * affects how the AvroToArrow code interprets the Avro schema.
+ */
+ private final boolean legacyMode;
+
/**
* Instantiate an instance.
*
@@ -64,6 +70,37 @@ public class AvroToArrowConfig {
this.targetBatchSize = targetBatchSize;
this.provider = provider;
this.skipFieldNames = skipFieldNames;
+
+ // Default values for optional parameters
+ legacyMode = true; // Keep compatibility with old behavior by default
+ }
+
+ /**
+ * Instantiate an instance.
+ *
+ * @param allocator The memory allocator to construct the Arrow vectors with.
+ * @param targetBatchSize The maximum rowCount to read each time when partially convert data.
+ * @param provider The dictionary provider used for enum type, adapter will update this provider.
+ * @param skipFieldNames Field names which to skip.
+ * @param legacyMode Keep compatibility with old behavior (pre-2025)
+ */
+ AvroToArrowConfig(
+ BufferAllocator allocator,
+ int targetBatchSize,
+ DictionaryProvider.MapDictionaryProvider provider,
+ Set skipFieldNames,
+ boolean legacyMode) {
+
+ Preconditions.checkArgument(
+ targetBatchSize == AvroToArrowVectorIterator.NO_LIMIT_BATCH_SIZE || targetBatchSize > 0,
+ "invalid targetBatchSize: %s",
+ targetBatchSize);
+
+ this.allocator = allocator;
+ this.targetBatchSize = targetBatchSize;
+ this.provider = provider;
+ this.skipFieldNames = skipFieldNames;
+ this.legacyMode = legacyMode;
}
public BufferAllocator getAllocator() {
@@ -81,4 +118,8 @@ public DictionaryProvider.MapDictionaryProvider getProvider() {
public Set getSkipFieldNames() {
return skipFieldNames;
}
+
+ public boolean isLegacyMode() {
+ return legacyMode;
+ }
}
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrowUtils.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrowUtils.java
index ed7642aabd..aedef7732e 100644
--- a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrowUtils.java
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/AvroToArrowUtils.java
@@ -41,6 +41,7 @@
import org.apache.arrow.adapter.avro.consumers.AvroLongConsumer;
import org.apache.arrow.adapter.avro.consumers.AvroMapConsumer;
import org.apache.arrow.adapter.avro.consumers.AvroNullConsumer;
+import org.apache.arrow.adapter.avro.consumers.AvroNullableConsumer;
import org.apache.arrow.adapter.avro.consumers.AvroStringConsumer;
import org.apache.arrow.adapter.avro.consumers.AvroStructConsumer;
import org.apache.arrow.adapter.avro.consumers.AvroUnionsConsumer;
@@ -49,17 +50,23 @@
import org.apache.arrow.adapter.avro.consumers.SkipConsumer;
import org.apache.arrow.adapter.avro.consumers.SkipFunction;
import org.apache.arrow.adapter.avro.consumers.logical.AvroDateConsumer;
+import org.apache.arrow.adapter.avro.consumers.logical.AvroDecimal256Consumer;
import org.apache.arrow.adapter.avro.consumers.logical.AvroDecimalConsumer;
import org.apache.arrow.adapter.avro.consumers.logical.AvroTimeMicroConsumer;
import org.apache.arrow.adapter.avro.consumers.logical.AvroTimeMillisConsumer;
import org.apache.arrow.adapter.avro.consumers.logical.AvroTimestampMicrosConsumer;
+import org.apache.arrow.adapter.avro.consumers.logical.AvroTimestampMicrosTzConsumer;
import org.apache.arrow.adapter.avro.consumers.logical.AvroTimestampMillisConsumer;
+import org.apache.arrow.adapter.avro.consumers.logical.AvroTimestampMillisTzConsumer;
+import org.apache.arrow.adapter.avro.consumers.logical.AvroTimestampNanosConsumer;
+import org.apache.arrow.adapter.avro.consumers.logical.AvroTimestampNanosTzConsumer;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.util.Preconditions;
import org.apache.arrow.vector.BaseIntVector;
import org.apache.arrow.vector.BigIntVector;
import org.apache.arrow.vector.BitVector;
import org.apache.arrow.vector.DateDayVector;
+import org.apache.arrow.vector.Decimal256Vector;
import org.apache.arrow.vector.DecimalVector;
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.FixedSizeBinaryVector;
@@ -69,8 +76,12 @@
import org.apache.arrow.vector.NullVector;
import org.apache.arrow.vector.TimeMicroVector;
import org.apache.arrow.vector.TimeMilliVector;
+import org.apache.arrow.vector.TimeStampMicroTZVector;
import org.apache.arrow.vector.TimeStampMicroVector;
+import org.apache.arrow.vector.TimeStampMilliTZVector;
import org.apache.arrow.vector.TimeStampMilliVector;
+import org.apache.arrow.vector.TimeStampNanoTZVector;
+import org.apache.arrow.vector.TimeStampNanoVector;
import org.apache.arrow.vector.VarBinaryVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.VectorSchemaRoot;
@@ -169,42 +180,69 @@ private static Consumer createConsumer(
switch (type) {
case UNION:
- consumer = createUnionConsumer(schema, name, config, consumerVector);
+ boolean nullableUnion =
+ schema.getTypes().stream().anyMatch(t -> t.getType() == Schema.Type.NULL);
+ if (schema.getTypes().size() == 2 && nullableUnion && !config.isLegacyMode()) {
+ // For a simple nullable (null | type), interpret the union as a single nullable field.
+ // Not available in legacy mode, which uses the literal interpretation instead
+ int nullIndex = schema.getTypes().get(0).getType() == Schema.Type.NULL ? 0 : 1;
+ int childIndex = nullIndex == 0 ? 1 : 0;
+ Schema childSchema = schema.getTypes().get(childIndex);
+ Consumer> childConsumer =
+ createConsumer(childSchema, name, true, config, consumerVector);
+ consumer = new AvroNullableConsumer<>(childConsumer, nullIndex);
+ } else {
+ // Literal interpretation of a union, which may or may not include a null element.
+ consumer = createUnionConsumer(schema, name, nullableUnion, config, consumerVector);
+ }
break;
case ARRAY:
- consumer = createArrayConsumer(schema, name, config, consumerVector);
+ consumer = createArrayConsumer(schema, name, nullable, config, consumerVector);
break;
case MAP:
- consumer = createMapConsumer(schema, name, config, consumerVector);
+ consumer = createMapConsumer(schema, name, nullable, config, consumerVector);
break;
case RECORD:
- consumer = createStructConsumer(schema, name, config, consumerVector);
+ consumer = createStructConsumer(schema, name, nullable, config, consumerVector);
break;
case ENUM:
- consumer = createEnumConsumer(schema, name, config, consumerVector);
+ consumer = createEnumConsumer(schema, name, nullable, config, consumerVector);
break;
case STRING:
arrowType = new ArrowType.Utf8();
- fieldType = new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ fieldType =
+ new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroStringConsumer((VarCharVector) vector);
break;
case FIXED:
- Map extProps = createExternalProps(schema);
+ Map extProps = createExternalProps(schema, config);
if (logicalType instanceof LogicalTypes.Decimal) {
- arrowType = createDecimalArrowType((LogicalTypes.Decimal) logicalType);
+ arrowType = createDecimalArrowType((LogicalTypes.Decimal) logicalType, schema);
fieldType =
new FieldType(
- nullable, arrowType, /* dictionary= */ null, getMetaData(schema, extProps));
+ nullable,
+ arrowType,
+ /* dictionary= */ null,
+ getMetaData(schema, extProps, config));
vector = createVector(consumerVector, fieldType, name, allocator);
- consumer =
- new AvroDecimalConsumer.FixedDecimalConsumer(
- (DecimalVector) vector, schema.getFixedSize());
+ if (schema.getFixedSize() <= 16) {
+ consumer =
+ new AvroDecimalConsumer.FixedDecimalConsumer(
+ (DecimalVector) vector, schema.getFixedSize());
+ } else {
+ consumer =
+ new AvroDecimal256Consumer.FixedDecimal256Consumer(
+ (Decimal256Vector) vector, schema.getFixedSize());
+ }
} else {
arrowType = new ArrowType.FixedSizeBinary(schema.getFixedSize());
fieldType =
new FieldType(
- nullable, arrowType, /* dictionary= */ null, getMetaData(schema, extProps));
+ nullable,
+ arrowType,
+ /* dictionary= */ null,
+ getMetaData(schema, extProps, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroFixedConsumer((FixedSizeBinaryVector) vector, schema.getFixedSize());
}
@@ -213,26 +251,30 @@ private static Consumer createConsumer(
if (logicalType instanceof LogicalTypes.Date) {
arrowType = new ArrowType.Date(DateUnit.DAY);
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroDateConsumer((DateDayVector) vector);
} else if (logicalType instanceof LogicalTypes.TimeMillis) {
arrowType = new ArrowType.Time(TimeUnit.MILLISECOND, 32);
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroTimeMillisConsumer((TimeMilliVector) vector);
} else {
arrowType = new ArrowType.Int(32, /* isSigned= */ true);
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroIntConsumer((IntVector) vector);
}
break;
case BOOLEAN:
arrowType = new ArrowType.Bool();
- fieldType = new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ fieldType =
+ new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroBooleanConsumer((BitVector) vector);
break;
@@ -240,60 +282,109 @@ private static Consumer createConsumer(
if (logicalType instanceof LogicalTypes.TimeMicros) {
arrowType = new ArrowType.Time(TimeUnit.MICROSECOND, 64);
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroTimeMicroConsumer((TimeMicroVector) vector);
- } else if (logicalType instanceof LogicalTypes.TimestampMillis) {
+ } else if (logicalType instanceof LogicalTypes.TimestampMillis && !config.isLegacyMode()) {
+ // In legacy mode the timestamp-xxx types are treated as local, there is no zone aware
+ // type
+ arrowType = new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC");
+ fieldType =
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
+ vector = createVector(consumerVector, fieldType, name, allocator);
+ consumer = new AvroTimestampMillisTzConsumer((TimeStampMilliTZVector) vector);
+ } else if (logicalType instanceof LogicalTypes.TimestampMicros && !config.isLegacyMode()) {
+ arrowType = new ArrowType.Timestamp(TimeUnit.MICROSECOND, "UTC");
+ fieldType =
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
+ vector = createVector(consumerVector, fieldType, name, allocator);
+ consumer = new AvroTimestampMicrosTzConsumer((TimeStampMicroTZVector) vector);
+ } else if (logicalType instanceof LogicalTypes.TimestampNanos && !config.isLegacyMode()) {
+ arrowType = new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC");
+ fieldType =
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
+ vector = createVector(consumerVector, fieldType, name, allocator);
+ consumer = new AvroTimestampNanosTzConsumer((TimeStampNanoTZVector) vector);
+ } else if (logicalType instanceof LogicalTypes.LocalTimestampMillis
+ || (logicalType instanceof LogicalTypes.TimestampMillis && config.isLegacyMode())) {
arrowType = new ArrowType.Timestamp(TimeUnit.MILLISECOND, null);
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroTimestampMillisConsumer((TimeStampMilliVector) vector);
- } else if (logicalType instanceof LogicalTypes.TimestampMicros) {
+ } else if (logicalType instanceof LogicalTypes.LocalTimestampMicros
+ || (logicalType instanceof LogicalTypes.TimestampMicros && config.isLegacyMode())) {
+ // In legacy mode the timestamp-xxx types are treated as local
arrowType = new ArrowType.Timestamp(TimeUnit.MICROSECOND, null);
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroTimestampMicrosConsumer((TimeStampMicroVector) vector);
+ } else if (logicalType instanceof LogicalTypes.LocalTimestampNanos
+ || (logicalType instanceof LogicalTypes.TimestampNanos && config.isLegacyMode())) {
+ arrowType = new ArrowType.Timestamp(TimeUnit.NANOSECOND, null);
+ fieldType =
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
+ vector = createVector(consumerVector, fieldType, name, allocator);
+ consumer = new AvroTimestampNanosConsumer((TimeStampNanoVector) vector);
} else {
arrowType = new ArrowType.Int(64, /* isSigned= */ true);
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroLongConsumer((BigIntVector) vector);
}
break;
case FLOAT:
arrowType = new ArrowType.FloatingPoint(SINGLE);
- fieldType = new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ fieldType =
+ new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroFloatConsumer((Float4Vector) vector);
break;
case DOUBLE:
arrowType = new ArrowType.FloatingPoint(DOUBLE);
- fieldType = new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ fieldType =
+ new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroDoubleConsumer((Float8Vector) vector);
break;
case BYTES:
if (logicalType instanceof LogicalTypes.Decimal) {
- arrowType = createDecimalArrowType((LogicalTypes.Decimal) logicalType);
+ LogicalTypes.Decimal decimalType = (LogicalTypes.Decimal) logicalType;
+ arrowType = createDecimalArrowType(decimalType, schema);
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
- consumer = new AvroDecimalConsumer.BytesDecimalConsumer((DecimalVector) vector);
+ if (decimalType.getPrecision() <= 38) {
+ consumer = new AvroDecimalConsumer.BytesDecimalConsumer((DecimalVector) vector);
+ } else {
+ consumer =
+ new AvroDecimal256Consumer.BytesDecimal256Consumer((Decimal256Vector) vector);
+ }
} else {
arrowType = new ArrowType.Binary();
fieldType =
- new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
+ new FieldType(
+ nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
vector = createVector(consumerVector, fieldType, name, allocator);
consumer = new AvroBytesConsumer((VarBinaryVector) vector);
}
break;
case NULL:
arrowType = new ArrowType.Null();
- fieldType = new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema));
- vector = fieldType.createNewSingleVector(name, allocator, /* schemaCallBack= */ null);
+ fieldType =
+ new FieldType(nullable, arrowType, /* dictionary= */ null, getMetaData(schema, config));
+ vector = new NullVector(name, fieldType); // Respect nullability defined in fieldType
consumer = new AvroNullConsumer((NullVector) vector);
break;
default:
@@ -304,19 +395,31 @@ private static Consumer createConsumer(
return consumer;
}
- private static ArrowType createDecimalArrowType(LogicalTypes.Decimal logicalType) {
+ private static ArrowType createDecimalArrowType(LogicalTypes.Decimal logicalType, Schema schema) {
final int scale = logicalType.getScale();
final int precision = logicalType.getPrecision();
Preconditions.checkArgument(
- precision > 0 && precision <= 38, "Precision must be in range of 1 to 38");
- Preconditions.checkArgument(scale >= 0 && scale <= 38, "Scale must be in range of 0 to 38.");
+ precision > 0 && precision <= 76, "Precision must be in range of 1 to 76");
+ Preconditions.checkArgument(scale >= 0 && scale <= 76, "Scale must be in range of 0 to 76.");
Preconditions.checkArgument(
scale <= precision,
"Invalid decimal scale: %s (greater than precision: %s)",
scale,
precision);
- return new ArrowType.Decimal(precision, scale, 128);
+ if (schema.getType() == Schema.Type.FIXED) {
+ if (schema.getFixedSize() <= 16) {
+ return new ArrowType.Decimal(precision, scale, 128);
+ } else {
+ return new ArrowType.Decimal(precision, scale, 256);
+ }
+ } else {
+ if (precision <= 38) {
+ return new ArrowType.Decimal(precision, scale, 128);
+ } else {
+ return new ArrowType.Decimal(precision, scale, 256);
+ }
+ }
}
private static Consumer createSkipConsumer(Schema schema) {
@@ -406,6 +509,30 @@ private static Consumer createSkipConsumer(Schema schema) {
return new SkipConsumer(skipFunction);
}
+ static org.apache.arrow.vector.types.pojo.Schema createArrowSchema(
+ Schema schema, AvroToArrowConfig config) {
+
+ // Create an Arrow schema matching the structure of vectors built by createCompositeConsumer()
+
+ Set skipFieldNames = config.getSkipFieldNames();
+ List arrowFields = new ArrayList<>(schema.getFields().size());
+
+ Schema.Type type = schema.getType();
+ if (type == Schema.Type.RECORD) {
+ for (Schema.Field field : schema.getFields()) {
+ if (!skipFieldNames.contains(field.name())) {
+ Field arrowField = avroSchemaToField(field.schema(), field.name(), config);
+ arrowFields.add(arrowField);
+ }
+ }
+ } else {
+ Field arrowField = avroSchemaToField(schema, schema.getName(), config);
+ arrowFields.add(arrowField);
+ }
+
+ return new org.apache.arrow.vector.types.pojo.Schema(arrowFields);
+ }
+
static CompositeAvroConsumer createCompositeConsumer(Schema schema, AvroToArrowConfig config) {
List consumers = new ArrayList<>();
@@ -442,11 +569,20 @@ private static String getDefaultFieldName(ArrowType type) {
}
private static Field avroSchemaToField(Schema schema, String name, AvroToArrowConfig config) {
- return avroSchemaToField(schema, name, config, null);
+ return avroSchemaToField(schema, name, false, config, null);
}
private static Field avroSchemaToField(
Schema schema, String name, AvroToArrowConfig config, Map externalProps) {
+ return avroSchemaToField(schema, name, false, config, externalProps);
+ }
+
+ private static Field avroSchemaToField(
+ Schema schema,
+ String name,
+ boolean nullable,
+ AvroToArrowConfig config,
+ Map externalProps) {
final Schema.Type type = schema.getType();
final LogicalType logicalType = schema.getLogicalType();
@@ -455,33 +591,53 @@ private static Field avroSchemaToField(
switch (type) {
case UNION:
- for (int i = 0; i < schema.getTypes().size(); i++) {
- Schema childSchema = schema.getTypes().get(i);
- // Union child vector should use default name
- children.add(avroSchemaToField(childSchema, null, config));
+ boolean nullableUnion =
+ schema.getTypes().stream().anyMatch(t -> t.getType() == Schema.Type.NULL);
+ if (nullableUnion && schema.getTypes().size() == 2 && !config.isLegacyMode()) {
+ // For a simple nullable (null | type), interpret the union as a single nullable field.
+ // Not available in legacy mode, which uses the literal interpretation instead
+ Schema childSchema =
+ schema.getTypes().get(0).getType() == Schema.Type.NULL
+ ? schema.getTypes().get(1)
+ : schema.getTypes().get(0);
+ return avroSchemaToField(childSchema, name, true, config, externalProps);
+ } else {
+ // Literal interpretation of a union, which may or may not include a null element.
+ for (int i = 0; i < schema.getTypes().size(); i++) {
+ Schema childSchema = schema.getTypes().get(i);
+ // Union child vector should use default name
+ children.add(avroSchemaToField(childSchema, null, nullableUnion, config, null));
+ }
+ fieldType =
+ createFieldType(
+ new ArrowType.Union(UnionMode.Sparse, null), schema, externalProps, config);
}
- fieldType =
- createFieldType(new ArrowType.Union(UnionMode.Sparse, null), schema, externalProps);
break;
case ARRAY:
Schema elementSchema = schema.getElementType();
- children.add(avroSchemaToField(elementSchema, elementSchema.getName(), config));
- fieldType = createFieldType(new ArrowType.List(), schema, externalProps);
+ children.add(avroSchemaToField(elementSchema, ListVector.DATA_VECTOR_NAME, config));
+ fieldType = createFieldType(nullable, new ArrowType.List(), schema, externalProps, config);
break;
case MAP:
// MapVector internal struct field and key field should be non-nullable
FieldType keyFieldType =
new FieldType(/* nullable= */ false, new ArrowType.Utf8(), /* dictionary= */ null);
- Field keyField = new Field("key", keyFieldType, /* children= */ null);
- Field valueField = avroSchemaToField(schema.getValueType(), "value", config);
+ Field keyField = new Field(MapVector.KEY_NAME, keyFieldType, /* children= */ null);
+ Field valueField = avroSchemaToField(schema.getValueType(), MapVector.VALUE_NAME, config);
FieldType structFieldType =
new FieldType(false, new ArrowType.Struct(), /* dictionary= */ null);
Field structField =
- new Field("internal", structFieldType, Arrays.asList(keyField, valueField));
+ new Field(
+ MapVector.DATA_VECTOR_NAME, structFieldType, Arrays.asList(keyField, valueField));
children.add(structField);
fieldType =
- createFieldType(new ArrowType.Map(/* keysSorted= */ false), schema, externalProps);
+ createFieldType(
+ nullable,
+ new ArrowType.Map(/* keysSorted= */ false),
+ schema,
+ externalProps,
+ config);
break;
case RECORD:
final Set skipFieldNames = config.getSkipFieldNames();
@@ -496,13 +652,14 @@ private static Field avroSchemaToField(
if (doc != null) {
extProps.put("doc", doc);
}
- if (aliases != null) {
+ if (aliases != null && (!aliases.isEmpty() || config.isLegacyMode())) {
extProps.put("aliases", convertAliases(aliases));
}
children.add(avroSchemaToField(childSchema, fullChildName, config, extProps));
}
}
- fieldType = createFieldType(new ArrowType.Struct(), schema, externalProps);
+ fieldType =
+ createFieldType(nullable, new ArrowType.Struct(), schema, externalProps, config);
break;
case ENUM:
DictionaryProvider.MapDictionaryProvider provider = config.getProvider();
@@ -512,23 +669,25 @@ private static Field avroSchemaToField(
fieldType =
createFieldType(
+ nullable,
indexType,
schema,
externalProps,
- new DictionaryEncoding(current, /* ordered= */ false, /* indexType= */ indexType));
+ new DictionaryEncoding(current, /* ordered= */ false, /* indexType= */ indexType),
+ config);
break;
case STRING:
- fieldType = createFieldType(new ArrowType.Utf8(), schema, externalProps);
+ fieldType = createFieldType(nullable, new ArrowType.Utf8(), schema, externalProps, config);
break;
case FIXED:
final ArrowType fixedArrowType;
if (logicalType instanceof LogicalTypes.Decimal) {
- fixedArrowType = createDecimalArrowType((LogicalTypes.Decimal) logicalType);
+ fixedArrowType = createDecimalArrowType((LogicalTypes.Decimal) logicalType, schema);
} else {
fixedArrowType = new ArrowType.FixedSizeBinary(schema.getFixedSize());
}
- fieldType = createFieldType(fixedArrowType, schema, externalProps);
+ fieldType = createFieldType(nullable, fixedArrowType, schema, externalProps, config);
break;
case INT:
final ArrowType intArrowType;
@@ -539,41 +698,62 @@ private static Field avroSchemaToField(
} else {
intArrowType = new ArrowType.Int(32, /* isSigned= */ true);
}
- fieldType = createFieldType(intArrowType, schema, externalProps);
+ fieldType = createFieldType(nullable, intArrowType, schema, externalProps, config);
break;
case BOOLEAN:
- fieldType = createFieldType(new ArrowType.Bool(), schema, externalProps);
+ fieldType = createFieldType(nullable, new ArrowType.Bool(), schema, externalProps, config);
break;
case LONG:
final ArrowType longArrowType;
if (logicalType instanceof LogicalTypes.TimeMicros) {
longArrowType = new ArrowType.Time(TimeUnit.MICROSECOND, 64);
} else if (logicalType instanceof LogicalTypes.TimestampMillis) {
- longArrowType = new ArrowType.Timestamp(TimeUnit.MILLISECOND, null);
+ // In legacy mode the timestamp-xxx types are treated as local
+ String tz = config.isLegacyMode() ? null : "UTC";
+ longArrowType = new ArrowType.Timestamp(TimeUnit.MILLISECOND, tz);
} else if (logicalType instanceof LogicalTypes.TimestampMicros) {
+ String tz = config.isLegacyMode() ? null : "UTC";
+ longArrowType = new ArrowType.Timestamp(TimeUnit.MICROSECOND, tz);
+ } else if (logicalType instanceof LogicalTypes.TimestampNanos) {
+ String tz = config.isLegacyMode() ? null : "UTC";
+ longArrowType = new ArrowType.Timestamp(TimeUnit.NANOSECOND, tz);
+ } else if (logicalType instanceof LogicalTypes.LocalTimestampMillis
+ && !config.isLegacyMode()) {
+ // In legacy mode the local-timestamp-xxx types are not recognized (result is just type =
+ // long)
+ longArrowType = new ArrowType.Timestamp(TimeUnit.MILLISECOND, null);
+ } else if (logicalType instanceof LogicalTypes.LocalTimestampMicros
+ && !config.isLegacyMode()) {
longArrowType = new ArrowType.Timestamp(TimeUnit.MICROSECOND, null);
+ } else if (logicalType instanceof LogicalTypes.LocalTimestampNanos
+ && !config.isLegacyMode()) {
+ longArrowType = new ArrowType.Timestamp(TimeUnit.NANOSECOND, null);
} else {
longArrowType = new ArrowType.Int(64, /* isSigned= */ true);
}
- fieldType = createFieldType(longArrowType, schema, externalProps);
+ fieldType = createFieldType(nullable, longArrowType, schema, externalProps, config);
break;
case FLOAT:
- fieldType = createFieldType(new ArrowType.FloatingPoint(SINGLE), schema, externalProps);
+ fieldType =
+ createFieldType(
+ nullable, new ArrowType.FloatingPoint(SINGLE), schema, externalProps, config);
break;
case DOUBLE:
- fieldType = createFieldType(new ArrowType.FloatingPoint(DOUBLE), schema, externalProps);
+ fieldType =
+ createFieldType(
+ nullable, new ArrowType.FloatingPoint(DOUBLE), schema, externalProps, config);
break;
case BYTES:
final ArrowType bytesArrowType;
if (logicalType instanceof LogicalTypes.Decimal) {
- bytesArrowType = createDecimalArrowType((LogicalTypes.Decimal) logicalType);
+ bytesArrowType = createDecimalArrowType((LogicalTypes.Decimal) logicalType, schema);
} else {
bytesArrowType = new ArrowType.Binary();
}
- fieldType = createFieldType(bytesArrowType, schema, externalProps);
+ fieldType = createFieldType(nullable, bytesArrowType, schema, externalProps, config);
break;
case NULL:
- fieldType = createFieldType(ArrowType.Null.INSTANCE, schema, externalProps);
+ fieldType = createFieldType(ArrowType.Null.INSTANCE, schema, externalProps, config);
break;
default:
// no-op, shouldn't get here
@@ -583,15 +763,24 @@ private static Field avroSchemaToField(
if (name == null) {
name = getDefaultFieldName(fieldType.getType());
}
+ if (name.contains(".") && !config.isLegacyMode()) {
+ // Do not include namespace as part of the field name
+ name = name.substring(name.lastIndexOf(".") + 1);
+ }
return new Field(name, fieldType, children.size() == 0 ? null : children);
}
private static Consumer createArrayConsumer(
- Schema schema, String name, AvroToArrowConfig config, FieldVector consumerVector) {
+ Schema schema,
+ String name,
+ boolean nullable,
+ AvroToArrowConfig config,
+ FieldVector consumerVector) {
ListVector listVector;
if (consumerVector == null) {
- final Field field = avroSchemaToField(schema, name, config);
+ final Field field =
+ avroSchemaToField(schema, name, nullable, config, /* externalProps= */ null);
listVector = (ListVector) field.createVector(config.getAllocator());
} else {
listVector = (ListVector) consumerVector;
@@ -607,13 +796,18 @@ private static Consumer createArrayConsumer(
}
private static Consumer createStructConsumer(
- Schema schema, String name, AvroToArrowConfig config, FieldVector consumerVector) {
+ Schema schema,
+ String name,
+ boolean nullable,
+ AvroToArrowConfig config,
+ FieldVector consumerVector) {
final Set skipFieldNames = config.getSkipFieldNames();
StructVector structVector;
if (consumerVector == null) {
- final Field field = avroSchemaToField(schema, name, config, createExternalProps(schema));
+ final Field field =
+ avroSchemaToField(schema, name, nullable, config, createExternalProps(schema, config));
structVector = (StructVector) field.createVector(config.getAllocator());
} else {
structVector = (StructVector) consumerVector;
@@ -644,11 +838,16 @@ private static Consumer createStructConsumer(
}
private static Consumer createEnumConsumer(
- Schema schema, String name, AvroToArrowConfig config, FieldVector consumerVector) {
+ Schema schema,
+ String name,
+ boolean nullable,
+ AvroToArrowConfig config,
+ FieldVector consumerVector) {
BaseIntVector indexVector;
if (consumerVector == null) {
- final Field field = avroSchemaToField(schema, name, config, createExternalProps(schema));
+ final Field field =
+ avroSchemaToField(schema, name, nullable, config, createExternalProps(schema, config));
indexVector = (BaseIntVector) field.createVector(config.getAllocator());
} else {
indexVector = (BaseIntVector) consumerVector;
@@ -668,11 +867,16 @@ private static Consumer createEnumConsumer(
}
private static Consumer createMapConsumer(
- Schema schema, String name, AvroToArrowConfig config, FieldVector consumerVector) {
+ Schema schema,
+ String name,
+ boolean nullable,
+ AvroToArrowConfig config,
+ FieldVector consumerVector) {
MapVector mapVector;
if (consumerVector == null) {
- final Field field = avroSchemaToField(schema, name, config);
+ final Field field =
+ avroSchemaToField(schema, name, nullable, config, /* externalProps= */ null);
mapVector = (MapVector) field.createVector(config.getAllocator());
} else {
mapVector = (MapVector) consumerVector;
@@ -698,12 +902,13 @@ private static Consumer createMapConsumer(
}
private static Consumer createUnionConsumer(
- Schema schema, String name, AvroToArrowConfig config, FieldVector consumerVector) {
+ Schema schema,
+ String name,
+ boolean nullableUnion,
+ AvroToArrowConfig config,
+ FieldVector consumerVector) {
final int size = schema.getTypes().size();
- final boolean nullable =
- schema.getTypes().stream().anyMatch(t -> t.getType() == Schema.Type.NULL);
-
UnionVector unionVector;
if (consumerVector == null) {
final Field field = avroSchemaToField(schema, name, config);
@@ -720,7 +925,8 @@ private static Consumer createUnionConsumer(
for (int i = 0; i < size; i++) {
FieldVector child = childVectors.get(i);
Schema subSchema = schema.getTypes().get(i);
- Consumer delegate = createConsumer(subSchema, subSchema.getName(), nullable, config, child);
+ Consumer delegate =
+ createConsumer(subSchema, subSchema.getName(), nullableUnion, config, child);
delegates[i] = delegate;
types[i] = child.getMinorType();
}
@@ -785,14 +991,24 @@ static VectorSchemaRoot avroToArrowVectors(
return root;
}
- private static Map getMetaData(Schema schema) {
+ // Do not include props that are part of the Avro format itself as field metadata
+ // These are already represented in the field / type structure and are not custom attributes
+ private static final List AVRO_FORMAT_METADATA =
+ Arrays.asList("logicalType", "precision", "scale");
+
+ private static Map getMetaData(Schema schema, AvroToArrowConfig config) {
Map metadata = new HashMap<>();
- schema.getObjectProps().forEach((k, v) -> metadata.put(k, v.toString()));
+ for (Map.Entry prop : schema.getObjectProps().entrySet()) {
+ if (!AVRO_FORMAT_METADATA.contains(prop.getKey()) || config.isLegacyMode()) {
+ metadata.put(prop.getKey(), prop.getValue().toString());
+ }
+ }
return metadata;
}
- private static Map getMetaData(Schema schema, Map externalProps) {
- Map metadata = getMetaData(schema);
+ private static Map getMetaData(
+ Schema schema, Map externalProps, AvroToArrowConfig config) {
+ Map metadata = getMetaData(schema, config);
if (externalProps != null) {
metadata.putAll(externalProps);
}
@@ -800,32 +1016,58 @@ private static Map getMetaData(Schema schema, Map createExternalProps(Schema schema) {
+ private static Map createExternalProps(Schema schema, AvroToArrowConfig config) {
final Map extProps = new HashMap<>();
String doc = schema.getDoc();
Set aliases = schema.getAliases();
if (doc != null) {
extProps.put("doc", doc);
}
- if (aliases != null) {
+ if (aliases != null && (!aliases.isEmpty() || config.isLegacyMode())) {
extProps.put("aliases", convertAliases(aliases));
}
return extProps;
}
private static FieldType createFieldType(
- ArrowType arrowType, Schema schema, Map externalProps) {
- return createFieldType(arrowType, schema, externalProps, /* dictionary= */ null);
+ ArrowType arrowType,
+ Schema schema,
+ Map externalProps,
+ AvroToArrowConfig config) {
+ return createFieldType(arrowType, schema, externalProps, /* dictionary= */ null, config);
+ }
+
+ private static FieldType createFieldType(
+ boolean nullable,
+ ArrowType arrowType,
+ Schema schema,
+ Map externalProps,
+ AvroToArrowConfig config) {
+ return createFieldType(
+ nullable, arrowType, schema, externalProps, /* dictionary= */ null, config);
}
private static FieldType createFieldType(
ArrowType arrowType,
Schema schema,
Map externalProps,
- DictionaryEncoding dictionary) {
+ DictionaryEncoding dictionary,
+ AvroToArrowConfig config) {
+
+ return createFieldType(
+ /* nullable= */ false, arrowType, schema, externalProps, dictionary, config);
+ }
+
+ private static FieldType createFieldType(
+ boolean nullable,
+ ArrowType arrowType,
+ Schema schema,
+ Map externalProps,
+ DictionaryEncoding dictionary,
+ AvroToArrowConfig config) {
return new FieldType(
- /* nullable= */ false, arrowType, dictionary, getMetaData(schema, externalProps));
+ nullable, arrowType, dictionary, getMetaData(schema, externalProps, config));
}
private static String convertAliases(Set aliases) {
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/AvroNullableConsumer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/AvroNullableConsumer.java
new file mode 100644
index 0000000000..b67819cb9d
--- /dev/null
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/AvroNullableConsumer.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.adapter.avro.consumers;
+
+import java.io.IOException;
+import org.apache.arrow.vector.FieldVector;
+import org.apache.avro.io.Decoder;
+
+/**
+ * Consumer wrapper which consumes nullable type values from avro decoder. Write the data to the
+ * underlying {@link FieldVector}.
+ *
+ * @param The vector within consumer or its delegate.
+ */
+public class AvroNullableConsumer extends BaseAvroConsumer {
+
+ private final Consumer delegate;
+ private final int nullIndex;
+
+ /** Instantiate a AvroNullableConsumer. */
+ @SuppressWarnings("unchecked")
+ public AvroNullableConsumer(Consumer delegate, int nullIndex) {
+ super((T) delegate.getVector());
+ this.delegate = delegate;
+ this.nullIndex = nullIndex;
+ }
+
+ @Override
+ public void consume(Decoder decoder) throws IOException {
+ int typeIndex = decoder.readInt();
+ if (typeIndex == nullIndex) {
+ decoder.readNull();
+ delegate.addNull();
+ } else {
+ delegate.consume(decoder);
+ }
+ currentIndex++;
+ }
+
+ @Override
+ public void addNull() {
+ // Can be called by containers of nullable types
+ delegate.addNull();
+ currentIndex++;
+ }
+
+ @Override
+ public void setPosition(int index) {
+ if (index < 0 || index > vector.getValueCount()) {
+ throw new IllegalArgumentException("Index out of bounds");
+ }
+ delegate.setPosition(index);
+ super.setPosition(index);
+ }
+
+ @Override
+ public boolean resetValueVector(T vector) {
+ boolean delegateOk = delegate.resetValueVector(vector);
+ boolean thisOk = super.resetValueVector(vector);
+ return thisOk && delegateOk;
+ }
+
+ @Override
+ public void close() throws Exception {
+ super.close();
+ delegate.close();
+ }
+}
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroDecimal256Consumer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroDecimal256Consumer.java
new file mode 100644
index 0000000000..12652833a1
--- /dev/null
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroDecimal256Consumer.java
@@ -0,0 +1,74 @@
+/*
+ * 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.avro.consumers.logical;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import org.apache.arrow.adapter.avro.consumers.BaseAvroConsumer;
+import org.apache.arrow.util.Preconditions;
+import org.apache.arrow.vector.Decimal256Vector;
+import org.apache.avro.io.Decoder;
+
+/**
+ * Consumer which consume 256-bit decimal type values from avro decoder. Write the data to {@link
+ * Decimal256Vector}.
+ */
+public abstract class AvroDecimal256Consumer extends BaseAvroConsumer {
+
+ protected AvroDecimal256Consumer(Decimal256Vector vector) {
+ super(vector);
+ }
+
+ /** Consumer for decimal logical type with 256 bit width and original bytes type. */
+ public static class BytesDecimal256Consumer extends AvroDecimal256Consumer {
+
+ private ByteBuffer cacheBuffer;
+
+ /** Instantiate a BytesDecimal256Consumer. */
+ public BytesDecimal256Consumer(Decimal256Vector vector) {
+ super(vector);
+ }
+
+ @Override
+ public void consume(Decoder decoder) throws IOException {
+ cacheBuffer = decoder.readBytes(cacheBuffer);
+ byte[] bytes = new byte[cacheBuffer.limit()];
+ Preconditions.checkArgument(bytes.length <= 32, "Decimal bytes length should <= 32.");
+ cacheBuffer.get(bytes);
+ vector.setBigEndian(currentIndex++, bytes);
+ }
+ }
+
+ /** Consumer for decimal logical type with 256 bit width and original fixed type. */
+ public static class FixedDecimal256Consumer extends AvroDecimal256Consumer {
+
+ private final byte[] reuseBytes;
+
+ /** Instantiate a FixedDecimal256Consumer. */
+ public FixedDecimal256Consumer(Decimal256Vector vector, int size) {
+ super(vector);
+ Preconditions.checkArgument(size <= 32, "Decimal bytes length should <= 32.");
+ reuseBytes = new byte[size];
+ }
+
+ @Override
+ public void consume(Decoder decoder) throws IOException {
+ decoder.readFixed(reuseBytes);
+ vector.setBigEndian(currentIndex++, reuseBytes);
+ }
+ }
+}
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMicrosConsumer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMicrosConsumer.java
index 88acf7b329..5af40ed17d 100644
--- a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMicrosConsumer.java
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMicrosConsumer.java
@@ -22,7 +22,7 @@
import org.apache.avro.io.Decoder;
/**
- * Consumer which consume date timestamp-micro values from avro decoder. Write the data to {@link
+ * Consumer which consumes local-timestamp-micros values from avro decoder. Write the data to {@link
* TimeStampMicroVector}.
*/
public class AvroTimestampMicrosConsumer extends BaseAvroConsumer {
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMicrosTzConsumer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMicrosTzConsumer.java
new file mode 100644
index 0000000000..a5dede4988
--- /dev/null
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMicrosTzConsumer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.avro.consumers.logical;
+
+import java.io.IOException;
+import org.apache.arrow.adapter.avro.consumers.BaseAvroConsumer;
+import org.apache.arrow.vector.TimeStampMicroTZVector;
+import org.apache.avro.io.Decoder;
+
+/**
+ * Consumer which consumes timestamp-micros values from avro decoder. Write the data to {@link
+ * TimeStampMicroTZVector}.
+ */
+public class AvroTimestampMicrosTzConsumer extends BaseAvroConsumer {
+
+ /** Instantiate a AvroTimestampMicrosTzConsumer. */
+ public AvroTimestampMicrosTzConsumer(TimeStampMicroTZVector vector) {
+ super(vector);
+ }
+
+ @Override
+ public void consume(Decoder decoder) throws IOException {
+ vector.set(currentIndex++, decoder.readLong());
+ }
+}
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMillisConsumer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMillisConsumer.java
index ec50d79023..bc451bd1dc 100644
--- a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMillisConsumer.java
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMillisConsumer.java
@@ -22,7 +22,7 @@
import org.apache.avro.io.Decoder;
/**
- * Consumer which consume date timestamp-millis values from avro decoder. Write the data to {@link
+ * Consumer which consume local-timestamp-millis values from avro decoder. Write the data to {@link
* TimeStampMilliVector}.
*/
public class AvroTimestampMillisConsumer extends BaseAvroConsumer {
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMillisTzConsumer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMillisTzConsumer.java
new file mode 100644
index 0000000000..255fe501fb
--- /dev/null
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampMillisTzConsumer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.avro.consumers.logical;
+
+import java.io.IOException;
+import org.apache.arrow.adapter.avro.consumers.BaseAvroConsumer;
+import org.apache.arrow.vector.TimeStampMilliTZVector;
+import org.apache.avro.io.Decoder;
+
+/**
+ * Consumer which consume timestamp-millis values from avro decoder. Write the data to {@link
+ * TimeStampMilliTZVector}.
+ */
+public class AvroTimestampMillisTzConsumer extends BaseAvroConsumer {
+
+ /** Instantiate a AvroTimestampMillisTzConsumer. */
+ public AvroTimestampMillisTzConsumer(TimeStampMilliTZVector vector) {
+ super(vector);
+ }
+
+ @Override
+ public void consume(Decoder decoder) throws IOException {
+ vector.set(currentIndex++, decoder.readLong());
+ }
+}
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampNanosConsumer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampNanosConsumer.java
new file mode 100644
index 0000000000..b5044d221f
--- /dev/null
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampNanosConsumer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.avro.consumers.logical;
+
+import java.io.IOException;
+import org.apache.arrow.adapter.avro.consumers.BaseAvroConsumer;
+import org.apache.arrow.vector.TimeStampNanoVector;
+import org.apache.avro.io.Decoder;
+
+/**
+ * Consumer which consume local-timestamp-nanos values from avro decoder. Write the data to {@link
+ * TimeStampNanoVector}.
+ */
+public class AvroTimestampNanosConsumer extends BaseAvroConsumer {
+
+ /** Instantiate a AvroTimestampNanosConsumer. */
+ public AvroTimestampNanosConsumer(TimeStampNanoVector vector) {
+ super(vector);
+ }
+
+ @Override
+ public void consume(Decoder decoder) throws IOException {
+ vector.set(currentIndex++, decoder.readLong());
+ }
+}
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampNanosTzConsumer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampNanosTzConsumer.java
new file mode 100644
index 0000000000..3f42b7ccbb
--- /dev/null
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/consumers/logical/AvroTimestampNanosTzConsumer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.avro.consumers.logical;
+
+import java.io.IOException;
+import org.apache.arrow.adapter.avro.consumers.BaseAvroConsumer;
+import org.apache.arrow.vector.TimeStampNanoTZVector;
+import org.apache.avro.io.Decoder;
+
+/**
+ * Consumer which consume timestamp-nanos values from avro decoder. Write the data to {@link
+ * TimeStampNanoTZVector}.
+ */
+public class AvroTimestampNanosTzConsumer extends BaseAvroConsumer {
+
+ /** Instantiate a AvroTimestampNanosConsumer. */
+ public AvroTimestampNanosTzConsumer(TimeStampNanoTZVector vector) {
+ super(vector);
+ }
+
+ @Override
+ public void consume(Decoder decoder) throws IOException {
+ vector.set(currentIndex++, decoder.readLong());
+ }
+}
diff --git a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/producers/AvroNullableProducer.java b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/producers/AvroNullableProducer.java
index f4215dbf84..5f8b314f49 100644
--- a/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/producers/AvroNullableProducer.java
+++ b/adapter/avro/src/main/java/org/apache/arrow/adapter/avro/producers/AvroNullableProducer.java
@@ -21,8 +21,8 @@
import org.apache.avro.io.Encoder;
/**
- * Producer wrapper which producers nullable types to an avro encoder. Write the data to the
- * underlying {@link FieldVector}.
+ * Producer wrapper which produces nullable types to an avro encoder. Read data from the underlying
+ * {@link FieldVector}.
*
* @param The vector within producer or its delegate, used for partially produce purpose.
*/
diff --git a/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/ArrowToAvroSchemaTest.java b/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/ArrowToAvroSchemaTest.java
index a05bbc1653..d3e12e763a 100644
--- a/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/ArrowToAvroSchemaTest.java
+++ b/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/ArrowToAvroSchemaTest.java
@@ -319,10 +319,10 @@ public void testConvertDecimalTypes() {
FieldType.notNullable(new ArrowType.Decimal(20, 10, 128)),
null),
new Field(
- "nullableDecimal256", FieldType.nullable(new ArrowType.Decimal(20, 4, 256)), null),
+ "nullableDecimal256", FieldType.nullable(new ArrowType.Decimal(55, 15, 256)), null),
new Field(
"nonNullableDecimal2561",
- FieldType.notNullable(new ArrowType.Decimal(20, 4, 256)),
+ FieldType.notNullable(new ArrowType.Decimal(55, 25, 256)),
null),
new Field(
"nonNullableDecimal2562",
@@ -330,7 +330,7 @@ public void testConvertDecimalTypes() {
null),
new Field(
"nonNullableDecimal2563",
- FieldType.notNullable(new ArrowType.Decimal(30, 15, 256)),
+ FieldType.notNullable(new ArrowType.Decimal(60, 50, 256)),
null));
Schema schema = ArrowToAvroUtils.createAvroSchema(fields, "TestRecord");
@@ -383,9 +383,9 @@ public void testConvertDecimalTypes() {
schema.getField("nullableDecimal256").schema().getTypes().get(0);
assertEquals(Schema.Type.FIXED, nullableDecimal256Schema.getType());
assertEquals(32, nullableDecimal256Schema.getFixedSize());
- assertEquals(LogicalTypes.decimal(20, 4), nullableDecimal256Schema.getLogicalType());
- assertEquals(20, nullableDecimal256Schema.getObjectProp("precision"));
- assertEquals(4, nullableDecimal256Schema.getObjectProp("scale"));
+ assertEquals(LogicalTypes.decimal(55, 15), nullableDecimal256Schema.getLogicalType());
+ assertEquals(55, nullableDecimal256Schema.getObjectProp("precision"));
+ assertEquals(15, nullableDecimal256Schema.getObjectProp("scale"));
assertEquals(
Schema.Type.NULL,
schema.getField("nullableDecimal256").schema().getTypes().get(1).getType());
@@ -394,9 +394,9 @@ public void testConvertDecimalTypes() {
Schema nonNullableDecimal2561Schema = schema.getField("nonNullableDecimal2561").schema();
assertEquals(Schema.Type.FIXED, nonNullableDecimal2561Schema.getType());
assertEquals(32, nonNullableDecimal2561Schema.getFixedSize());
- assertEquals(LogicalTypes.decimal(20, 4), nonNullableDecimal2561Schema.getLogicalType());
- assertEquals(20, nonNullableDecimal2561Schema.getObjectProp("precision"));
- assertEquals(4, nonNullableDecimal2561Schema.getObjectProp("scale"));
+ assertEquals(LogicalTypes.decimal(55, 25), nonNullableDecimal2561Schema.getLogicalType());
+ assertEquals(55, nonNullableDecimal2561Schema.getObjectProp("precision"));
+ assertEquals(25, nonNullableDecimal2561Schema.getObjectProp("scale"));
// Assertions for nonNullableDecimal2562
Schema nonNullableDecimal2562Schema = schema.getField("nonNullableDecimal2562").schema();
@@ -410,9 +410,9 @@ public void testConvertDecimalTypes() {
Schema nonNullableDecimal2563Schema = schema.getField("nonNullableDecimal2563").schema();
assertEquals(Schema.Type.FIXED, nonNullableDecimal2563Schema.getType());
assertEquals(32, nonNullableDecimal2563Schema.getFixedSize());
- assertEquals(LogicalTypes.decimal(30, 15), nonNullableDecimal2563Schema.getLogicalType());
- assertEquals(30, nonNullableDecimal2563Schema.getObjectProp("precision"));
- assertEquals(15, nonNullableDecimal2563Schema.getObjectProp("scale"));
+ assertEquals(LogicalTypes.decimal(60, 50), nonNullableDecimal2563Schema.getLogicalType());
+ assertEquals(60, nonNullableDecimal2563Schema.getObjectProp("precision"));
+ assertEquals(50, nonNullableDecimal2563Schema.getObjectProp("scale"));
}
@Test
diff --git a/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/AvroLogicalTypesTest.java b/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/AvroLogicalTypesTest.java
index 173cc855b1..801456d79b 100644
--- a/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/AvroLogicalTypesTest.java
+++ b/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/AvroLogicalTypesTest.java
@@ -173,7 +173,7 @@ public void testInvalidDecimalPrecision() throws Exception {
IllegalArgumentException e =
assertThrows(IllegalArgumentException.class, () -> writeAndRead(schema, data));
- assertTrue(e.getMessage().contains("Precision must be in range of 1 to 38"));
+ assertTrue(e.getMessage().contains("Precision must be in range of 1 to 76"));
}
@Test
diff --git a/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/RoundTripDataTest.java b/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/RoundTripDataTest.java
new file mode 100644
index 0000000000..85e6a960b0
--- /dev/null
+++ b/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/RoundTripDataTest.java
@@ -0,0 +1,1606 @@
+/*
+ * 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.avro;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.apache.arrow.adapter.avro.producers.CompositeAvroProducer;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.BigIntVector;
+import org.apache.arrow.vector.BitVector;
+import org.apache.arrow.vector.DateDayVector;
+import org.apache.arrow.vector.Decimal256Vector;
+import org.apache.arrow.vector.DecimalVector;
+import org.apache.arrow.vector.FieldVector;
+import org.apache.arrow.vector.FixedSizeBinaryVector;
+import org.apache.arrow.vector.Float4Vector;
+import org.apache.arrow.vector.Float8Vector;
+import org.apache.arrow.vector.IntVector;
+import org.apache.arrow.vector.NullVector;
+import org.apache.arrow.vector.TimeMicroVector;
+import org.apache.arrow.vector.TimeMilliVector;
+import org.apache.arrow.vector.TimeStampMicroTZVector;
+import org.apache.arrow.vector.TimeStampMicroVector;
+import org.apache.arrow.vector.TimeStampMilliTZVector;
+import org.apache.arrow.vector.TimeStampMilliVector;
+import org.apache.arrow.vector.TimeStampNanoTZVector;
+import org.apache.arrow.vector.TimeStampNanoVector;
+import org.apache.arrow.vector.VarBinaryVector;
+import org.apache.arrow.vector.VarCharVector;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.complex.ListVector;
+import org.apache.arrow.vector.complex.MapVector;
+import org.apache.arrow.vector.complex.StructVector;
+import org.apache.arrow.vector.complex.writer.BaseWriter;
+import org.apache.arrow.vector.complex.writer.FieldWriter;
+import org.apache.arrow.vector.types.DateUnit;
+import org.apache.arrow.vector.types.FloatingPointPrecision;
+import org.apache.arrow.vector.types.TimeUnit;
+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.avro.Schema;
+import org.apache.avro.io.BinaryDecoder;
+import org.apache.avro.io.BinaryEncoder;
+import org.apache.avro.io.DecoderFactory;
+import org.apache.avro.io.EncoderFactory;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+public class RoundTripDataTest {
+
+ @TempDir public static File TMP;
+
+ private static AvroToArrowConfig basicConfig(BufferAllocator allocator) {
+ return new AvroToArrowConfig(allocator, 1000, null, Collections.emptySet(), false);
+ }
+
+ private static VectorSchemaRoot readDataFile(
+ Schema schema, File dataFile, BufferAllocator allocator) throws Exception {
+
+ try (FileInputStream fis = new FileInputStream(dataFile)) {
+ BinaryDecoder decoder = new DecoderFactory().directBinaryDecoder(fis, null);
+ return AvroToArrow.avroToArrow(schema, decoder, basicConfig(allocator));
+ }
+ }
+
+ private static void roundTripTest(
+ VectorSchemaRoot root, BufferAllocator allocator, File dataFile, int rowCount)
+ throws Exception {
+
+ // Write an AVRO block using the producer classes
+ try (FileOutputStream fos = new FileOutputStream(dataFile)) {
+ BinaryEncoder encoder = new EncoderFactory().directBinaryEncoder(fos, null);
+ CompositeAvroProducer producer =
+ ArrowToAvroUtils.createCompositeProducer(root.getFieldVectors());
+ for (int row = 0; row < rowCount; row++) {
+ producer.produce(encoder);
+ }
+ encoder.flush();
+ }
+
+ // Generate AVRO schema
+ Schema schema = ArrowToAvroUtils.createAvroSchema(root.getSchema().getFields());
+
+ // Read back in and compare
+ try (VectorSchemaRoot roundTrip = readDataFile(schema, dataFile, allocator)) {
+
+ assertEquals(root.getSchema(), roundTrip.getSchema());
+ assertEquals(rowCount, roundTrip.getRowCount());
+
+ // Read and check values
+ for (int row = 0; row < rowCount; row++) {
+ assertEquals(root.getVector(0).getObject(row), roundTrip.getVector(0).getObject(row));
+ }
+ }
+ }
+
+ private static void roundTripByteArrayTest(
+ VectorSchemaRoot root, BufferAllocator allocator, File dataFile, int rowCount)
+ throws Exception {
+
+ // Write an AVRO block using the producer classes
+ try (FileOutputStream fos = new FileOutputStream(dataFile)) {
+ BinaryEncoder encoder = new EncoderFactory().directBinaryEncoder(fos, null);
+ CompositeAvroProducer producer =
+ ArrowToAvroUtils.createCompositeProducer(root.getFieldVectors());
+ for (int row = 0; row < rowCount; row++) {
+ producer.produce(encoder);
+ }
+ encoder.flush();
+ }
+
+ // Generate AVRO schema
+ Schema schema = ArrowToAvroUtils.createAvroSchema(root.getSchema().getFields());
+
+ // Read back in and compare
+ try (VectorSchemaRoot roundTrip = readDataFile(schema, dataFile, allocator)) {
+
+ assertEquals(root.getSchema(), roundTrip.getSchema());
+ assertEquals(rowCount, roundTrip.getRowCount());
+
+ // Read and check values
+ for (int row = 0; row < rowCount; row++) {
+ byte[] rootBytes = (byte[]) root.getVector(0).getObject(row);
+ byte[] roundTripBytes = (byte[]) roundTrip.getVector(0).getObject(row);
+ assertArrayEquals(rootBytes, roundTripBytes);
+ }
+ }
+ }
+
+ // Data round trip for primitive types, nullable and non-nullable
+
+ @Test
+ public void testRoundTripNullColumn() throws Exception {
+
+ // The current read implementation expects EOF, which never happens for a single null vector
+ // Include a boolean vector with this test for now, so that EOF exception will be triggered
+
+ // Field definition
+ FieldType nullField = new FieldType(false, new ArrowType.Null(), null);
+ FieldType booleanField = new FieldType(false, new ArrowType.Bool(), null);
+
+ // Create empty vector
+ BufferAllocator allocator = new RootAllocator();
+ NullVector nullVector = new NullVector(new Field("nullColumn", nullField, null));
+ BitVector booleanVector = new BitVector(new Field("boolean", booleanField, null), allocator);
+
+ int rowCount = 10;
+
+ // Set up VSR
+ List vectors = Arrays.asList(nullVector, booleanVector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set all values to null
+ for (int row = 0; row < rowCount; row++) {
+ nullVector.setNull(row);
+ booleanVector.set(row, 0);
+ }
+
+ File dataFile = new File(TMP, "testRoundTripNullColumn.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripBooleans() throws Exception {
+
+ // Field definition
+ FieldType booleanField = new FieldType(false, new ArrowType.Bool(), null);
+
+ // Create empty vector
+ BufferAllocator allocator = new RootAllocator();
+ BitVector booleanVector = new BitVector(new Field("boolean", booleanField, null), allocator);
+
+ // Set up VSR
+ List vectors = Arrays.asList(booleanVector);
+ int rowCount = 10;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ for (int row = 0; row < rowCount; row++) {
+ booleanVector.set(row, row % 2 == 0 ? 1 : 0);
+ }
+
+ File dataFile = new File(TMP, "testRoundTripBooleans.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableBooleans() throws Exception {
+
+ // Field definition
+ FieldType booleanField = new FieldType(true, new ArrowType.Bool(), null);
+
+ // Create empty vector
+ BufferAllocator allocator = new RootAllocator();
+ BitVector booleanVector = new BitVector(new Field("boolean", booleanField, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors = Arrays.asList(booleanVector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Null value
+ booleanVector.setNull(0);
+
+ // False value
+ booleanVector.set(1, 0);
+
+ // True value
+ booleanVector.set(2, 1);
+
+ File dataFile = new File(TMP, "testRoundTripNullableBooleans.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripIntegers() throws Exception {
+
+ // Field definitions
+ FieldType int32Field = new FieldType(false, new ArrowType.Int(32, true), null);
+ FieldType int64Field = new FieldType(false, new ArrowType.Int(64, true), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ IntVector int32Vector = new IntVector(new Field("int32", int32Field, null), allocator);
+ BigIntVector int64Vector = new BigIntVector(new Field("int64", int64Field, null), allocator);
+
+ // Set up VSR
+ List vectors = Arrays.asList(int32Vector, int64Vector);
+
+ int rowCount = 12;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ for (int row = 0; row < 10; row++) {
+ int32Vector.set(row, 513 * row * (row % 2 == 0 ? 1 : -1));
+ int64Vector.set(row, 3791L * row * (row % 2 == 0 ? 1 : -1));
+ }
+
+ // Min values
+ int32Vector.set(10, Integer.MIN_VALUE);
+ int64Vector.set(10, Long.MIN_VALUE);
+
+ // Max values
+ int32Vector.set(11, Integer.MAX_VALUE);
+ int64Vector.set(11, Long.MAX_VALUE);
+
+ File dataFile = new File(TMP, "testRoundTripIntegers.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableIntegers() throws Exception {
+
+ // Field definitions
+ FieldType int32Field = new FieldType(true, new ArrowType.Int(32, true), null);
+ FieldType int64Field = new FieldType(true, new ArrowType.Int(64, true), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ IntVector int32Vector = new IntVector(new Field("int32", int32Field, null), allocator);
+ BigIntVector int64Vector = new BigIntVector(new Field("int64", int64Field, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors = Arrays.asList(int32Vector, int64Vector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Null values
+ int32Vector.setNull(0);
+ int64Vector.setNull(0);
+
+ // Zero values
+ int32Vector.set(1, 0);
+ int64Vector.set(1, 0);
+
+ // Non-zero values
+ int32Vector.set(2, Integer.MAX_VALUE);
+ int64Vector.set(2, Long.MAX_VALUE);
+
+ File dataFile = new File(TMP, "testRoundTripNullableIntegers.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripFloatingPoints() throws Exception {
+
+ // Field definitions
+ FieldType float32Field =
+ new FieldType(false, new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE), null);
+ FieldType float64Field =
+ new FieldType(false, new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ Float4Vector float32Vector =
+ new Float4Vector(new Field("float32", float32Field, null), allocator);
+ Float8Vector float64Vector =
+ new Float8Vector(new Field("float64", float64Field, null), allocator);
+
+ // Set up VSR
+ List vectors = Arrays.asList(float32Vector, float64Vector);
+ int rowCount = 15;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ for (int row = 0; row < 10; row++) {
+ float32Vector.set(row, 37.6f * row * (row % 2 == 0 ? 1 : -1));
+ float64Vector.set(row, 37.6d * row * (row % 2 == 0 ? 1 : -1));
+ }
+
+ float32Vector.set(10, Float.MIN_VALUE);
+ float64Vector.set(10, Double.MIN_VALUE);
+
+ float32Vector.set(11, Float.MAX_VALUE);
+ float64Vector.set(11, Double.MAX_VALUE);
+
+ float32Vector.set(12, Float.NaN);
+ float64Vector.set(12, Double.NaN);
+
+ float32Vector.set(13, Float.POSITIVE_INFINITY);
+ float64Vector.set(13, Double.POSITIVE_INFINITY);
+
+ float32Vector.set(14, Float.NEGATIVE_INFINITY);
+ float64Vector.set(14, Double.NEGATIVE_INFINITY);
+
+ File dataFile = new File(TMP, "testRoundTripFloatingPoints.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableFloatingPoints() throws Exception {
+
+ // Field definitions
+ FieldType float32Field =
+ new FieldType(true, new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE), null);
+ FieldType float64Field =
+ new FieldType(true, new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ Float4Vector float32Vector =
+ new Float4Vector(new Field("float32", float32Field, null), allocator);
+ Float8Vector float64Vector =
+ new Float8Vector(new Field("float64", float64Field, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors = Arrays.asList(float32Vector, float64Vector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Null values
+ float32Vector.setNull(0);
+ float64Vector.setNull(0);
+
+ // Zero values
+ float32Vector.set(1, 0.0f);
+ float64Vector.set(1, 0.0);
+
+ // Non-zero values
+ float32Vector.set(2, 1.0f);
+ float64Vector.set(2, 1.0);
+
+ File dataFile = new File(TMP, "testRoundTripNullableFloatingPoints.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripStrings() throws Exception {
+
+ // Field definition
+ FieldType stringField = new FieldType(false, new ArrowType.Utf8(), null);
+
+ // Create empty vector
+ BufferAllocator allocator = new RootAllocator();
+ VarCharVector stringVector =
+ new VarCharVector(new Field("string", stringField, null), allocator);
+
+ // Set up VSR
+ List vectors = Arrays.asList(stringVector);
+ int rowCount = 5;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ stringVector.setSafe(0, "Hello world!".getBytes());
+ stringVector.setSafe(1, "<%**\r\n\t\\abc\0$$>".getBytes());
+ stringVector.setSafe(2, "你好世界".getBytes());
+ stringVector.setSafe(3, "مرحبا بالعالم".getBytes());
+ stringVector.setSafe(4, "(P ∧ P ⇒ Q) ⇒ Q".getBytes());
+
+ File dataFile = new File(TMP, "testRoundTripStrings.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableStrings() throws Exception {
+
+ // Field definition
+ FieldType stringField = new FieldType(true, new ArrowType.Utf8(), null);
+
+ // Create empty vector
+ BufferAllocator allocator = new RootAllocator();
+ VarCharVector stringVector =
+ new VarCharVector(new Field("string", stringField, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors = Arrays.asList(stringVector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ stringVector.setNull(0);
+ stringVector.setSafe(1, "".getBytes());
+ stringVector.setSafe(2, "not empty".getBytes());
+
+ File dataFile = new File(TMP, "testRoundTripNullableStrings.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripBinary() throws Exception {
+
+ // Field definition
+ FieldType binaryField = new FieldType(false, new ArrowType.Binary(), null);
+ FieldType fixedField = new FieldType(false, new ArrowType.FixedSizeBinary(5), null);
+
+ // Create empty vector
+ BufferAllocator allocator = new RootAllocator();
+ VarBinaryVector binaryVector =
+ new VarBinaryVector(new Field("binary", binaryField, null), allocator);
+ FixedSizeBinaryVector fixedVector =
+ new FixedSizeBinaryVector(new Field("fixed", fixedField, null), allocator);
+
+ // Set up VSR
+ List vectors = Arrays.asList(binaryVector, fixedVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ binaryVector.setSafe(0, new byte[] {1, 2, 3});
+ binaryVector.setSafe(1, new byte[] {4, 5, 6, 7});
+ binaryVector.setSafe(2, new byte[] {8, 9});
+
+ fixedVector.setSafe(0, new byte[] {1, 2, 3, 4, 5});
+ fixedVector.setSafe(1, new byte[] {4, 5, 6, 7, 8, 9});
+ fixedVector.setSafe(2, new byte[] {8, 9, 10, 11, 12});
+
+ File dataFile = new File(TMP, "testRoundTripBinary.avro");
+
+ roundTripByteArrayTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableBinary() throws Exception {
+
+ // Field definition
+ FieldType binaryField = new FieldType(true, new ArrowType.Binary(), null);
+ FieldType fixedField = new FieldType(true, new ArrowType.FixedSizeBinary(5), null);
+
+ // Create empty vector
+ BufferAllocator allocator = new RootAllocator();
+ VarBinaryVector binaryVector =
+ new VarBinaryVector(new Field("binary", binaryField, null), allocator);
+ FixedSizeBinaryVector fixedVector =
+ new FixedSizeBinaryVector(new Field("fixed", fixedField, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors = Arrays.asList(binaryVector, fixedVector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ binaryVector.setNull(0);
+ binaryVector.setSafe(1, new byte[] {});
+ binaryVector.setSafe(2, new byte[] {10, 11, 12});
+
+ fixedVector.setNull(0);
+ fixedVector.setSafe(1, new byte[] {0, 0, 0, 0, 0});
+ fixedVector.setSafe(2, new byte[] {10, 11, 12, 13, 14});
+
+ File dataFile = new File(TMP, "testRoundTripNullableBinary.avro");
+
+ roundTripByteArrayTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ // Data round trip for logical types, nullable and non-nullable
+
+ @Test
+ public void testRoundTripDecimals() throws Exception {
+
+ // Field definitions
+ FieldType decimal128Field1 = new FieldType(false, new ArrowType.Decimal(38, 10, 128), null);
+ FieldType decimal128Field2 = new FieldType(false, new ArrowType.Decimal(38, 5, 128), null);
+ FieldType decimal256Field1 = new FieldType(false, new ArrowType.Decimal(76, 20, 256), null);
+ FieldType decimal256Field2 = new FieldType(false, new ArrowType.Decimal(76, 10, 256), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ DecimalVector decimal128Vector1 =
+ new DecimalVector(new Field("decimal128_1", decimal128Field1, null), allocator);
+ DecimalVector decimal128Vector2 =
+ new DecimalVector(new Field("decimal128_2", decimal128Field2, null), allocator);
+ Decimal256Vector decimal256Vector1 =
+ new Decimal256Vector(new Field("decimal256_1", decimal256Field1, null), allocator);
+ Decimal256Vector decimal256Vector2 =
+ new Decimal256Vector(new Field("decimal256_2", decimal256Field2, null), allocator);
+
+ // Set up VSR
+ List vectors =
+ Arrays.asList(decimal128Vector1, decimal128Vector2, decimal256Vector1, decimal256Vector2);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ decimal128Vector1.setSafe(
+ 0, new BigDecimal("12345.67890").setScale(10, RoundingMode.UNNECESSARY));
+ decimal128Vector1.setSafe(
+ 1, new BigDecimal("-98765.43210").setScale(10, RoundingMode.UNNECESSARY));
+ decimal128Vector1.setSafe(
+ 2, new BigDecimal("54321.09876").setScale(10, RoundingMode.UNNECESSARY));
+
+ decimal128Vector2.setSafe(
+ 0, new BigDecimal("12345.67890").setScale(5, RoundingMode.UNNECESSARY));
+ decimal128Vector2.setSafe(
+ 1, new BigDecimal("-98765.43210").setScale(5, RoundingMode.UNNECESSARY));
+ decimal128Vector2.setSafe(
+ 2, new BigDecimal("54321.09876").setScale(5, RoundingMode.UNNECESSARY));
+
+ decimal256Vector1.setSafe(
+ 0,
+ new BigDecimal("12345678901234567890.12345678901234567890")
+ .setScale(20, RoundingMode.UNNECESSARY));
+ decimal256Vector1.setSafe(
+ 1,
+ new BigDecimal("-98765432109876543210.98765432109876543210")
+ .setScale(20, RoundingMode.UNNECESSARY));
+ decimal256Vector1.setSafe(
+ 2,
+ new BigDecimal("54321098765432109876.54321098765432109876")
+ .setScale(20, RoundingMode.UNNECESSARY));
+
+ decimal256Vector2.setSafe(
+ 0,
+ new BigDecimal("12345678901234567890.1234567890").setScale(10, RoundingMode.UNNECESSARY));
+ decimal256Vector2.setSafe(
+ 1,
+ new BigDecimal("-98765432109876543210.9876543210")
+ .setScale(10, RoundingMode.UNNECESSARY));
+ decimal256Vector2.setSafe(
+ 2,
+ new BigDecimal("54321098765432109876.5432109876").setScale(10, RoundingMode.UNNECESSARY));
+
+ File dataFile = new File(TMP, "testRoundTripDecimals.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableDecimals() throws Exception {
+
+ // Field definitions
+ FieldType decimal128Field1 = new FieldType(true, new ArrowType.Decimal(38, 10, 128), null);
+ FieldType decimal128Field2 = new FieldType(true, new ArrowType.Decimal(38, 5, 128), null);
+ FieldType decimal256Field1 = new FieldType(true, new ArrowType.Decimal(76, 20, 256), null);
+ FieldType decimal256Field2 = new FieldType(true, new ArrowType.Decimal(76, 10, 256), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ DecimalVector decimal128Vector1 =
+ new DecimalVector(new Field("decimal128_1", decimal128Field1, null), allocator);
+ DecimalVector decimal128Vector2 =
+ new DecimalVector(new Field("decimal128_2", decimal128Field2, null), allocator);
+ Decimal256Vector decimal256Vector1 =
+ new Decimal256Vector(new Field("decimal256_1", decimal256Field1, null), allocator);
+ Decimal256Vector decimal256Vector2 =
+ new Decimal256Vector(new Field("decimal256_2", decimal256Field2, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors =
+ Arrays.asList(decimal128Vector1, decimal128Vector2, decimal256Vector1, decimal256Vector2);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ decimal128Vector1.setNull(0);
+ decimal128Vector1.setSafe(1, BigDecimal.ZERO.setScale(10, RoundingMode.UNNECESSARY));
+ decimal128Vector1.setSafe(
+ 2, new BigDecimal("12345.67890").setScale(10, RoundingMode.UNNECESSARY));
+
+ decimal128Vector2.setNull(0);
+ decimal128Vector2.setSafe(1, BigDecimal.ZERO.setScale(5, RoundingMode.UNNECESSARY));
+ decimal128Vector2.setSafe(
+ 2, new BigDecimal("98765.43210").setScale(5, RoundingMode.UNNECESSARY));
+
+ decimal256Vector1.setNull(0);
+ decimal256Vector1.setSafe(1, BigDecimal.ZERO.setScale(20, RoundingMode.UNNECESSARY));
+ decimal256Vector1.setSafe(
+ 2,
+ new BigDecimal("12345678901234567890.12345678901234567890")
+ .setScale(20, RoundingMode.UNNECESSARY));
+
+ decimal256Vector2.setNull(0);
+ decimal256Vector2.setSafe(1, BigDecimal.ZERO.setScale(10, RoundingMode.UNNECESSARY));
+ decimal256Vector2.setSafe(
+ 2,
+ new BigDecimal("98765432109876543210.9876543210").setScale(10, RoundingMode.UNNECESSARY));
+
+ File dataFile = new File(TMP, "testRoundTripNullableDecimals.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripDates() throws Exception {
+
+ // Field definitions
+ FieldType dateDayField = new FieldType(false, new ArrowType.Date(DateUnit.DAY), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ DateDayVector dateDayVector =
+ new DateDayVector(new Field("dateDay", dateDayField, null), allocator);
+
+ // Set up VSR
+ List vectors = Arrays.asList(dateDayVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ dateDayVector.setSafe(0, (int) LocalDate.now().toEpochDay());
+ dateDayVector.setSafe(1, (int) LocalDate.now().toEpochDay() + 1);
+ dateDayVector.setSafe(2, (int) LocalDate.now().toEpochDay() + 2);
+
+ File dataFile = new File(TMP, "testRoundTripDates.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableDates() throws Exception {
+
+ // Field definitions
+ FieldType dateDayField = new FieldType(true, new ArrowType.Date(DateUnit.DAY), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ DateDayVector dateDayVector =
+ new DateDayVector(new Field("dateDay", dateDayField, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors = Arrays.asList(dateDayVector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ dateDayVector.setNull(0);
+ dateDayVector.setSafe(1, 0);
+ dateDayVector.setSafe(2, (int) LocalDate.now().toEpochDay());
+
+ File dataFile = new File(TMP, "testRoundTripNullableDates.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripTimes() throws Exception {
+
+ // Field definitions
+ FieldType timeMillisField =
+ new FieldType(false, new ArrowType.Time(TimeUnit.MILLISECOND, 32), null);
+ FieldType timeMicrosField =
+ new FieldType(false, new ArrowType.Time(TimeUnit.MICROSECOND, 64), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ TimeMilliVector timeMillisVector =
+ new TimeMilliVector(new Field("timeMillis", timeMillisField, null), allocator);
+ TimeMicroVector timeMicrosVector =
+ new TimeMicroVector(new Field("timeMicros", timeMicrosField, null), allocator);
+
+ // Set up VSR
+ List vectors = Arrays.asList(timeMillisVector, timeMicrosVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ timeMillisVector.setSafe(
+ 0, (int) (ZonedDateTime.now().toLocalTime().toNanoOfDay() / 1000000));
+ timeMillisVector.setSafe(
+ 1, (int) (ZonedDateTime.now().toLocalTime().toNanoOfDay() / 1000000) - 1000);
+ timeMillisVector.setSafe(
+ 2, (int) (ZonedDateTime.now().toLocalTime().toNanoOfDay() / 1000000) - 2000);
+
+ timeMicrosVector.setSafe(0, ZonedDateTime.now().toLocalTime().toNanoOfDay() / 1000);
+ timeMicrosVector.setSafe(1, ZonedDateTime.now().toLocalTime().toNanoOfDay() / 1000 - 1000000);
+ timeMicrosVector.setSafe(2, ZonedDateTime.now().toLocalTime().toNanoOfDay() / 1000 - 2000000);
+
+ File dataFile = new File(TMP, "testRoundTripTimes.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableTimes() throws Exception {
+
+ // Field definitions
+ FieldType timeMillisField =
+ new FieldType(true, new ArrowType.Time(TimeUnit.MILLISECOND, 32), null);
+ FieldType timeMicrosField =
+ new FieldType(true, new ArrowType.Time(TimeUnit.MICROSECOND, 64), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ TimeMilliVector timeMillisVector =
+ new TimeMilliVector(new Field("timeMillis", timeMillisField, null), allocator);
+ TimeMicroVector timeMicrosVector =
+ new TimeMicroVector(new Field("timeMicros", timeMicrosField, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors = Arrays.asList(timeMillisVector, timeMicrosVector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ timeMillisVector.setNull(0);
+ timeMillisVector.setSafe(1, 0);
+ timeMillisVector.setSafe(
+ 2, (int) (ZonedDateTime.now().toLocalTime().toNanoOfDay() / 1000000));
+
+ timeMicrosVector.setNull(0);
+ timeMicrosVector.setSafe(1, 0);
+ timeMicrosVector.setSafe(2, ZonedDateTime.now().toLocalTime().toNanoOfDay() / 1000);
+
+ File dataFile = new File(TMP, "testRoundTripNullableTimes.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripZoneAwareTimestamps() throws Exception {
+
+ // Field definitions
+ FieldType timestampMillisField =
+ new FieldType(false, new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC"), null);
+ FieldType timestampMicrosField =
+ new FieldType(false, new ArrowType.Timestamp(TimeUnit.MICROSECOND, "UTC"), null);
+ FieldType timestampNanosField =
+ new FieldType(false, new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC"), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ TimeStampMilliTZVector timestampMillisVector =
+ new TimeStampMilliTZVector(
+ new Field("timestampMillis", timestampMillisField, null), allocator);
+ TimeStampMicroTZVector timestampMicrosVector =
+ new TimeStampMicroTZVector(
+ new Field("timestampMicros", timestampMicrosField, null), allocator);
+ TimeStampNanoTZVector timestampNanosVector =
+ new TimeStampNanoTZVector(
+ new Field("timestampNanos", timestampNanosField, null), allocator);
+
+ // Set up VSR
+ List vectors =
+ Arrays.asList(timestampMillisVector, timestampMicrosVector, timestampNanosVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ timestampMillisVector.setSafe(0, (int) Instant.now().toEpochMilli());
+ timestampMillisVector.setSafe(1, (int) Instant.now().toEpochMilli() - 1000);
+ timestampMillisVector.setSafe(2, (int) Instant.now().toEpochMilli() - 2000);
+
+ timestampMicrosVector.setSafe(0, Instant.now().toEpochMilli() * 1000);
+ timestampMicrosVector.setSafe(1, (Instant.now().toEpochMilli() - 1000) * 1000);
+ timestampMicrosVector.setSafe(2, (Instant.now().toEpochMilli() - 2000) * 1000);
+
+ timestampNanosVector.setSafe(0, Instant.now().toEpochMilli() * 1000000);
+ timestampNanosVector.setSafe(1, (Instant.now().toEpochMilli() - 1000) * 1000000);
+ timestampNanosVector.setSafe(2, (Instant.now().toEpochMilli() - 2000) * 1000000);
+
+ File dataFile = new File(TMP, "testRoundTripZoneAwareTimestamps.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableZoneAwareTimestamps() throws Exception {
+
+ // Field definitions
+ FieldType timestampMillisField =
+ new FieldType(true, new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC"), null);
+ FieldType timestampMicrosField =
+ new FieldType(true, new ArrowType.Timestamp(TimeUnit.MICROSECOND, "UTC"), null);
+ FieldType timestampNanosField =
+ new FieldType(true, new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC"), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ TimeStampMilliTZVector timestampMillisVector =
+ new TimeStampMilliTZVector(
+ new Field("timestampMillis", timestampMillisField, null), allocator);
+ TimeStampMicroTZVector timestampMicrosVector =
+ new TimeStampMicroTZVector(
+ new Field("timestampMicros", timestampMicrosField, null), allocator);
+ TimeStampNanoTZVector timestampNanosVector =
+ new TimeStampNanoTZVector(
+ new Field("timestampNanos", timestampNanosField, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors =
+ Arrays.asList(timestampMillisVector, timestampMicrosVector, timestampNanosVector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ timestampMillisVector.setNull(0);
+ timestampMillisVector.setSafe(1, 0);
+ timestampMillisVector.setSafe(2, (int) Instant.now().toEpochMilli());
+
+ timestampMicrosVector.setNull(0);
+ timestampMicrosVector.setSafe(1, 0);
+ timestampMicrosVector.setSafe(2, Instant.now().toEpochMilli() * 1000);
+
+ timestampNanosVector.setNull(0);
+ timestampNanosVector.setSafe(1, 0);
+ timestampNanosVector.setSafe(2, Instant.now().toEpochMilli() * 1000000);
+
+ File dataFile = new File(TMP, "testRoundTripNullableZoneAwareTimestamps.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripLocalTimestamps() throws Exception {
+
+ // Field definitions
+ FieldType timestampMillisField =
+ new FieldType(false, new ArrowType.Timestamp(TimeUnit.MILLISECOND, null), null);
+ FieldType timestampMicrosField =
+ new FieldType(false, new ArrowType.Timestamp(TimeUnit.MICROSECOND, null), null);
+ FieldType timestampNanosField =
+ new FieldType(false, new ArrowType.Timestamp(TimeUnit.NANOSECOND, null), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ TimeStampMilliVector timestampMillisVector =
+ new TimeStampMilliVector(
+ new Field("timestampMillis", timestampMillisField, null), allocator);
+ TimeStampMicroVector timestampMicrosVector =
+ new TimeStampMicroVector(
+ new Field("timestampMicros", timestampMicrosField, null), allocator);
+ TimeStampNanoVector timestampNanosVector =
+ new TimeStampNanoVector(new Field("timestampNanos", timestampNanosField, null), allocator);
+
+ // Set up VSR
+ List vectors =
+ Arrays.asList(timestampMillisVector, timestampMicrosVector, timestampNanosVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ timestampMillisVector.setSafe(0, (int) Instant.now().toEpochMilli());
+ timestampMillisVector.setSafe(1, (int) Instant.now().toEpochMilli() - 1000);
+ timestampMillisVector.setSafe(2, (int) Instant.now().toEpochMilli() - 2000);
+
+ timestampMicrosVector.setSafe(0, Instant.now().toEpochMilli() * 1000);
+ timestampMicrosVector.setSafe(1, (Instant.now().toEpochMilli() - 1000) * 1000);
+ timestampMicrosVector.setSafe(2, (Instant.now().toEpochMilli() - 2000) * 1000);
+
+ timestampNanosVector.setSafe(0, Instant.now().toEpochMilli() * 1000000);
+ timestampNanosVector.setSafe(1, (Instant.now().toEpochMilli() - 1000) * 1000000);
+ timestampNanosVector.setSafe(2, (Instant.now().toEpochMilli() - 2000) * 1000000);
+
+ File dataFile = new File(TMP, "testRoundTripLocalTimestamps.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableLocalTimestamps() throws Exception {
+
+ // Field definitions
+ FieldType timestampMillisField =
+ new FieldType(true, new ArrowType.Timestamp(TimeUnit.MILLISECOND, null), null);
+ FieldType timestampMicrosField =
+ new FieldType(true, new ArrowType.Timestamp(TimeUnit.MICROSECOND, null), null);
+ FieldType timestampNanosField =
+ new FieldType(true, new ArrowType.Timestamp(TimeUnit.NANOSECOND, null), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ TimeStampMilliVector timestampMillisVector =
+ new TimeStampMilliVector(
+ new Field("timestampMillis", timestampMillisField, null), allocator);
+ TimeStampMicroVector timestampMicrosVector =
+ new TimeStampMicroVector(
+ new Field("timestampMicros", timestampMicrosField, null), allocator);
+ TimeStampNanoVector timestampNanosVector =
+ new TimeStampNanoVector(new Field("timestampNanos", timestampNanosField, null), allocator);
+
+ int rowCount = 3;
+
+ // Set up VSR
+ List vectors =
+ Arrays.asList(timestampMillisVector, timestampMicrosVector, timestampNanosVector);
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ timestampMillisVector.setNull(0);
+ timestampMillisVector.setSafe(1, 0);
+ timestampMillisVector.setSafe(2, (int) Instant.now().toEpochMilli());
+
+ timestampMicrosVector.setNull(0);
+ timestampMicrosVector.setSafe(1, 0);
+ timestampMicrosVector.setSafe(2, Instant.now().toEpochMilli() * 1000);
+
+ timestampNanosVector.setNull(0);
+ timestampNanosVector.setSafe(1, 0);
+ timestampNanosVector.setSafe(2, Instant.now().toEpochMilli() * 1000000);
+
+ File dataFile = new File(TMP, "testRoundTripNullableLocalTimestamps.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ // Data round trip for containers of primitive and logical types, nullable and non-nullable
+
+ @Test
+ public void testRoundTripLists() throws Exception {
+
+ // Field definitions
+ FieldType intListField = new FieldType(false, new ArrowType.List(), null);
+ FieldType stringListField = new FieldType(false, new ArrowType.List(), null);
+ FieldType dateListField = new FieldType(false, new ArrowType.List(), null);
+
+ Field intField = new Field("item", FieldType.notNullable(new ArrowType.Int(32, true)), null);
+ Field stringField = new Field("item", FieldType.notNullable(new ArrowType.Utf8()), null);
+ Field dateField =
+ new Field("item", FieldType.notNullable(new ArrowType.Date(DateUnit.DAY)), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ ListVector intListVector = new ListVector("intList", allocator, intListField, null);
+ ListVector stringListVector = new ListVector("stringList", allocator, stringListField, null);
+ ListVector dateListVector = new ListVector("dateList", allocator, dateListField, null);
+
+ intListVector.initializeChildrenFromFields(Arrays.asList(intField));
+ stringListVector.initializeChildrenFromFields(Arrays.asList(stringField));
+ dateListVector.initializeChildrenFromFields(Arrays.asList(dateField));
+
+ // Set up VSR
+ List vectors = Arrays.asList(intListVector, stringListVector, dateListVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ FieldWriter intListWriter = intListVector.getWriter();
+ FieldWriter stringListWriter = stringListVector.getWriter();
+ FieldWriter dateListWriter = dateListVector.getWriter();
+
+ // Set test data for intList
+ for (int i = 0; i < rowCount; i++) {
+ intListWriter.startList();
+ for (int j = 0; j < 5 - i; j++) {
+ intListWriter.writeInt(j);
+ }
+ intListWriter.endList();
+ }
+
+ // Set test data for stringList
+ for (int i = 0; i < rowCount; i++) {
+ stringListWriter.startList();
+ for (int j = 0; j < 5 - i; j++) {
+ stringListWriter.writeVarChar("string" + j);
+ }
+ stringListWriter.endList();
+ }
+
+ // Set test data for dateList
+ for (int i = 0; i < rowCount; i++) {
+ dateListWriter.startList();
+ for (int j = 0; j < 5 - i; j++) {
+ dateListWriter.writeDateDay((int) LocalDate.now().plusDays(j).toEpochDay());
+ }
+ dateListWriter.endList();
+ }
+
+ // Update count for the vectors
+ intListVector.setValueCount(rowCount);
+ stringListVector.setValueCount(rowCount);
+ dateListVector.setValueCount(rowCount);
+
+ File dataFile = new File(TMP, "testRoundTripLists.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableLists() throws Exception {
+
+ // Field definitions
+ FieldType nullListType = new FieldType(true, new ArrowType.List(), null);
+ FieldType nonNullListType = new FieldType(false, new ArrowType.List(), null);
+
+ Field nullFieldType = new Field("item", FieldType.nullable(new ArrowType.Int(32, true)), null);
+ Field nonNullFieldType =
+ new Field("item", FieldType.notNullable(new ArrowType.Int(32, true)), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ ListVector nullEntriesVector =
+ new ListVector("nullEntriesVector", allocator, nonNullListType, null);
+ ListVector nullListVector = new ListVector("nullListVector", allocator, nullListType, null);
+ ListVector nullBothVector = new ListVector("nullBothVector", allocator, nullListType, null);
+
+ nullEntriesVector.initializeChildrenFromFields(Arrays.asList(nullFieldType));
+ nullListVector.initializeChildrenFromFields(Arrays.asList(nonNullFieldType));
+ nullBothVector.initializeChildrenFromFields(Arrays.asList(nullFieldType));
+
+ // Set up VSR
+ List vectors = Arrays.asList(nullEntriesVector, nullListVector, nullBothVector);
+ int rowCount = 4;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data for nullEntriesVector
+ FieldWriter nullEntriesWriter = nullEntriesVector.getWriter();
+ nullEntriesWriter.startList();
+ nullEntriesWriter.integer().writeNull();
+ nullEntriesWriter.integer().writeNull();
+ nullEntriesWriter.endList();
+ nullEntriesWriter.startList();
+ nullEntriesWriter.integer().writeInt(0);
+ nullEntriesWriter.integer().writeInt(0);
+ nullEntriesWriter.endList();
+ nullEntriesWriter.startList();
+ nullEntriesWriter.integer().writeInt(123);
+ nullEntriesWriter.integer().writeInt(456);
+ nullEntriesWriter.endList();
+ nullEntriesWriter.startList();
+ nullEntriesWriter.integer().writeInt(789);
+ nullEntriesWriter.integer().writeInt(789);
+ nullEntriesWriter.endList();
+
+ // Set test data for nullListVector
+ FieldWriter nullListWriter = nullListVector.getWriter();
+ nullListWriter.writeNull();
+ nullListWriter.setPosition(1); // writeNull() does not inc. idx() on list vector
+ nullListWriter.startList();
+ nullListWriter.integer().writeInt(0);
+ nullListWriter.integer().writeInt(0);
+ nullListWriter.endList();
+ nullEntriesWriter.startList();
+ nullEntriesWriter.integer().writeInt(123);
+ nullEntriesWriter.integer().writeInt(456);
+ nullEntriesWriter.endList();
+ nullEntriesWriter.startList();
+ nullEntriesWriter.integer().writeInt(789);
+ nullEntriesWriter.integer().writeInt(789);
+ nullEntriesWriter.endList();
+
+ // Set test data for nullBothVector
+ FieldWriter nullBothWriter = nullBothVector.getWriter();
+ nullBothWriter.writeNull();
+ nullBothWriter.setPosition(1);
+ nullBothWriter.startList();
+ nullBothWriter.integer().writeNull();
+ nullBothWriter.integer().writeNull();
+ nullBothWriter.endList();
+ nullListWriter.startList();
+ nullListWriter.integer().writeInt(0);
+ nullListWriter.integer().writeInt(0);
+ nullListWriter.endList();
+ nullEntriesWriter.startList();
+ nullEntriesWriter.integer().writeInt(123);
+ nullEntriesWriter.integer().writeInt(456);
+ nullEntriesWriter.endList();
+
+ // Update count for the vectors
+ nullListVector.setValueCount(4);
+ nullEntriesVector.setValueCount(4);
+ nullBothVector.setValueCount(4);
+
+ File dataFile = new File(TMP, "testRoundTripNullableLists.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripMap() throws Exception {
+
+ // Field definitions
+ FieldType intMapField = new FieldType(false, new ArrowType.Map(false), null);
+ FieldType stringMapField = new FieldType(false, new ArrowType.Map(false), null);
+ FieldType dateMapField = new FieldType(false, new ArrowType.Map(false), null);
+
+ Field keyField = new Field("key", FieldType.notNullable(new ArrowType.Utf8()), null);
+ Field intField = new Field("value", FieldType.notNullable(new ArrowType.Int(32, true)), null);
+ Field stringField = new Field("value", FieldType.notNullable(new ArrowType.Utf8()), null);
+ Field dateField =
+ new Field("value", FieldType.notNullable(new ArrowType.Date(DateUnit.DAY)), null);
+
+ Field intEntryField =
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(keyField, intField));
+ Field stringEntryField =
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(keyField, stringField));
+ Field dateEntryField =
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(keyField, dateField));
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ MapVector intMapVector = new MapVector("intMap", allocator, intMapField, null);
+ MapVector stringMapVector = new MapVector("stringMap", allocator, stringMapField, null);
+ MapVector dateMapVector = new MapVector("dateMap", allocator, dateMapField, null);
+
+ intMapVector.initializeChildrenFromFields(Arrays.asList(intEntryField));
+ stringMapVector.initializeChildrenFromFields(Arrays.asList(stringEntryField));
+ dateMapVector.initializeChildrenFromFields(Arrays.asList(dateEntryField));
+
+ // Set up VSR
+ List vectors = Arrays.asList(intMapVector, stringMapVector, dateMapVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Total number of entries that will be writen to each vector
+ int entryCount = 5 + 4 + 3;
+
+ // Set test data for intList
+ BaseWriter.MapWriter writer = intMapVector.getWriter();
+ for (int i = 0; i < rowCount; i++) {
+ writer.startMap();
+ for (int j = 0; j < 5 - i; j++) {
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key" + j);
+ writer.value().integer().writeInt(j);
+ writer.endEntry();
+ }
+ writer.endMap();
+ }
+
+ // Update count for data vector (map writer does not do this)
+ intMapVector.getDataVector().setValueCount(entryCount);
+
+ // Set test data for stringList
+ BaseWriter.MapWriter stringWriter = stringMapVector.getWriter();
+ for (int i = 0; i < rowCount; i++) {
+ stringWriter.startMap();
+ for (int j = 0; j < 5 - i; j++) {
+ stringWriter.startEntry();
+ stringWriter.key().varChar().writeVarChar("key" + j);
+ stringWriter.value().varChar().writeVarChar("string" + j);
+ stringWriter.endEntry();
+ }
+ stringWriter.endMap();
+ }
+
+ // Update count for the vectors
+ intMapVector.setValueCount(rowCount);
+ stringMapVector.setValueCount(rowCount);
+ dateMapVector.setValueCount(rowCount);
+
+ // Update count for data vector (map writer does not do this)
+ stringMapVector.getDataVector().setValueCount(entryCount);
+
+ // Set test data for dateList
+ BaseWriter.MapWriter dateWriter = dateMapVector.getWriter();
+ for (int i = 0; i < rowCount; i++) {
+ dateWriter.startMap();
+ for (int j = 0; j < 5 - i; j++) {
+ dateWriter.startEntry();
+ dateWriter.key().varChar().writeVarChar("key" + j);
+ dateWriter.value().dateDay().writeDateDay((int) LocalDate.now().plusDays(j).toEpochDay());
+ dateWriter.endEntry();
+ }
+ dateWriter.endMap();
+ }
+
+ // Update count for data vector (map writer does not do this)
+ dateMapVector.getDataVector().setValueCount(entryCount);
+
+ File dataFile = new File(TMP, "testRoundTripMap.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableMap() throws Exception {
+
+ // Field definitions
+ FieldType nullMapType = new FieldType(true, new ArrowType.Map(false), null);
+ FieldType nonNullMapType = new FieldType(false, new ArrowType.Map(false), null);
+
+ Field keyField = new Field("key", FieldType.notNullable(new ArrowType.Utf8()), null);
+ Field nullFieldType = new Field("value", FieldType.nullable(new ArrowType.Int(32, true)), null);
+ Field nonNullFieldType =
+ new Field("value", FieldType.notNullable(new ArrowType.Int(32, true)), null);
+ Field nullEntryField =
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(keyField, nullFieldType));
+ Field nonNullEntryField =
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(keyField, nonNullFieldType));
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ MapVector nullEntriesVector =
+ new MapVector("nullEntriesVector", allocator, nonNullMapType, null);
+ MapVector nullMapVector = new MapVector("nullMapVector", allocator, nullMapType, null);
+ MapVector nullBothVector = new MapVector("nullBothVector", allocator, nullMapType, null);
+
+ nullEntriesVector.initializeChildrenFromFields(Arrays.asList(nullEntryField));
+ nullMapVector.initializeChildrenFromFields(Arrays.asList(nonNullEntryField));
+ nullBothVector.initializeChildrenFromFields(Arrays.asList(nullEntryField));
+
+ // Set up VSR
+ List vectors = Arrays.asList(nullEntriesVector, nullMapVector, nullBothVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data for intList
+ BaseWriter.MapWriter writer = nullEntriesVector.getWriter();
+ writer.startMap();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key0");
+ writer.value().integer().writeNull();
+ writer.endEntry();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key1");
+ writer.value().integer().writeNull();
+ writer.endEntry();
+ writer.endMap();
+ writer.startMap();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key2");
+ writer.value().integer().writeInt(0);
+ writer.endEntry();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key3");
+ writer.value().integer().writeInt(0);
+ writer.endEntry();
+ writer.endMap();
+ writer.startMap();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key4");
+ writer.value().integer().writeInt(123);
+ writer.endEntry();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key5");
+ writer.value().integer().writeInt(456);
+ writer.endEntry();
+ writer.endMap();
+
+ // Set test data for stringList
+ BaseWriter.MapWriter nullMapWriter = nullMapVector.getWriter();
+ nullMapWriter.writeNull();
+ nullMapWriter.setPosition(1); // writeNull() does not inc. idx() on map (list) vector
+ nullMapWriter.startMap();
+ nullMapWriter.startEntry();
+ nullMapWriter.key().varChar().writeVarChar("key2");
+ nullMapWriter.value().integer().writeInt(0);
+ nullMapWriter.endEntry();
+ writer.startMap();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key3");
+ writer.value().integer().writeInt(0);
+ writer.endEntry();
+ nullMapWriter.endMap();
+ nullMapWriter.startMap();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key4");
+ writer.value().integer().writeInt(123);
+ writer.endEntry();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key5");
+ writer.value().integer().writeInt(456);
+ writer.endEntry();
+ nullMapWriter.endMap();
+
+ // Set test data for dateList
+ BaseWriter.MapWriter nullBothWriter = nullBothVector.getWriter();
+ nullBothWriter.writeNull();
+ nullBothWriter.setPosition(1);
+ nullBothWriter.startMap();
+ nullBothWriter.startEntry();
+ nullBothWriter.key().varChar().writeVarChar("key2");
+ nullBothWriter.value().integer().writeNull();
+ nullBothWriter.endEntry();
+ nullBothWriter.startEntry();
+ nullBothWriter.key().varChar().writeVarChar("key3");
+ nullBothWriter.value().integer().writeNull();
+ nullBothWriter.endEntry();
+ nullBothWriter.endMap();
+ nullBothWriter.startMap();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key4");
+ writer.value().integer().writeInt(123);
+ writer.endEntry();
+ writer.startEntry();
+ writer.key().varChar().writeVarChar("key5");
+ writer.value().integer().writeInt(456);
+ writer.endEntry();
+ nullBothWriter.endMap();
+
+ // Update count for the vectors
+ nullEntriesVector.setValueCount(3);
+ nullMapVector.setValueCount(3);
+ nullBothVector.setValueCount(3);
+
+ File dataFile = new File(TMP, "testRoundTripNullableMap.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripStruct() throws Exception {
+
+ // Field definitions
+ FieldType structFieldType = new FieldType(false, new ArrowType.Struct(), null);
+ Field intField =
+ new Field("intField", FieldType.notNullable(new ArrowType.Int(32, true)), null);
+ Field stringField = new Field("stringField", FieldType.notNullable(new ArrowType.Utf8()), null);
+ Field dateField =
+ new Field("dateField", FieldType.notNullable(new ArrowType.Date(DateUnit.DAY)), null);
+
+ // Create empty vector
+ BufferAllocator allocator = new RootAllocator();
+ StructVector structVector = new StructVector("struct", allocator, structFieldType, null);
+ structVector.initializeChildrenFromFields(Arrays.asList(intField, stringField, dateField));
+
+ // Set up VSR
+ List vectors = Arrays.asList(structVector);
+ int rowCount = 3;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data
+ BaseWriter.StructWriter structWriter = structVector.getWriter();
+
+ for (int i = 0; i < rowCount; i++) {
+ structWriter.start();
+ structWriter.integer("intField").writeInt(i);
+ structWriter.varChar("stringField").writeVarChar("string" + i);
+ structWriter.dateDay("dateField").writeDateDay((int) LocalDate.now().toEpochDay() + i);
+ structWriter.end();
+ }
+
+ File dataFile = new File(TMP, "testRoundTripStruct.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+
+ @Test
+ public void testRoundTripNullableStructs() throws Exception {
+
+ // Field definitions
+ FieldType structFieldType = new FieldType(false, new ArrowType.Struct(), null);
+ FieldType nullableStructFieldType = new FieldType(true, new ArrowType.Struct(), null);
+ Field intField =
+ new Field("intField", FieldType.notNullable(new ArrowType.Int(32, true)), null);
+ Field nullableIntField =
+ new Field("nullableIntField", FieldType.nullable(new ArrowType.Int(32, true)), null);
+
+ // Create empty vectors
+ BufferAllocator allocator = new RootAllocator();
+ StructVector structVector = new StructVector("struct", allocator, structFieldType, null);
+ StructVector nullableStructVector =
+ new StructVector("nullableStruct", allocator, nullableStructFieldType, null);
+ structVector.initializeChildrenFromFields(Arrays.asList(intField, nullableIntField));
+ nullableStructVector.initializeChildrenFromFields(Arrays.asList(intField, nullableIntField));
+
+ // Set up VSR
+ List vectors = Arrays.asList(structVector, nullableStructVector);
+ int rowCount = 4;
+
+ try (VectorSchemaRoot root = new VectorSchemaRoot(vectors)) {
+
+ root.setRowCount(rowCount);
+ root.allocateNew();
+
+ // Set test data for structVector
+ BaseWriter.StructWriter structWriter = structVector.getWriter();
+ for (int i = 0; i < rowCount; i++) {
+ structWriter.setPosition(i);
+ structWriter.start();
+ structWriter.integer("intField").writeInt(i);
+ if (i % 2 == 0) {
+ structWriter.integer("nullableIntField").writeInt(i * 10);
+ } else {
+ structWriter.integer("nullableIntField").writeNull();
+ }
+ structWriter.end();
+ }
+
+ // Set test data for nullableStructVector
+ BaseWriter.StructWriter nullableStructWriter = nullableStructVector.getWriter();
+ for (int i = 0; i < rowCount; i++) {
+ nullableStructWriter.setPosition(i);
+ if (i >= 2) {
+ nullableStructWriter.start();
+ nullableStructWriter.integer("intField").writeInt(i);
+ if (i % 2 == 0) {
+ nullableStructWriter.integer("nullableIntField").writeInt(i * 10);
+ } else {
+ nullableStructWriter.integer("nullableIntField").writeNull();
+ }
+ nullableStructWriter.end();
+ } else {
+ nullableStructWriter.writeNull();
+ }
+ }
+
+ // Update count for the vector
+ structVector.setValueCount(rowCount);
+ nullableStructVector.setValueCount(rowCount);
+
+ File dataFile = new File(TMP, "testRoundTripNullableStructs.avro");
+
+ roundTripTest(root, allocator, dataFile, rowCount);
+ }
+ }
+}
diff --git a/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/RoundTripSchemaTest.java b/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/RoundTripSchemaTest.java
new file mode 100644
index 0000000000..864e2c8b59
--- /dev/null
+++ b/adapter/avro/src/test/java/org/apache/arrow/adapter/avro/RoundTripSchemaTest.java
@@ -0,0 +1,443 @@
+/*
+ * 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.avro;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.apache.arrow.vector.types.DateUnit;
+import org.apache.arrow.vector.types.FloatingPointPrecision;
+import org.apache.arrow.vector.types.TimeUnit;
+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.avro.Schema;
+import org.junit.jupiter.api.Test;
+
+public class RoundTripSchemaTest {
+
+ private void doRoundTripTest(List fields) {
+
+ AvroToArrowConfig config = new AvroToArrowConfig(null, 1, null, Collections.emptySet(), false);
+
+ Schema avroSchema = ArrowToAvroUtils.createAvroSchema(fields, "TestRecord");
+ org.apache.arrow.vector.types.pojo.Schema arrowSchema =
+ AvroToArrowUtils.createArrowSchema(avroSchema, config);
+
+ // Compare string representations - equality not defined for logical types
+ assertEquals(fields, arrowSchema.getFields());
+ }
+
+ // Schema round trip for primitive types, nullable and non-nullable
+
+ @Test
+ public void testRoundTripNullType() {
+
+ List fields =
+ Arrays.asList(new Field("nullType", FieldType.notNullable(new ArrowType.Null()), null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripBooleanType() {
+
+ List fields =
+ Arrays.asList(
+ new Field("nullableBool", FieldType.nullable(new ArrowType.Bool()), null),
+ new Field("nonNullableBool", FieldType.notNullable(new ArrowType.Bool()), null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripIntegerTypes() {
+
+ AvroToArrowConfig config = new AvroToArrowConfig(null, 1, null, Collections.emptySet(), false);
+
+ // Only round trip types with direct equivalent in Avro
+
+ List fields =
+ Arrays.asList(
+ new Field("nullableInt32", FieldType.nullable(new ArrowType.Int(32, true)), null),
+ new Field("nonNullableInt32", FieldType.notNullable(new ArrowType.Int(32, true)), null),
+ new Field("nullableInt64", FieldType.nullable(new ArrowType.Int(64, true)), null),
+ new Field(
+ "nonNullableInt64", FieldType.notNullable(new ArrowType.Int(64, true)), null));
+
+ Schema avroSchema = ArrowToAvroUtils.createAvroSchema(fields, "TestRecord");
+ org.apache.arrow.vector.types.pojo.Schema arrowSchema =
+ AvroToArrowUtils.createArrowSchema(avroSchema, config);
+
+ // Exact match on fields after round trip
+ assertEquals(fields, arrowSchema.getFields());
+ }
+
+ @Test
+ public void testRoundTripFloatingPointTypes() {
+
+ // Only round trip types with direct equivalent in Avro
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableFloat32",
+ FieldType.nullable(new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE)),
+ null),
+ new Field(
+ "nonNullableFloat32",
+ FieldType.notNullable(new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE)),
+ null),
+ new Field(
+ "nullableFloat64",
+ FieldType.nullable(new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
+ null),
+ new Field(
+ "nonNullableFloat64",
+ FieldType.notNullable(new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
+ null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripStringTypes() {
+
+ List fields =
+ Arrays.asList(
+ new Field("nullableUtf8", FieldType.nullable(new ArrowType.Utf8()), null),
+ new Field("nonNullableUtf8", FieldType.notNullable(new ArrowType.Utf8()), null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripBinaryTypes() {
+
+ List fields =
+ Arrays.asList(
+ new Field("nullableBinary", FieldType.nullable(new ArrowType.Binary()), null),
+ new Field("nonNullableBinary", FieldType.notNullable(new ArrowType.Binary()), null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripFixedSizeBinaryTypes() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableFixedSizeBinary",
+ FieldType.nullable(new ArrowType.FixedSizeBinary(10)),
+ null),
+ new Field(
+ "nonNullableFixedSizeBinary",
+ FieldType.notNullable(new ArrowType.FixedSizeBinary(10)),
+ null));
+
+ doRoundTripTest(fields);
+ }
+
+ // Schema round trip for logical types, nullable and non-nullable
+
+ @Test
+ public void testRoundTripDecimalTypes() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableDecimal128", FieldType.nullable(new ArrowType.Decimal(10, 2, 128)), null),
+ new Field(
+ "nonNullableDecimal1281",
+ FieldType.notNullable(new ArrowType.Decimal(10, 2, 128)),
+ null),
+ new Field(
+ "nonNullableDecimal1282",
+ FieldType.notNullable(new ArrowType.Decimal(15, 5, 128)),
+ null),
+ new Field(
+ "nonNullableDecimal1283",
+ FieldType.notNullable(new ArrowType.Decimal(20, 10, 128)),
+ null),
+ new Field(
+ "nullableDecimal256", FieldType.nullable(new ArrowType.Decimal(55, 15, 256)), null),
+ new Field(
+ "nonNullableDecimal2561",
+ FieldType.notNullable(new ArrowType.Decimal(55, 25, 256)),
+ null),
+ new Field(
+ "nonNullableDecimal2562",
+ FieldType.notNullable(new ArrowType.Decimal(25, 8, 256)),
+ null),
+ new Field(
+ "nonNullableDecimal2563",
+ FieldType.notNullable(new ArrowType.Decimal(60, 50, 256)),
+ null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripDateTypes() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableDateDay", FieldType.nullable(new ArrowType.Date(DateUnit.DAY)), null),
+ new Field(
+ "nonNullableDateDay",
+ FieldType.notNullable(new ArrowType.Date(DateUnit.DAY)),
+ null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripTimeTypes() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableTimeMillis",
+ FieldType.nullable(new ArrowType.Time(TimeUnit.MILLISECOND, 32)),
+ null),
+ new Field(
+ "nonNullableTimeMillis",
+ FieldType.notNullable(new ArrowType.Time(TimeUnit.MILLISECOND, 32)),
+ null),
+ new Field(
+ "nullableTimeMicros",
+ FieldType.nullable(new ArrowType.Time(TimeUnit.MICROSECOND, 64)),
+ null),
+ new Field(
+ "nonNullableTimeMicros",
+ FieldType.notNullable(new ArrowType.Time(TimeUnit.MICROSECOND, 64)),
+ null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripZoneAwareTimestampTypes() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableTimestampMillisTz",
+ FieldType.nullable(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC")),
+ null),
+ new Field(
+ "nonNullableTimestampMillisTz",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC")),
+ null),
+ new Field(
+ "nullableTimestampMicrosTz",
+ FieldType.nullable(new ArrowType.Timestamp(TimeUnit.MICROSECOND, "UTC")),
+ null),
+ new Field(
+ "nonNullableTimestampMicrosTz",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.MICROSECOND, "UTC")),
+ null),
+ new Field(
+ "nullableTimestampNanosTz",
+ FieldType.nullable(new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC")),
+ null),
+ new Field(
+ "nonNullableTimestampNanosTz",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC")),
+ null));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripLocalTimestampTypes() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableTimestampMillis",
+ FieldType.nullable(new ArrowType.Timestamp(TimeUnit.MILLISECOND, null)),
+ null),
+ new Field(
+ "nonNullableTimestampMillis",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.MILLISECOND, null)),
+ null),
+ new Field(
+ "nullableTimestampMicros",
+ FieldType.nullable(new ArrowType.Timestamp(TimeUnit.MICROSECOND, null)),
+ null),
+ new Field(
+ "nonNullableTimestampMicros",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.MICROSECOND, null)),
+ null),
+ new Field(
+ "nullableTimestampNanos",
+ FieldType.nullable(new ArrowType.Timestamp(TimeUnit.NANOSECOND, null)),
+ null),
+ new Field(
+ "nonNullableTimestampNanos",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.NANOSECOND, null)),
+ null));
+
+ doRoundTripTest(fields);
+ }
+
+ // Schema round trip for complex types, where the contents are primitive and logical types
+
+ @Test
+ public void testRoundTripListType() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableIntList",
+ FieldType.nullable(new ArrowType.List()),
+ Arrays.asList(
+ new Field("$data$", FieldType.nullable(new ArrowType.Int(32, true)), null))),
+ new Field(
+ "nullableDoubleList",
+ FieldType.nullable(new ArrowType.List()),
+ Arrays.asList(
+ new Field(
+ "$data$",
+ FieldType.notNullable(
+ new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
+ null))),
+ new Field(
+ "nonNullableDecimalList",
+ FieldType.notNullable(new ArrowType.List()),
+ Arrays.asList(
+ new Field(
+ "$data$", FieldType.nullable(new ArrowType.Decimal(10, 2, 128)), null))),
+ new Field(
+ "nonNullableTimestampList",
+ FieldType.notNullable(new ArrowType.List()),
+ Arrays.asList(
+ new Field(
+ "$data$",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC")),
+ null))));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripMapType() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableMapWithNullableInt",
+ FieldType.nullable(new ArrowType.Map(false)),
+ Arrays.asList(
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(
+ new Field("key", FieldType.notNullable(new ArrowType.Utf8()), null),
+ new Field(
+ "value", FieldType.nullable(new ArrowType.Int(32, true)), null))))),
+ new Field(
+ "nullableMapWithNonNullableDouble",
+ FieldType.nullable(new ArrowType.Map(false)),
+ Arrays.asList(
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(
+ new Field("key", FieldType.notNullable(new ArrowType.Utf8()), null),
+ new Field(
+ "value",
+ FieldType.notNullable(
+ new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
+ null))))),
+ new Field(
+ "nonNullableMapWithNullableDecimal",
+ FieldType.notNullable(new ArrowType.Map(false)),
+ Arrays.asList(
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(
+ new Field("key", FieldType.notNullable(new ArrowType.Utf8()), null),
+ new Field(
+ "value",
+ FieldType.nullable(new ArrowType.Decimal(10, 2, 128)),
+ null))))),
+ new Field(
+ "nonNullableMapWithNonNullableTimestamp",
+ FieldType.notNullable(new ArrowType.Map(false)),
+ Arrays.asList(
+ new Field(
+ "entries",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(
+ new Field("key", FieldType.notNullable(new ArrowType.Utf8()), null),
+ new Field(
+ "value",
+ FieldType.notNullable(
+ new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC")),
+ null))))));
+
+ doRoundTripTest(fields);
+ }
+
+ @Test
+ public void testRoundTripStructType() {
+
+ List fields =
+ Arrays.asList(
+ new Field(
+ "nullableRecord",
+ FieldType.nullable(new ArrowType.Struct()),
+ Arrays.asList(
+ new Field("field1", FieldType.nullable(new ArrowType.Int(32, true)), null),
+ new Field(
+ "field2",
+ FieldType.notNullable(
+ new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
+ null),
+ new Field(
+ "field3", FieldType.nullable(new ArrowType.Decimal(10, 2, 128)), null),
+ new Field(
+ "field4",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC")),
+ null))),
+ new Field(
+ "nonNullableRecord",
+ FieldType.notNullable(new ArrowType.Struct()),
+ Arrays.asList(
+ new Field("field1", FieldType.nullable(new ArrowType.Int(32, true)), null),
+ new Field(
+ "field2",
+ FieldType.notNullable(
+ new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
+ null),
+ new Field(
+ "field3", FieldType.nullable(new ArrowType.Decimal(10, 2, 128)), null),
+ new Field(
+ "field4",
+ FieldType.notNullable(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC")),
+ null))));
+
+ doRoundTripTest(fields);
+ }
+}
diff --git a/adapter/avro/src/test/resources/schema/logical/test_decimal_invalid1.avsc b/adapter/avro/src/test/resources/schema/logical/test_decimal_invalid1.avsc
index 18d7d63fc7..c1867811c7 100644
--- a/adapter/avro/src/test/resources/schema/logical/test_decimal_invalid1.avsc
+++ b/adapter/avro/src/test/resources/schema/logical/test_decimal_invalid1.avsc
@@ -20,6 +20,6 @@
"name": "test",
"type": "bytes",
"logicalType" : "decimal",
- "precision": 39,
+ "precision": 77,
"scale": 2
}
diff --git a/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_micros.avsc b/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_micros.avsc
new file mode 100644
index 0000000000..db456e8a84
--- /dev/null
+++ b/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_micros.avsc
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+{
+ "namespace": "org.apache.arrow.avro",
+ "name": "test",
+ "type": "long",
+ "logicalType" : "local-timestamp-micros"
+}
diff --git a/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_millis.avsc b/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_millis.avsc
new file mode 100644
index 0000000000..6a3cf9bccb
--- /dev/null
+++ b/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_millis.avsc
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+{
+ "namespace": "org.apache.arrow.avro",
+ "name": "test",
+ "type": "long",
+ "logicalType" : "local-timestamp-millis"
+}
diff --git a/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_nanos.avsc b/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_nanos.avsc
new file mode 100644
index 0000000000..96ca8bbfa4
--- /dev/null
+++ b/adapter/avro/src/test/resources/schema/logical/test_local_timestamp_nanos.avsc
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+{
+ "namespace": "org.apache.arrow.avro",
+ "name": "test",
+ "type": "long",
+ "logicalType" : "local-timestamp-nanos"
+}
diff --git a/adapter/avro/src/test/resources/schema/logical/test_timestamp_nanos.avsc b/adapter/avro/src/test/resources/schema/logical/test_timestamp_nanos.avsc
new file mode 100644
index 0000000000..9e05eab408
--- /dev/null
+++ b/adapter/avro/src/test/resources/schema/logical/test_timestamp_nanos.avsc
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+{
+ "namespace": "org.apache.arrow.avro",
+ "name": "test",
+ "type": "long",
+ "logicalType" : "timestamp-nanos"
+}
From 0d296dffd39e14dbcf288c2da873777e08091cbd Mon Sep 17 00:00:00 2001
From: Sutou Kouhei
Date: Wed, 23 Apr 2025 10:29:08 +0900
Subject: [PATCH 014/271] GH-587: [Release] Add .env description to
dev/release/README.md (#724)
## What's Changed
Add missing `dev/release/.env` description to `dev/release/README.md`.
This also moves `GH_TOKEN` to `dev/release/.env`.
Closes #587.
---
dev/release/.env.example | 5 ++
dev/release/README.md | 99 +++++++++++++++++++++++--------------
dev/release/bump_version.sh | 7 +++
dev/release/release.sh | 7 +++
dev/release/release_rc.sh | 14 +++---
5 files changed, 89 insertions(+), 43 deletions(-)
diff --git a/dev/release/.env.example b/dev/release/.env.example
index cc7fd58bfc..4a57e34ed3 100644
--- a/dev/release/.env.example
+++ b/dev/release/.env.example
@@ -15,6 +15,11 @@
# specific language governing permissions and limitations
# under the License.
+# The GitHub token to upload artifacts to GitHub Release.
+#
+# You must set this.
+#GH_TOKEN=secret
+
# The GPG key ID to sign artifacts. The GPG key ID must be registered
# to both of the followings:
#
diff --git a/dev/release/README.md b/dev/release/README.md
index 9069a4fdaf..8aee0fd106 100644
--- a/dev/release/README.md
+++ b/dev/release/README.md
@@ -27,45 +27,38 @@
4. Publish (detailed later)
5. Bump version for new development (detailed later)
-### Bump version for new release
+### Prepare release environment
-Run `dev/release/bump_version.sh` on a working copy of your fork not
-`git@github.com:apache/arrow-java`:
+This step is needed only when you act as a release manager first time.
-```console
-$ git clone git@github.com:${YOUR_GITHUB_ACCOUNT}/arrow-java.git arrow-java.${YOUR_GITHUB_ACCOUNT}
-$ cd arrow-java.${YOUR_GITHUB_ACCOUNT}
-$ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/bump_version.sh ${NEW_VERSION}
-```
+We use the following variables in multiple steps:
-Here is an example to bump version to 19.0.0:
+* `GH_TOKEN`: GitHub personal access token to automate GitHub related
+ operations
+* `GPG_KEY_ID`: PGP key ID that is used for signing official artifacts
+ by GnuPG
-```
-$ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/bump_version.sh 19.0.0
-```
+We use `dev/release/.env` to share these variables in multiple
+steps. You can use `dev/release/.env.example` as a template:
-It creates a feature branch and adds a commit that bumps version. This
-opens a pull request from the feature branch by `gh pr create`. So you
-need `gh` command and GitHub personal access token.
+```console
+$ cp dev/release/.env{.example,}
+$ chmod go-r dev/release/.env
+$ editor dev/release/.env
+```
-See also:
+See
https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
+how to prepare GitHub personal access token for `GH_TOKEN`.
-We need to merge the pull request before we cut a RC. If we try cut a
-RC without merging the pull request, the script to cut a RC is failed.
+Note that you also need to install `gh` command because our scripts
+use `gh` command to use GitHub API. See
+https://github.com/cli/cli#installation how to install `gh`
+command.
-### Prepare RC and vote
-
-You can use `dev/release/release_rc.sh`.
-
-Requirements to run `release_rc.sh`:
-
- * You must be an Apache Arrow committer or PMC member
- * You must prepare your PGP key for signing
- * You must configure Maven
-
-If you don't have a PGP key,
-https://infra.apache.org/release-signing.html#generate may be helpful.
+If you don't have a PGP key for `GPG_KEY_ID`, see
+https://infra.apache.org/release-signing.html#genegrate how to
+generate your PGP key.
Your PGP key must be registered to the followings:
@@ -85,6 +78,40 @@ $ head KEYS
$ svn ci KEYS
```
+### Bump version for new release
+
+Run `dev/release/bump_version.sh` on a working copy of your fork not
+`git@github.com:apache/arrow-java`:
+
+```console
+$ git clone git@github.com:${YOUR_GITHUB_ACCOUNT}/arrow-java.git arrow-java.${YOUR_GITHUB_ACCOUNT}
+$ cd arrow-java.${YOUR_GITHUB_ACCOUNT}
+$ dev/release/bump_version.sh ${NEW_VERSION}
+```
+
+Here is an example to bump version to 19.0.0:
+
+```
+$ dev/release/bump_version.sh 19.0.0
+```
+
+It creates a feature branch and adds a commit that bumps version. This
+opens a pull request from the feature branch.
+
+We need to merge the pull request before we cut a RC. If we try
+cutting a RC without merging the pull request, the script to cut a RC
+is failed.
+
+### Prepare RC and vote
+
+You can use `dev/release/release_rc.sh`.
+
+Requirements to run `release_rc.sh`:
+
+ * You must be an Apache Arrow committer or PMC member
+ * You must prepare your PGP key for signing
+ * You must configure Maven
+
Configure Maven to publish artifacts to Apache repositories. You will
need to setup a master password at `~/.m2/settings-security.xml` and
`~/.m2/settings.xml` as specified on [the Apache
@@ -102,7 +129,7 @@ Run `dev/release/release_rc.sh` on a working copy of
```console
$ git clone git@github.com:apache/arrow-java.git
$ cd arrow-java
-$ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/release_rc.sh ${RC}
+$ dev/release/release_rc.sh ${RC}
(Send a vote email to dev@arrow.apache.org.
You can use a draft shown by release_rc.sh for the email.)
```
@@ -110,7 +137,7 @@ $ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/release_rc.sh ${RC}
Here is an example to release RC1:
```console
-$ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/release_rc.sh 1
+$ dev/release/release_rc.sh 1
```
The argument of `release_rc.sh` is the RC number. If RC1 has a
@@ -128,13 +155,13 @@ Run `dev/release/release.sh` on a working copy of
archive to apache.org:
```console
-$ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/release.sh ${VERSION} ${RC}
+$ dev/release/release.sh ${VERSION} ${RC}
```
Here is an example to release 19.0.0 RC1:
```console
-$ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/release.sh 19.0.0 1
+$ dev/release/release.sh 19.0.0 1
```
Add the release to ASF's report database via [Apache Committee Report
@@ -160,13 +187,13 @@ Run `dev/release/bump_version.sh` on a working copy of your fork not
```console
$ git clone git@github.com:${YOUR_GITHUB_ACCOUNT}/arrow-java.git arrow-java.${YOUR_GITHUB_ACCOUNT}
$ cd arrow-java.${YOUR_GITHUB_ACCOUNT}
-$ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/bump_version.sh ${NEW_VERSION}-SNAPSHOT
+$ dev/release/bump_version.sh ${NEW_VERSION}-SNAPSHOT
```
Here is an example to bump version to 19.0.1-SNAPSHOT:
```
-$ GH_TOKEN=${YOUR_GITHUB_TOKEN} dev/release/bump_version.sh 19.0.0-SNAPSHOT
+$ dev/release/bump_version.sh 19.0.0-SNAPSHOT
```
It creates a feature branch and adds a commit that bumps version. This
diff --git a/dev/release/bump_version.sh b/dev/release/bump_version.sh
index d1c127de35..458e930f98 100755
--- a/dev/release/bump_version.sh
+++ b/dev/release/bump_version.sh
@@ -31,6 +31,13 @@ fi
version=$1
+if [ ! -f "${SOURCE_DIR}/.env" ]; then
+ echo "You must create ${SOURCE_DIR}/.env"
+ echo "You can use ${SOURCE_DIR}/.env.example as template"
+ exit 1
+fi
+. "${SOURCE_DIR}/.env"
+
cd "${SOURCE_TOP_DIR}"
git_origin_url="$(git remote get-url origin)"
diff --git a/dev/release/release.sh b/dev/release/release.sh
index c5e67907e2..70e1f96454 100755
--- a/dev/release/release.sh
+++ b/dev/release/release.sh
@@ -28,6 +28,13 @@ fi
version=$1
rc=$2
+if [ ! -f "${SOURCE_DIR}/.env" ]; then
+ echo "You must create ${SOURCE_DIR}/.env"
+ echo "You can use ${SOURCE_DIR}/.env.example as template"
+ exit 1
+fi
+. "${SOURCE_DIR}/.env"
+
git_origin_url="$(git remote get-url origin)"
repository="${git_origin_url#*github.com?}"
repository="${repository%.git}"
diff --git a/dev/release/release_rc.sh b/dev/release/release_rc.sh
index b0107dce85..ff77718b8d 100755
--- a/dev/release/release_rc.sh
+++ b/dev/release/release_rc.sh
@@ -36,6 +36,13 @@ rc=$1
: "${RELEASE_SIGN:=${RELEASE_DEFAULT}}"
: "${RELEASE_UPLOAD:=${RELEASE_DEFAULT}}"
+if [ ! -f "${SOURCE_DIR}/.env" ]; then
+ echo "You must create ${SOURCE_DIR}/.env"
+ echo "You can use ${SOURCE_DIR}/.env.example as template"
+ exit 1
+fi
+. "${SOURCE_DIR}/.env"
+
cd "${SOURCE_TOP_DIR}"
if [ "${RELEASE_PULL}" -gt 0 ] || [ "${RELEASE_PUSH_TAG}" -gt 0 ]; then
@@ -84,13 +91,6 @@ artifacts_dir="apache-arrow-java-${version}-rc${rc}"
signed_artifacts_dir="${artifacts_dir}-signed"
if [ "${RELEASE_SIGN}" -gt 0 ]; then
- if [ ! -f "${SOURCE_DIR}/.env" ]; then
- echo "You must create ${SOURCE_DIR}/.env"
- echo "You can use ${SOURCE_DIR}/.env.example as template"
- exit 1
- fi
- . "${SOURCE_DIR}/.env"
-
git_origin_url="$(git remote get-url origin)"
repository="${git_origin_url#*github.com?}"
repository="${repository%.git}"
From 26630c38f563821aa40d1c5a6df30101dde8c171 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Diego=20Fern=C3=A1ndez=20Giraldo?=
Date: Sun, 27 Apr 2025 23:28:37 -0600
Subject: [PATCH 015/271] GH-463: Improve TZ support for JDBC driver (#464)
This PR adds support for natively fetching `java.time.*` objects through
the JDBC driver.
DateVector
- getObject(LocalDate.class)
DateTimeVector
- getObject(OffsetDateTime.class)
- getObject(LocalDateTime.class)
- getObject(ZonedDateTime.class)
- getObject(Instant.class)
TimeVector
- getObject(LocalTime.class)
This PR also changes the behavior for vectors that include TZ info.
These will now return as `TIMESTAMP_WITH_TIMEZONE`.
The behavior for different ways to access a TimeStampVector are as
follows:
| | Vector with TZ | Vector W/O TZ |
|---------------------------------|------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| getTimestamp() | Get timestamp in the vector TZ | Get timestamp in UTC
|
| getTimestamp(calendar) | Get timestamp by adjusting from the vector TZ
to the desired calendar TZ | Get timestamp by adjusting from UTC to the
desired calendar TZ (a bug, IMO) |
| getObject(LocalDateTime.class) | Get LocalDateTime by taking the
timestamp at the vector TZ and taking the "wall-clock" time at that
moment | Treat the epoch value in the vector as the "wall-clock" time |
| getObject(Instant.class) | Get Instant represented by the value in the
vector TZ | Get Instant represented by the value in UTC |
| getObject(OffsetDateTime.class) | Get OffsetDateTime represented by
the value in the vector TZ (will account for daylight adjustment) | Get
OffsetDateTime represented by the value in UTC (will account for
daylight adjustment) |
| getObject(ZonedDateTime.class) | Get ZonedDateTime represented by the
value in the vector at in its TZ | Get ZonedDateTime represented by the
value in the vector at in UTC |
Closes #463
---
.../ArrowFlightJdbcDateVectorAccessor.java | 19 +++
...rrowFlightJdbcTimeStampVectorAccessor.java | 101 +++++++++++++--
.../ArrowFlightJdbcTimeVectorAccessor.java | 19 +++
.../arrow/driver/jdbc/utils/SqlTypes.java | 8 +-
.../jdbc/ArrowDatabaseMetadataTest.java | 5 +-
...FlightJdbcTimeStampVectorAccessorTest.java | 118 +++++++++++++++++-
.../arrow/driver/jdbc/utils/SqlTypesTest.java | 12 ++
7 files changed, 269 insertions(+), 13 deletions(-)
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcDateVectorAccessor.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcDateVectorAccessor.java
index ebe4016209..cdafeffc32 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcDateVectorAccessor.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcDateVectorAccessor.java
@@ -24,7 +24,9 @@
import static org.apache.calcite.avatica.util.DateTimeUtils.unixDateToString;
import java.sql.Date;
+import java.sql.SQLException;
import java.sql.Timestamp;
+import java.time.LocalDate;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import java.util.function.IntSupplier;
@@ -85,6 +87,19 @@ public Object getObject() {
return this.getDate(null);
}
+ @Override
+ public T getObject(final Class type) throws SQLException {
+ final Object value;
+ if (type == LocalDate.class) {
+ value = getLocalDate();
+ } else if (type == Date.class) {
+ value = getObject();
+ } else {
+ throw new SQLException("Object type not supported for Date Vector");
+ }
+ return !type.isPrimitive() && wasNull ? null : type.cast(value);
+ }
+
@Override
public Date getDate(Calendar calendar) {
fillHolder();
@@ -134,4 +149,8 @@ protected static TimeUnit getTimeUnitForVector(ValueVector vector) {
throw new IllegalArgumentException("Invalid Arrow vector");
}
+
+ private LocalDate getLocalDate() {
+ return getDate(null).toLocalDate();
+ }
}
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeStampVectorAccessor.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeStampVectorAccessor.java
index debdd0fcb4..813fbc7cfd 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeStampVectorAccessor.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeStampVectorAccessor.java
@@ -21,11 +21,18 @@
import static org.apache.arrow.driver.jdbc.accessor.impl.calendar.ArrowFlightJdbcTimeStampVectorGetter.createGetter;
import java.sql.Date;
+import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
+import java.time.Instant;
import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
+import java.util.Objects;
+import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.function.IntSupplier;
@@ -43,6 +50,7 @@ public class ArrowFlightJdbcTimeStampVectorAccessor extends ArrowFlightJdbcAcces
private final TimeUnit timeUnit;
private final LongToLocalDateTime longToLocalDateTime;
private final Holder holder;
+ private final boolean isZoned;
/** Functional interface used to convert a number (in any time resolution) to LocalDateTime. */
interface LongToLocalDateTime {
@@ -58,6 +66,9 @@ public ArrowFlightJdbcTimeStampVectorAccessor(
this.holder = new Holder();
this.getter = createGetter(vector);
+ // whether the vector included TZ info
+ this.isZoned = getVectorIsZoned(vector);
+ // non-null, either the vector TZ or default to UTC
this.timeZone = getTimeZoneForVector(vector);
this.timeUnit = getTimeUnitForVector(vector);
this.longToLocalDateTime = getLongToLocalDateTimeForVector(vector, this.timeZone);
@@ -68,11 +79,62 @@ public Class> getObjectClass() {
return Timestamp.class;
}
+ @Override
+ public T getObject(final Class type) throws SQLException {
+ final Object value;
+ if (!this.isZoned
+ & Set.of(OffsetDateTime.class, ZonedDateTime.class, Instant.class).contains(type)) {
+ throw new SQLException(
+ "Vectors without timezones can't be converted to objects with offset/tz info.");
+ } else if (type == OffsetDateTime.class) {
+ value = getOffsetDateTime();
+ } else if (type == LocalDateTime.class) {
+ value = getLocalDateTime(null);
+ } else if (type == ZonedDateTime.class) {
+ value = getZonedDateTime();
+ } else if (type == Instant.class) {
+ value = getInstant();
+ } else if (type == Timestamp.class) {
+ value = getObject();
+ } else {
+ throw new SQLException("Object type not supported for TimeStamp Vector");
+ }
+
+ return !type.isPrimitive() && wasNull ? null : type.cast(value);
+ }
+
@Override
public Object getObject() {
return this.getTimestamp(null);
}
+ private ZonedDateTime getZonedDateTime() {
+ LocalDateTime localDateTime = getLocalDateTime(null);
+ if (localDateTime == null) {
+ return null;
+ }
+
+ return localDateTime.atZone(this.timeZone.toZoneId());
+ }
+
+ private OffsetDateTime getOffsetDateTime() {
+ LocalDateTime localDateTime = getLocalDateTime(null);
+ if (localDateTime == null) {
+ return null;
+ }
+ ZoneOffset offset = this.timeZone.toZoneId().getRules().getOffset(localDateTime);
+ return localDateTime.atOffset(offset);
+ }
+
+ private Instant getInstant() {
+ LocalDateTime localDateTime = getLocalDateTime(null);
+ if (localDateTime == null) {
+ return null;
+ }
+ ZoneOffset offset = this.timeZone.toZoneId().getRules().getOffset(localDateTime);
+ return localDateTime.toInstant(offset);
+ }
+
private LocalDateTime getLocalDateTime(Calendar calendar) {
getter.get(getCurrentRow(), holder);
this.wasNull = holder.isSet == 0;
@@ -85,7 +147,9 @@ private LocalDateTime getLocalDateTime(Calendar calendar) {
LocalDateTime localDateTime = this.longToLocalDateTime.fromLong(value);
- if (calendar != null) {
+ // Adjust timestamp to desired calendar (if provided) only if the column includes TZ info,
+ // otherwise treat as wall-clock time
+ if (calendar != null && this.isZoned) {
TimeZone timeZone = calendar.getTimeZone();
long millis = this.timeUnit.toMillis(value);
localDateTime =
@@ -102,7 +166,7 @@ public Date getDate(Calendar calendar) {
return null;
}
- return new Date(Timestamp.valueOf(localDateTime).getTime());
+ return new Date(getTimestampWithOffset(calendar, localDateTime).getTime());
}
@Override
@@ -112,7 +176,7 @@ public Time getTime(Calendar calendar) {
return null;
}
- return new Time(Timestamp.valueOf(localDateTime).getTime());
+ return new Time(getTimestampWithOffset(calendar, localDateTime).getTime());
}
@Override
@@ -122,6 +186,24 @@ public Timestamp getTimestamp(Calendar calendar) {
return null;
}
+ return getTimestampWithOffset(calendar, localDateTime);
+ }
+
+ /**
+ * Apply offset to LocalDateTime to get a Timestamp with legacy behavior. Previously we applied
+ * the offset to the LocalDateTime even if the underlying Vector did not have a TZ. In order to
+ * support java.time.* accessors, we fixed this so we only apply the offset if the underlying
+ * vector includes TZ info. In order to maintain backward compatibility, we apply the offset if
+ * needed for getDate, getTime, and getTimestamp.
+ */
+ private Timestamp getTimestampWithOffset(Calendar calendar, LocalDateTime localDateTime) {
+ if (calendar != null && !isZoned) {
+ TimeZone timeZone = calendar.getTimeZone();
+ long millis = Timestamp.valueOf(localDateTime).getTime();
+ localDateTime =
+ localDateTime.minus(
+ timeZone.getOffset(millis) - this.timeZone.getOffset(millis), ChronoUnit.MILLIS);
+ }
return Timestamp.valueOf(localDateTime);
}
@@ -170,11 +252,14 @@ protected static TimeZone getTimeZoneForVector(TimeStampVector vector) {
ArrowType.Timestamp arrowType =
(ArrowType.Timestamp) vector.getField().getFieldType().getType();
- String timezoneName = arrowType.getTimezone();
- if (timezoneName == null) {
- return TimeZone.getTimeZone("UTC");
- }
-
+ String timezoneName = Objects.requireNonNullElse(arrowType.getTimezone(), "UTC");
return TimeZone.getTimeZone(timezoneName);
}
+
+ protected static boolean getVectorIsZoned(TimeStampVector vector) {
+ ArrowType.Timestamp arrowType =
+ (ArrowType.Timestamp) vector.getField().getFieldType().getType();
+
+ return arrowType.getTimezone() != null;
+ }
}
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeVectorAccessor.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeVectorAccessor.java
index 2c03ee631e..d525c2fdd2 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeVectorAccessor.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeVectorAccessor.java
@@ -20,8 +20,10 @@
import static org.apache.arrow.driver.jdbc.accessor.impl.calendar.ArrowFlightJdbcTimeVectorGetter.Holder;
import static org.apache.arrow.driver.jdbc.accessor.impl.calendar.ArrowFlightJdbcTimeVectorGetter.createGetter;
+import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
+import java.time.LocalTime;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import java.util.function.IntSupplier;
@@ -121,6 +123,19 @@ public Object getObject() {
return this.getTime(null);
}
+ @Override
+ public T getObject(final Class type) throws SQLException {
+ final Object value;
+ if (type == LocalTime.class) {
+ value = getLocalTime();
+ } else if (type == Time.class) {
+ value = getObject();
+ } else {
+ throw new SQLException("Object type not supported for Time Vector");
+ }
+ return !type.isPrimitive() && wasNull ? null : type.cast(value);
+ }
+
@Override
public Time getTime(Calendar calendar) {
fillHolder();
@@ -134,6 +149,10 @@ public Time getTime(Calendar calendar) {
return new ArrowFlightJdbcTime(DateTimeUtils.applyCalendarOffset(milliseconds, calendar));
}
+ private LocalTime getLocalTime() {
+ return getTime(null).toLocalTime();
+ }
+
private void fillHolder() {
getter.get(getCurrentRow(), holder);
this.wasNull = holder.isSet == 0;
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/SqlTypes.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/SqlTypes.java
index 96cb056db2..1b76ca0c95 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/SqlTypes.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/SqlTypes.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.driver.jdbc.utils;
+import com.google.common.base.Strings;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
@@ -120,7 +121,12 @@ public static int getSqlTypeIdFromArrowType(ArrowType arrowType) {
case Time:
return Types.TIME;
case Timestamp:
- return Types.TIMESTAMP;
+ String tz = ((ArrowType.Timestamp) arrowType).getTimezone();
+ if (Strings.isNullOrEmpty(tz)) {
+ return Types.TIMESTAMP;
+ } else {
+ return Types.TIMESTAMP_WITH_TIMEZONE;
+ }
case Bool:
return Types.BOOLEAN;
case Decimal:
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
index 88a172e4f2..70d3bcbd33 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
@@ -299,8 +299,9 @@ public class ArrowDatabaseMetadataTest {
private static Connection connection;
static {
- List expectedGetColumnsDataTypes = Arrays.asList(3, 93, 4);
- List expectedGetColumnsTypeName = Arrays.asList("DECIMAL", "TIMESTAMP", "INTEGER");
+ List expectedGetColumnsDataTypes = Arrays.asList(3, 2014, 4);
+ List expectedGetColumnsTypeName =
+ Arrays.asList("DECIMAL", "TIMESTAMP_WITH_TIMEZONE", "INTEGER");
List expectedGetColumnsRadix = Arrays.asList(10, null, 10);
List expectedGetColumnsColumnSize = Arrays.asList(5, 29, 10);
List expectedGetColumnsDecimalDigits = Arrays.asList(2, 9, 0);
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeStampVectorAccessorTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeStampVectorAccessorTest.java
index 2e329f148e..e4863bd80e 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeStampVectorAccessorTest.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/accessor/impl/calendar/ArrowFlightJdbcTimeStampVectorAccessorTest.java
@@ -16,17 +16,23 @@
*/
package org.apache.arrow.driver.jdbc.accessor.impl.calendar;
-import static org.apache.arrow.driver.jdbc.accessor.impl.calendar.ArrowFlightJdbcTimeStampVectorAccessor.getTimeUnitForVector;
-import static org.apache.arrow.driver.jdbc.accessor.impl.calendar.ArrowFlightJdbcTimeStampVectorAccessor.getTimeZoneForVector;
+import static org.apache.arrow.driver.jdbc.accessor.impl.calendar.ArrowFlightJdbcTimeStampVectorAccessor.*;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import java.sql.Date;
+import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
+import java.time.Instant;
import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.util.Calendar;
+import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@@ -199,6 +205,99 @@ public void testShouldGetTimestampReturnValidTimestampWithCalendar(
});
}
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testShouldGetObjectReturnValidLocalDateTime(
+ Supplier vectorSupplier, String vectorType, String timeZone)
+ throws Exception {
+ setup(vectorSupplier);
+ final String expectedTimeZone = Objects.requireNonNullElse(timeZone, "UTC");
+
+ accessorIterator.iterate(
+ vector,
+ (accessor, currentRow) -> {
+ final LocalDateTime value = accessor.getObject(LocalDateTime.class);
+ final LocalDateTime expectedValue =
+ getZonedDateTime(currentRow, expectedTimeZone).toLocalDateTime();
+
+ assertThat(value, equalTo(expectedValue));
+ assertThat(accessor.wasNull(), is(false));
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testShouldGetObjectReturnValidInstant(
+ Supplier vectorSupplier, String vectorType, String timeZone)
+ throws Exception {
+ setup(vectorSupplier);
+ final String expectedTimeZone = Objects.requireNonNullElse(timeZone, "UTC");
+ final boolean vectorHasTz = timeZone != null;
+ accessorIterator.iterate(
+ vector,
+ (accessor, currentRow) -> {
+ if (vectorHasTz) {
+ final Instant value = accessor.getObject(Instant.class);
+ final Instant expectedValue =
+ getZonedDateTime(currentRow, expectedTimeZone).toInstant();
+
+ assertThat(value, equalTo(expectedValue));
+ assertThat(accessor.wasNull(), is(false));
+ } else {
+ assertThrows(SQLException.class, () -> accessor.getObject(Instant.class));
+ }
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testShouldGetObjectReturnValidOffsetDateTime(
+ Supplier vectorSupplier, String vectorType, String timeZone)
+ throws Exception {
+ setup(vectorSupplier);
+ final String expectedTimeZone = Objects.requireNonNullElse(timeZone, "UTC");
+ final boolean vectorHasTz = timeZone != null;
+ accessorIterator.iterate(
+ vector,
+ (accessor, currentRow) -> {
+ if (vectorHasTz) {
+ final OffsetDateTime value = accessor.getObject(OffsetDateTime.class);
+ final OffsetDateTime expectedValue =
+ getZonedDateTime(currentRow, expectedTimeZone).toOffsetDateTime();
+
+ assertThat(value, equalTo(expectedValue));
+ assertThat(value.getOffset(), equalTo(expectedValue.getOffset()));
+ assertThat(accessor.wasNull(), is(false));
+ } else {
+ assertThrows(SQLException.class, () -> accessor.getObject(OffsetDateTime.class));
+ }
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("data")
+ public void testShouldGetObjectReturnValidZonedDateTime(
+ Supplier vectorSupplier, String vectorType, String timeZone)
+ throws Exception {
+ setup(vectorSupplier);
+ final String expectedTimeZone = Objects.requireNonNullElse(timeZone, "UTC");
+ final boolean vectorHasTz = timeZone != null;
+ accessorIterator.iterate(
+ vector,
+ (accessor, currentRow) -> {
+ if (vectorHasTz) {
+ final ZonedDateTime value = accessor.getObject(ZonedDateTime.class);
+ final ZonedDateTime expectedValue = getZonedDateTime(currentRow, expectedTimeZone);
+
+ assertThat(value, equalTo(expectedValue));
+ assertThat(value.getZone(), equalTo(ZoneId.of(expectedTimeZone)));
+ assertThat(accessor.wasNull(), is(false));
+ } else {
+ assertThrows(SQLException.class, () -> accessor.getObject(ZonedDateTime.class));
+ }
+ });
+ }
+
@ParameterizedTest
@MethodSource("data")
public void testShouldGetTimestampReturnNull(Supplier vectorSupplier) {
@@ -320,6 +419,21 @@ private Timestamp getTimestampForVector(int currentRow, String timeZone) {
return expectedTimestamp;
}
+ /** ZonedDateTime contains all necessary information to generate any java.time object. */
+ private ZonedDateTime getZonedDateTime(int currentRow, String timeZone) {
+ Object object = vector.getObject(currentRow);
+ TimeZone tz = TimeZone.getTimeZone(timeZone);
+ ZonedDateTime expectedTimestamp = null;
+ if (object instanceof LocalDateTime) {
+ expectedTimestamp = ((LocalDateTime) object).atZone(tz.toZoneId());
+ } else if (object instanceof Long) {
+ TimeUnit timeUnit = getTimeUnitForVector(vector);
+ Instant instant = Instant.ofEpochMilli(timeUnit.toMillis((Long) object));
+ expectedTimestamp = ZonedDateTime.ofInstant(instant, tz.toZoneId());
+ }
+ return expectedTimestamp;
+ }
+
@ParameterizedTest
@MethodSource("data")
public void testShouldGetObjectClass(Supplier vectorSupplier) throws Exception {
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/SqlTypesTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/SqlTypesTest.java
index 00af3c96ba..a6dd6b3275 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/SqlTypesTest.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/SqlTypesTest.java
@@ -48,9 +48,15 @@ public void testGetSqlTypeIdFromArrowType() {
assertEquals(Types.DATE, getSqlTypeIdFromArrowType(new ArrowType.Date(DateUnit.MILLISECOND)));
assertEquals(
Types.TIME, getSqlTypeIdFromArrowType(new ArrowType.Time(TimeUnit.MILLISECOND, 32)));
+ assertEquals(
+ Types.TIMESTAMP,
+ getSqlTypeIdFromArrowType(new ArrowType.Timestamp(TimeUnit.MILLISECOND, null)));
assertEquals(
Types.TIMESTAMP,
getSqlTypeIdFromArrowType(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "")));
+ assertEquals(
+ Types.TIMESTAMP_WITH_TIMEZONE,
+ getSqlTypeIdFromArrowType(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC")));
assertEquals(Types.BOOLEAN, getSqlTypeIdFromArrowType(new ArrowType.Bool()));
@@ -95,9 +101,15 @@ public void testGetSqlTypeNameFromArrowType() {
assertEquals("DATE", getSqlTypeNameFromArrowType(new ArrowType.Date(DateUnit.MILLISECOND)));
assertEquals("TIME", getSqlTypeNameFromArrowType(new ArrowType.Time(TimeUnit.MILLISECOND, 32)));
+ assertEquals(
+ "TIMESTAMP",
+ getSqlTypeNameFromArrowType(new ArrowType.Timestamp(TimeUnit.MILLISECOND, null)));
assertEquals(
"TIMESTAMP",
getSqlTypeNameFromArrowType(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "")));
+ assertEquals(
+ "TIMESTAMP_WITH_TIMEZONE",
+ getSqlTypeNameFromArrowType(new ArrowType.Timestamp(TimeUnit.MILLISECOND, "UTC")));
assertEquals("BOOLEAN", getSqlTypeNameFromArrowType(new ArrowType.Bool()));
From c2234549827488d32b6698f91e26053a293d808e Mon Sep 17 00:00:00 2001
From: wangyunlai
Date: Mon, 28 Apr 2025 14:23:46 +0800
Subject: [PATCH 016/271] GH-729: [JDBC] Fix BinaryConsumer consuming null
value (#730)
## What's Changed
Set `startOffset` of the next item when `BinaryConsumer` consuming
`null` value.
Closes #729 .
---
.../adapter/jdbc/consumer/BinaryConsumer.java | 25 ++++++++++---------
.../jdbc/consumer/BinaryConsumerTest.java | 11 +++++++-
2 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/consumer/BinaryConsumer.java b/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/consumer/BinaryConsumer.java
index edbc6360df..73ec04b8a0 100644
--- a/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/consumer/BinaryConsumer.java
+++ b/adapter/jdbc/src/main/java/org/apache/arrow/adapter/jdbc/consumer/BinaryConsumer.java
@@ -51,13 +51,15 @@ public BinaryConsumer(VarBinaryVector vector, int index) {
/** consume a InputStream. */
public void consume(InputStream is) throws IOException {
+ while (currentIndex >= vector.getValueCapacity()) {
+ vector.reallocValidityAndOffsetBuffers();
+ }
+
+ final int startOffset = vector.getStartOffset(currentIndex);
+ final ArrowBuf offsetBuffer = vector.getOffsetBuffer();
+ int dataLength = 0;
+
if (is != null) {
- while (currentIndex >= vector.getValueCapacity()) {
- vector.reallocValidityAndOffsetBuffers();
- }
- final int startOffset = vector.getStartOffset(currentIndex);
- final ArrowBuf offsetBuffer = vector.getOffsetBuffer();
- int dataLength = 0;
int read;
while ((read = is.read(reuseBytes)) != -1) {
while (vector.getDataBuffer().capacity() < (startOffset + dataLength + read)) {
@@ -66,11 +68,12 @@ public void consume(InputStream is) throws IOException {
vector.getDataBuffer().setBytes(startOffset + dataLength, reuseBytes, 0, read);
dataLength += read;
}
- offsetBuffer.setInt(
- (currentIndex + 1) * ((long) VarBinaryVector.OFFSET_WIDTH), startOffset + dataLength);
+
BitVectorHelper.setBit(vector.getValidityBuffer(), currentIndex);
- vector.setLastSet(currentIndex);
}
+ offsetBuffer.setInt(
+ (currentIndex + 1) * ((long) VarBinaryVector.OFFSET_WIDTH), startOffset + dataLength);
+ vector.setLastSet(currentIndex);
}
public void moveWriterPosition() {
@@ -95,9 +98,7 @@ public NullableBinaryConsumer(VarBinaryVector vector, int index) {
@Override
public void consume(ResultSet resultSet) throws SQLException, IOException {
InputStream is = resultSet.getBinaryStream(columnIndexInResultSet);
- if (!resultSet.wasNull()) {
- consume(is);
- }
+ consume(is);
moveWriterPosition();
}
}
diff --git a/adapter/jdbc/src/test/java/org/apache/arrow/adapter/jdbc/consumer/BinaryConsumerTest.java b/adapter/jdbc/src/test/java/org/apache/arrow/adapter/jdbc/consumer/BinaryConsumerTest.java
index b1e253794d..bb836578e2 100644
--- a/adapter/jdbc/src/test/java/org/apache/arrow/adapter/jdbc/consumer/BinaryConsumerTest.java
+++ b/adapter/jdbc/src/test/java/org/apache/arrow/adapter/jdbc/consumer/BinaryConsumerTest.java
@@ -22,6 +22,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
import org.apache.arrow.vector.BaseValueVector;
import org.apache.arrow.vector.VarBinaryVector;
import org.junit.jupiter.api.Test;
@@ -65,7 +66,11 @@ public void testConsumeInputStream(byte[][] values, boolean nullable) throws IOE
nullable,
binaryConsumer -> {
for (byte[] value : values) {
- binaryConsumer.consume(new ByteArrayInputStream(value));
+ if (value != null) {
+ binaryConsumer.consume(new ByteArrayInputStream(value));
+ } else {
+ binaryConsumer.consume((InputStream) null);
+ }
binaryConsumer.moveWriterPosition();
}
},
@@ -119,5 +124,9 @@ public void testConsumeInputStream() throws IOException {
testRecords[i] = createBytes(DEFAULT_RECORD_BYTE_COUNT);
}
testConsumeInputStream(testRecords, false);
+
+ byte[] bytes1 = new byte[] {1, 2, 3};
+ byte[] bytes2 = new byte[] {4, 5, 6};
+ testConsumeInputStream(new byte[][] {bytes1, null, bytes2}, true);
}
}
From b9e37f0ccecc2651fec3487472c203bd223290e8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Apr 2025 17:32:40 +0900
Subject: [PATCH 017/271] MINOR: [CI] Bump actions/download-artifact from 4.2.1
to 4.3.0 (#733)
Bumps
[actions/download-artifact](https://github.com/actions/download-artifact)
from 4.2.1 to 4.3.0.
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/rc.yml | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/rc.yml b/.github/workflows/rc.yml
index 5b78cc9395..7e3cf5f6f2 100644
--- a/.github/workflows/rc.yml
+++ b/.github/workflows/rc.yml
@@ -101,7 +101,7 @@ jobs:
packages: write
steps:
- name: Download source archive
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-source
- name: Extract source archive
@@ -174,7 +174,7 @@ jobs:
MACOSX_DEPLOYMENT_TARGET: "14.0"
steps:
- name: Download source archive
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-source
- name: Extract source archive
@@ -298,7 +298,7 @@ jobs:
arch: "x86_64"
steps:
- name: Download source archive
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-source
- name: Extract source archive
@@ -371,7 +371,7 @@ jobs:
- jni-windows
steps:
- name: Download artifacts
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: artifacts
- name: Decompress artifacts
@@ -452,11 +452,11 @@ jobs:
with:
cache: 'pip'
- name: Download source archive
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-source
- name: Download Javadocs
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: reference
- name: Extract source archive
@@ -521,7 +521,7 @@ jobs:
cp ../.asf.yaml ./
git add .nojekyll .asf.yaml
- name: Download
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release-html
- name: Extract
@@ -557,7 +557,7 @@ jobs:
- ubuntu-latest
steps:
- name: Download release artifacts
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: release-*
- name: Verify
@@ -591,7 +591,7 @@ jobs:
contents: write
steps:
- name: Download release artifacts
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: release-*
path: artifacts
From 8314c1912ed501171a2085feb1909b07892a6a25 Mon Sep 17 00:00:00 2001
From: Mateusz Rzeszutek
Date: Sat, 3 May 2025 11:01:23 +0200
Subject: [PATCH 018/271] GH-737: [FlightSQL] Allow returning column remarks in
FlightSQL's CommandGetTables (#727)
Resolves #737
## What's Changed
This is an implementation of
https://github.com/apache/arrow/pull/46110 for Java
---
arrow-format/FlightSql.proto | 4 ++
.../tests/FlightSqlScenarioProducer.java | 2 +
.../driver/jdbc/ArrowDatabaseMetadata.java | 6 +++
.../arrow/driver/jdbc/utils/ConvertUtils.java | 4 ++
.../jdbc/ArrowDatabaseMetadataTest.java | 53 ++++++++++++++-----
.../driver/jdbc/utils/ConvertUtilsTest.java | 2 +
.../flight/sql/FlightSqlColumnMetadata.java | 21 ++++++++
7 files changed, 80 insertions(+), 12 deletions(-)
diff --git a/arrow-format/FlightSql.proto b/arrow-format/FlightSql.proto
index 3568d851cb..566230c2a6 100644
--- a/arrow-format/FlightSql.proto
+++ b/arrow-format/FlightSql.proto
@@ -1212,6 +1212,7 @@ message CommandGetDbSchemas {
* - ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE - "1" indicates if the column is case-sensitive, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_READ_ONLY - "1" indicates if the column is read only, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_SEARCHABLE - "1" indicates if the column is searchable via WHERE clause, "0" otherwise.
+ * - ARROW:FLIGHT:SQL:REMARKS - A comment describing the column.
* The returned data should be ordered by catalog_name, db_schema_name, table_name, then table_type, followed by table_schema if requested.
*/
message CommandGetTables {
@@ -1678,6 +1679,7 @@ message ActionEndSavepointRequest {
* - ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE - "1" indicates if the column is case-sensitive, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_READ_ONLY - "1" indicates if the column is read only, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_SEARCHABLE - "1" indicates if the column is searchable via WHERE clause, "0" otherwise.
+ * - ARROW:FLIGHT:SQL:REMARKS - A comment describing the column.
* - GetFlightInfo: execute the query.
*/
message CommandStatementQuery {
@@ -1703,6 +1705,7 @@ message CommandStatementQuery {
* - ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE - "1" indicates if the column is case-sensitive, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_READ_ONLY - "1" indicates if the column is read only, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_SEARCHABLE - "1" indicates if the column is searchable via WHERE clause, "0" otherwise.
+ * - ARROW:FLIGHT:SQL:REMARKS - A comment describing the column.
* - GetFlightInfo: execute the query.
* - DoPut: execute the query.
*/
@@ -1739,6 +1742,7 @@ message TicketStatementQuery {
* - ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE - "1" indicates if the column is case-sensitive, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_READ_ONLY - "1" indicates if the column is read only, "0" otherwise.
* - ARROW:FLIGHT:SQL:IS_SEARCHABLE - "1" indicates if the column is searchable via WHERE clause, "0" otherwise.
+ * - ARROW:FLIGHT:SQL:REMARKS - A comment describing the column.
*
* If the schema is retrieved after parameter values have been bound with DoPut, then the server should account
* for the parameters when determining the schema.
diff --git a/flight/flight-integration-tests/src/main/java/org/apache/arrow/flight/integration/tests/FlightSqlScenarioProducer.java b/flight/flight-integration-tests/src/main/java/org/apache/arrow/flight/integration/tests/FlightSqlScenarioProducer.java
index be746b5757..e400c031c2 100644
--- a/flight/flight-integration-tests/src/main/java/org/apache/arrow/flight/integration/tests/FlightSqlScenarioProducer.java
+++ b/flight/flight-integration-tests/src/main/java/org/apache/arrow/flight/integration/tests/FlightSqlScenarioProducer.java
@@ -98,6 +98,7 @@ static Schema getQuerySchema() {
.isSearchable(true)
.catalogName("catalog_test")
.precision(100)
+ .remarks("test column")
.build()
.getMetadataMap()),
null)));
@@ -126,6 +127,7 @@ static Schema getQueryWithTransactionSchema() {
.isSearchable(true)
.catalogName("catalog_test")
.precision(100)
+ .remarks("test column")
.build()
.getMetadataMap()),
null)));
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
index 3f072d071b..7185ddfe01 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadata.java
@@ -1066,6 +1066,7 @@ private int setGetColumnsVectorSchemaRootFromFields(
(VarCharVector) currentRoot.getVector("IS_AUTOINCREMENT");
final VarCharVector isGeneratedColumnVector =
(VarCharVector) currentRoot.getVector("IS_GENERATEDCOLUMN");
+ final VarCharVector remarksVector = (VarCharVector) currentRoot.getVector("REMARKS");
for (int i = 0; i < tableColumnsSize; i++, ordinalIndex++) {
final Field field = tableColumns.get(i);
@@ -1139,6 +1140,11 @@ private int setGetColumnsVectorSchemaRootFromFields(
isAutoincrementVector.setSafe(insertIndex, EMPTY_BYTE_ARRAY);
}
+ String remarks = columnMetadata.getRemarks();
+ if (remarks != null) {
+ remarksVector.setSafe(insertIndex, remarks.getBytes(CHARSET));
+ }
+
// Fields also don't hold information about IS_AUTOINCREMENT and IS_GENERATEDCOLUMN,
// so we're setting an empty string (as bytes), which means it couldn't be determined.
isGeneratedColumnVector.setSafe(insertIndex, EMPTY_BYTE_ARRAY);
diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java
index 17b0f42dc7..5dd4c69c73 100644
--- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java
+++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/utils/ConvertUtils.java
@@ -136,6 +136,10 @@ public static void setOnColumnMetaDataBuilder(
if (searchable != null) {
builder.setSearchable(searchable);
}
+ final String remarks = columnMetadata.getRemarks();
+ if (remarks != null) {
+ builder.setLabel(remarks);
+ }
}
/**
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
index 70d3bcbd33..81579cc387 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/ArrowDatabaseMetadataTest.java
@@ -26,7 +26,6 @@
import static java.util.stream.Collectors.toList;
import static java.util.stream.IntStream.range;
import static org.apache.arrow.driver.jdbc.utils.MockFlightSqlProducer.serializeSchema;
-import static org.apache.arrow.flight.sql.impl.FlightSql.CommandGetCrossReference;
import static org.apache.arrow.flight.sql.impl.FlightSql.SqlSupportsConvert.SQL_CONVERT_BIGINT_VALUE;
import static org.apache.arrow.flight.sql.impl.FlightSql.SqlSupportsConvert.SQL_CONVERT_BIT_VALUE;
import static org.apache.arrow.flight.sql.impl.FlightSql.SqlSupportsConvert.SQL_CONVERT_INTEGER_VALUE;
@@ -55,9 +54,11 @@
import org.apache.arrow.driver.jdbc.utils.ResultSetTestUtils;
import org.apache.arrow.driver.jdbc.utils.ThrowableAssertionUtils;
import org.apache.arrow.flight.FlightProducer.ServerStreamListener;
+import org.apache.arrow.flight.sql.FlightSqlColumnMetadata;
import org.apache.arrow.flight.sql.FlightSqlProducer.Schemas;
import org.apache.arrow.flight.sql.impl.FlightSql;
import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetCatalogs;
+import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetCrossReference;
import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetDbSchemas;
import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetExportedKeys;
import org.apache.arrow.flight.sql.impl.FlightSql.CommandGetImportedKeys;
@@ -79,6 +80,7 @@
import org.apache.arrow.vector.types.Types;
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.types.pojo.Schema;
import org.apache.arrow.vector.util.Text;
import org.junit.jupiter.api.AfterAll;
@@ -322,7 +324,7 @@ public class ArrowDatabaseMetadataTest {
expectedGetColumnsDecimalDigits.get(i % 3),
expectedGetColumnsRadix.get(i % 3),
!Objects.equals(expectedGetColumnsIsNullable.get(i % 3), "NO") ? 1 : 0,
- null,
+ format("column description #%d", (i % 3) + 1),
null,
null,
null,
@@ -419,17 +421,44 @@ public static void setUpBeforeClass() throws SQLException {
try (final BufferAllocator allocator = new RootAllocator();
final VectorSchemaRoot root =
VectorSchemaRoot.create(Schemas.GET_TABLES_SCHEMA, allocator)) {
+ final Field field1 =
+ new Field(
+ "column_1",
+ new FieldType(
+ true,
+ ArrowType.Decimal.createDecimal(5, 2, 128),
+ null,
+ new FlightSqlColumnMetadata.Builder()
+ .remarks("column description #1")
+ .build()
+ .getMetadataMap()),
+ null);
+ final Field field2 =
+ new Field(
+ "column_2",
+ new FieldType(
+ true,
+ new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC"),
+ null,
+ new FlightSqlColumnMetadata.Builder()
+ .remarks("column description #2")
+ .build()
+ .getMetadataMap()),
+ null);
+ final Field field3 =
+ new Field(
+ "column_3",
+ new FieldType(
+ false,
+ Types.MinorType.INT.getType(),
+ null,
+ new FlightSqlColumnMetadata.Builder()
+ .remarks("column description #3")
+ .build()
+ .getMetadataMap()),
+ null);
final byte[] filledTableSchemaBytes =
- copyFrom(
- serializeSchema(
- new Schema(
- Arrays.asList(
- Field.nullable(
- "column_1", ArrowType.Decimal.createDecimal(5, 2, 128)),
- Field.nullable(
- "column_2",
- new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC")),
- Field.notNullable("column_3", Types.MinorType.INT.getType())))))
+ copyFrom(serializeSchema(new Schema(Arrays.asList(field1, field2, field3))))
.toByteArray();
final VarCharVector catalogName = (VarCharVector) root.getVector("catalog_name");
final VarCharVector schemaName = (VarCharVector) root.getVector("db_schema_name");
diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java
index f6f549b5ed..b6fdc99694 100644
--- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java
+++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/utils/ConvertUtilsTest.java
@@ -46,6 +46,7 @@ public void testShouldSetOnColumnMetaDataBuilder() {
.isSearchable(true)
.precision(20)
.scale(10)
+ .remarks("test column")
.build();
ConvertUtils.setOnColumnMetaDataBuilder(builder, expectedColumnMetaData.getMetadataMap());
assertBuilder(builder, expectedColumnMetaData);
@@ -119,5 +120,6 @@ private void assertBuilder(
assertThat(flightSqlColumnMetaData.isReadOnly(), equalTo(builder.getReadOnly()));
assertThat(precision == null ? 0 : precision, equalTo(builder.getPrecision()));
assertThat(scale == null ? 0 : scale, equalTo(builder.getScale()));
+ assertThat(flightSqlColumnMetaData.getRemarks(), equalTo(builder.getLabel()));
}
}
diff --git a/flight/flight-sql/src/main/java/org/apache/arrow/flight/sql/FlightSqlColumnMetadata.java b/flight/flight-sql/src/main/java/org/apache/arrow/flight/sql/FlightSqlColumnMetadata.java
index 1bcc55a660..3a969e10cf 100644
--- a/flight/flight-sql/src/main/java/org/apache/arrow/flight/sql/FlightSqlColumnMetadata.java
+++ b/flight/flight-sql/src/main/java/org/apache/arrow/flight/sql/FlightSqlColumnMetadata.java
@@ -53,6 +53,7 @@ public class FlightSqlColumnMetadata {
private static final String IS_CASE_SENSITIVE = "ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE";
private static final String IS_READ_ONLY = "ARROW:FLIGHT:SQL:IS_READ_ONLY";
private static final String IS_SEARCHABLE = "ARROW:FLIGHT:SQL:IS_SEARCHABLE";
+ private static final String REMARKS = "ARROW:FLIGHT:SQL:REMARKS";
private static final String BOOLEAN_TRUE_STR = "1";
private static final String BOOLEAN_FALSE_STR = "0";
@@ -193,6 +194,15 @@ public Boolean isSearchable() {
return stringToBoolean(value);
}
+ /**
+ * Returns the comment describing the column.
+ *
+ * @return The comment describing the column.
+ */
+ public String getRemarks() {
+ return metadataMap.get(REMARKS);
+ }
+
/** Builder of FlightSqlColumnMetadata, used on FlightSqlProducer implementations. */
public static class Builder {
private final Map metadataMap;
@@ -312,6 +322,17 @@ public Builder isSearchable(boolean isSearchable) {
return this;
}
+ /**
+ * Sets the comment describing the column.
+ *
+ * @param remarks The comment describing the column.
+ * @return This builder.
+ */
+ public Builder remarks(String remarks) {
+ metadataMap.put(REMARKS, remarks);
+ return this;
+ }
+
/**
* Builds a new instance of FlightSqlColumnMetadata.
*
From f3b50019bdbef5ecc1bfcb293129bbcf7204babd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 6 May 2025 09:23:07 +0200
Subject: [PATCH 019/271] MINOR: Bump com.google.guava:guava-bom from
33.4.5-jre to 33.4.8-jre (#720)
Bumps [com.google.guava:guava-bom](https://github.com/google/guava) from
33.4.5-jre to 33.4.8-jre.
Release notes
Guava 33.4.8 fixes a problem that we introduced while starting to
migrate guava-android off Unsafe in 33.4.7.
Even if you're not upgrading from Guava 33.4.0 or earlier, still read
the
release notes for Guava 33.4.1. Those release notes contain
information about the effects of Guava 33.4.5 and higher on the module
system.
util.concurrent: Removed our VarHandle
code from guava-android. While the code was never used at
runtime under Android, it was causing problems
under the Android Gradle Plugin with a minSdkVersion
below 26. To continue to avoid sun.misc.Unsafe under the
JVM, guava-android will now always use
AtomicReferenceFieldUpdater when run there.
(75da92419a)
Guava 33.4.7, like 33.4.6,
fixes two problems that we introduced while modularizing Guava and
migrating off Unsafe in 33.4.5.
Even if you're not upgrading from Guava 33.4.0 or earlier, still read
the
release notes for Guava 33.4.1. Those release notes contain
information about the effects of Guava 33.4.5 and higher on the module
system.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index ab988c7d05..fc274abcff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -95,7 +95,7 @@ under the License.
1.9.05.12.12.0.17
- 33.4.5-jre
+ 33.4.8-jre4.1.119.Final1.71.04.30.1
From 75741c8ae41b74944ad84de0e2007b8a1dc3c3dd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 6 May 2025 09:23:50 +0200
Subject: [PATCH 020/271] MINOR: Bump com.google.protobuf:protobuf-bom from
4.30.1 to 4.30.2 (#696)
Bumps
[com.google.protobuf:protobuf-bom](https://github.com/protocolbuffers/protobuf)
from 4.30.1 to 4.30.2.
Commits
43e1626
Updating version.json and repo version numbers to: 30.2
7a4c63b
Fix lite classes in the protobuf-java Maven release to be JDK8
compatible. (#...
7831669
Remove dllexport attribute on variable definition. (#20833)
da9cadc
Restore JDK8 compatibility in Bazel for libraries with dependencies from
Mave...
09b5078
Add protobuf_maven artifacts to protobuf_maven_dev as well so they can
still ...
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
You can trigger a rebase of this PR by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
> **Note**
> Automatic rebases have been disabled on this pull request as it has
been open for over 30 days.
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index fc274abcff..2e2f1659dd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -98,7 +98,7 @@ under the License.
33.4.8-jre4.1.119.Final1.71.0
- 4.30.1
+ 4.30.22.18.33.4.125.2.10
From fc75ff7e2bd7abe187320371a7470c9a7547fdae Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 6 May 2025 18:14:21 +0200
Subject: [PATCH 021/271] MINOR: Bump dep.junit.jupiter.version from 5.12.1 to
5.12.2 (#713)
Bumps `dep.junit.jupiter.version` from 5.12.1 to 5.12.2.
Updates `org.junit.jupiter:junit-jupiter-engine` from 5.12.1 to 5.12.2
Release notes
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 2e2f1659dd..b8b24ca110 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,7 +93,7 @@ under the License.
${project.build.directory}/generated-sources1.9.0
- 5.12.1
+ 5.12.22.0.1733.4.8-jre4.1.119.Final
From feb8e95fe2b283c23ec0b380d3522a9e3991bd0c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 7 May 2025 09:51:19 +0200
Subject: [PATCH 022/271] MINOR: Bump
com.diffplug.spotless:spotless-maven-plugin from 2.44.3 to 2.44.4 (#717)
Bumps
[com.diffplug.spotless:spotless-maven-plugin](https://github.com/diffplug/spotless)
from 2.44.3 to 2.44.4.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
bom/pom.xml | 2 +-
pom.xml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/bom/pom.xml b/bom/pom.xml
index 171d0bc5e9..61b452b9c1 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -203,7 +203,7 @@ under the License.
com.diffplug.spotlessspotless-maven-plugin
- 2.44.3
+ 2.44.4org.codehaus.mojo
diff --git a/pom.xml b/pom.xml
index b8b24ca110..fe716bad6e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -487,7 +487,7 @@ under the License.
com.diffplug.spotlessspotless-maven-plugin
- 2.44.3
+ 2.44.4org.codehaus.mojo
From f32198fe28687c6e43df21149272764f2710947a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 7 May 2025 11:06:36 +0200
Subject: [PATCH 023/271] MINOR: Bump org.apache.commons:commons-text from
1.13.0 to 1.13.1 (#714)
Bumps org.apache.commons:commons-text from 1.13.0 to 1.13.1.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
flight/flight-sql/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flight/flight-sql/pom.xml b/flight/flight-sql/pom.xml
index 5d8e720fd6..5f06a5e9eb 100644
--- a/flight/flight-sql/pom.xml
+++ b/flight/flight-sql/pom.xml
@@ -113,7 +113,7 @@ under the License.
org.apache.commonscommons-text
- 1.13.0
+ 1.13.1test
From b0cc03d4c62dabc44746f462f274b20003890e95 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 7 May 2025 11:07:00 +0200
Subject: [PATCH 024/271] MINOR: Bump org.jacoco:jacoco-maven-plugin from
0.8.12 to 0.8.13 (#699)
Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco)
from 0.8.12 to 0.8.13.
Release notes
JaCoCo now officially supports Java 23 and Java 24 (GitHub #1757,
#1631,
#1867).
Experimental support for Java 25 class files (GitHub #1807).
Calculation of line coverage for Kotlin inline
functions (GitHub #1670).
Calculation of line coverage for Kotlin inline
functions with reified type parameter (GitHub #1670,
#1700).
Calculation of coverage for Kotlin JvmSynthetic
functions (GitHub #1700).
Part of bytecode generated by the Kotlin Compose compiler plugin is
filtered out during generation of report (GitHub #1616).
Part of bytecode generated by the Kotlin compiler for inline value
classes is filtered out during generation of report (GitHub #1475).
Part of bytecode generated by the Kotlin compiler for suspending
lambdas without suspension points is filtered out during generation of
report (GitHub #1283).
Part of bytecode generated by the Kotlin compiler for when
expressions and statements with nullable enum subject is filtered out
during generation of report (GitHub #1774).
Part of bytecode generated by the Kotlin compiler for when
expressions and statements with nullable String subject is filtered out
during generation of report (GitHub #1769).
Part of bytecode generated by the Kotlin compiler for chains of safe
call operators is filtered out during generation of report (GitHub #1810,
#1818).
Method getEntries generated by the Kotlin compiler for
enum classes is filtered out during generation of report (GitHub #1625).
Methods generated by the Kotlin compiler for constructors and
functions with JvmOverloads annotation are filtered out
(GitHub #1768).
Fixed bugs
Fixed interpretation of Kotlin SMAP (GitHub #1525).
File extensions are preserved in HTML report in case of clashes of
normalized file names (GitHub #1660).
Non-functional Changes
JaCoCo build now uses Maven Wrapper and requires at least Maven
3.9.9 (GitHub #1708,
#1707,
#1681).
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
You can trigger a rebase of this PR by commenting `@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
> **Note**
> Automatic rebases have been disabled on this pull request as it has
been open for over 30 days.
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index fe716bad6e..8360e1be2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -347,7 +347,7 @@ under the License.
org.jacocojacoco-maven-plugin
- 0.8.12
+ 0.8.13
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
flight/flight-core/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flight/flight-core/pom.xml b/flight/flight-core/pom.xml
index 757de85769..92f58b84de 100644
--- a/flight/flight-core/pom.xml
+++ b/flight/flight-core/pom.xml
@@ -134,7 +134,7 @@ under the License.
com.google.api.grpcproto-google-common-protos
- 2.54.1
+ 2.56.0test
From 93b7fdc2113c9c21e359edc910a6b44061b0ee50 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 12 May 2025 10:47:23 +0200
Subject: [PATCH 035/271] MINOR: Bump com.github.ben-manes.caffeine:caffeine
from 3.1.8 to 3.2.0 (#747)
Bumps
[com.github.ben-manes.caffeine:caffeine](https://github.com/ben-manes/caffeine)
from 3.1.8 to 3.2.0.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
flight/flight-sql-jdbc-core/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flight/flight-sql-jdbc-core/pom.xml b/flight/flight-sql-jdbc-core/pom.xml
index a95fbaca30..04c4691faf 100644
--- a/flight/flight-sql-jdbc-core/pom.xml
+++ b/flight/flight-sql-jdbc-core/pom.xml
@@ -151,7 +151,7 @@ under the License.
com.github.ben-manes.caffeinecaffeine
- 3.1.8
+ 3.2.0
From 8c74ced9c03e4aba1d1c5fea5f223a42be234dcd Mon Sep 17 00:00:00 2001
From: Gang Wu
Date: Tue, 13 May 2025 23:43:14 +0800
Subject: [PATCH 036/271] MINOR: Bump version to 19.0.0-SNAPSHOT (#754)
---
adapter/avro/pom.xml | 2 +-
adapter/jdbc/pom.xml | 2 +-
adapter/orc/pom.xml | 2 +-
algorithm/pom.xml | 2 +-
bom/pom.xml | 4 ++--
c/pom.xml | 2 +-
compression/pom.xml | 2 +-
dataset/pom.xml | 2 +-
flight/flight-core/pom.xml | 2 +-
flight/flight-integration-tests/pom.xml | 2 +-
flight/flight-sql-jdbc-core/pom.xml | 2 +-
flight/flight-sql-jdbc-driver/pom.xml | 2 +-
flight/flight-sql/pom.xml | 2 +-
flight/pom.xml | 2 +-
format/pom.xml | 2 +-
gandiva/pom.xml | 2 +-
memory/memory-core/pom.xml | 2 +-
memory/memory-netty-buffer-patch/pom.xml | 2 +-
memory/memory-netty/pom.xml | 2 +-
memory/memory-unsafe/pom.xml | 2 +-
memory/pom.xml | 2 +-
performance/pom.xml | 2 +-
pom.xml | 4 ++--
tools/pom.xml | 2 +-
vector/pom.xml | 2 +-
25 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/adapter/avro/pom.xml b/adapter/avro/pom.xml
index cf9d353330..827d19f2a2 100644
--- a/adapter/avro/pom.xml
+++ b/adapter/avro/pom.xml
@@ -23,7 +23,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOT../../pom.xml
diff --git a/adapter/jdbc/pom.xml b/adapter/jdbc/pom.xml
index f92863fbd6..2f621d7a05 100644
--- a/adapter/jdbc/pom.xml
+++ b/adapter/jdbc/pom.xml
@@ -23,7 +23,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOT../../pom.xml
diff --git a/adapter/orc/pom.xml b/adapter/orc/pom.xml
index e60a7ceb3c..e3ae7d5163 100644
--- a/adapter/orc/pom.xml
+++ b/adapter/orc/pom.xml
@@ -23,7 +23,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOT../../pom.xml
diff --git a/algorithm/pom.xml b/algorithm/pom.xml
index e934eb7b22..898c2605b6 100644
--- a/algorithm/pom.xml
+++ b/algorithm/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-algorithmArrow Algorithms
diff --git a/bom/pom.xml b/bom/pom.xml
index 80f03d1205..61b452b9c1 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -29,7 +29,7 @@ under the License.
org.apache.arrowarrow-bom
- 18.3.0
+ 19.0.0-SNAPSHOTpomArrow Bill of Materials
@@ -68,7 +68,7 @@ under the License.
scm:git:https://github.com/apache/arrow-java.gitscm:git:https://github.com/apache/arrow-java.git
- v18.3.0
+ mainhttps://github.com/apache/arrow-java/tree/${project.scm.tag}
diff --git a/c/pom.xml b/c/pom.xml
index 290cb561c1..c90b6dc0ef 100644
--- a/c/pom.xml
+++ b/c/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-c-data
diff --git a/compression/pom.xml b/compression/pom.xml
index 3443f11478..6f60eb7d0a 100644
--- a/compression/pom.xml
+++ b/compression/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-compressionArrow Compression
diff --git a/dataset/pom.xml b/dataset/pom.xml
index efbe310ea2..6e56d555b7 100644
--- a/dataset/pom.xml
+++ b/dataset/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-dataset
diff --git a/flight/flight-core/pom.xml b/flight/flight-core/pom.xml
index 92f58b84de..24beac391e 100644
--- a/flight/flight-core/pom.xml
+++ b/flight/flight-core/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-flight
- 18.3.0
+ 19.0.0-SNAPSHOTflight-core
diff --git a/flight/flight-integration-tests/pom.xml b/flight/flight-integration-tests/pom.xml
index e7fb999149..78a2d08ee1 100644
--- a/flight/flight-integration-tests/pom.xml
+++ b/flight/flight-integration-tests/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-flight
- 18.3.0
+ 19.0.0-SNAPSHOTflight-integration-tests
diff --git a/flight/flight-sql-jdbc-core/pom.xml b/flight/flight-sql-jdbc-core/pom.xml
index 04c4691faf..d8e012101c 100644
--- a/flight/flight-sql-jdbc-core/pom.xml
+++ b/flight/flight-sql-jdbc-core/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-flight
- 18.3.0
+ 19.0.0-SNAPSHOTflight-sql-jdbc-core
diff --git a/flight/flight-sql-jdbc-driver/pom.xml b/flight/flight-sql-jdbc-driver/pom.xml
index 3776e97f3f..559c42597d 100644
--- a/flight/flight-sql-jdbc-driver/pom.xml
+++ b/flight/flight-sql-jdbc-driver/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-flight
- 18.3.0
+ 19.0.0-SNAPSHOTflight-sql-jdbc-driver
diff --git a/flight/flight-sql/pom.xml b/flight/flight-sql/pom.xml
index 66ade30306..5f06a5e9eb 100644
--- a/flight/flight-sql/pom.xml
+++ b/flight/flight-sql/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-flight
- 18.3.0
+ 19.0.0-SNAPSHOTflight-sql
diff --git a/flight/pom.xml b/flight/pom.xml
index 7b31e8ce91..2fc3e89ef8 100644
--- a/flight/pom.xml
+++ b/flight/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-flight
diff --git a/format/pom.xml b/format/pom.xml
index 9b4eebfe3c..d3578b63d2 100644
--- a/format/pom.xml
+++ b/format/pom.xml
@@ -23,7 +23,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-format
diff --git a/gandiva/pom.xml b/gandiva/pom.xml
index 167bf39cb9..5367bfdedf 100644
--- a/gandiva/pom.xml
+++ b/gandiva/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTorg.apache.arrow.gandiva
diff --git a/memory/memory-core/pom.xml b/memory/memory-core/pom.xml
index 840d3464ba..72ee69d60a 100644
--- a/memory/memory-core/pom.xml
+++ b/memory/memory-core/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-memory
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-memory-core
diff --git a/memory/memory-netty-buffer-patch/pom.xml b/memory/memory-netty-buffer-patch/pom.xml
index e9a63b2122..07dc7d2403 100644
--- a/memory/memory-netty-buffer-patch/pom.xml
+++ b/memory/memory-netty-buffer-patch/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-memory
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-memory-netty-buffer-patch
diff --git a/memory/memory-netty/pom.xml b/memory/memory-netty/pom.xml
index 42f35efb33..6d660da117 100644
--- a/memory/memory-netty/pom.xml
+++ b/memory/memory-netty/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-memory
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-memory-netty
diff --git a/memory/memory-unsafe/pom.xml b/memory/memory-unsafe/pom.xml
index 0af306cbfc..92dc0c9fe5 100644
--- a/memory/memory-unsafe/pom.xml
+++ b/memory/memory-unsafe/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-memory
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-memory-unsafe
diff --git a/memory/pom.xml b/memory/pom.xml
index 09a5bc2924..bc34c26050 100644
--- a/memory/pom.xml
+++ b/memory/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-memorypom
diff --git a/performance/pom.xml b/performance/pom.xml
index 02bdf46a11..3f18188e3a 100644
--- a/performance/pom.xml
+++ b/performance/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-performancejar
diff --git a/pom.xml b/pom.xml
index 9d89616356..d40f428b22 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTpomApache Arrow Java Root POM
@@ -81,7 +81,7 @@ under the License.
scm:git:https://github.com/apache/arrow-java.gitscm:git:https://github.com/apache/arrow-java.git
- v18.3.0
+ mainhttps://github.com/apache/arrow-java/tree/${project.scm.tag}
diff --git a/tools/pom.xml b/tools/pom.xml
index d60281d80c..cb9a161308 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-toolsArrow Tools
diff --git a/vector/pom.xml b/vector/pom.xml
index 450e4ff71d..52ad5105ea 100644
--- a/vector/pom.xml
+++ b/vector/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.arrowarrow-java-root
- 18.3.0
+ 19.0.0-SNAPSHOTarrow-vectorArrow Vectors
From b459647910a6a373fb9f3d9cd0eb8cc717932bed Mon Sep 17 00:00:00 2001
From: Gang Wu
Date: Wed, 14 May 2025 10:42:38 +0800
Subject: [PATCH 037/271] MINOR: add missing SOURCE_DIR in
dev/release/release.sh (#755)
## What's Changed
`dev/release/release.sh` requires `SOURCE_DIR` to locate `.env` but it
is missing.
---
dev/release/release.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dev/release/release.sh b/dev/release/release.sh
index 70e1f96454..f08a618c4f 100755
--- a/dev/release/release.sh
+++ b/dev/release/release.sh
@@ -19,6 +19,8 @@
set -eu
+SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
if [ "$#" -ne 2 ]; then
echo "Usage: $0 "
echo " e.g.: $0 19.0.1 1"
From 6be33bba2366f2335b962f25fe6ea0789665ae89 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adam=20Po=C5=82omski?=
Date: Thu, 15 May 2025 04:57:23 +0200
Subject: [PATCH 038/271] MINOR: Empty stream double check (#742)
## What's Changed
In some cases InflaterInputStream
can return a non zero on `available()` method call, while it is actually
at EOS. A subsequent `read()` call would respond with -1 and eventually
cause:
Caused by: org.apache.arrow.flight.FlightRuntimeException: Failed to
read message.
at
org.apache.arrow.flight.CallStatus.toRuntimeException(CallStatus.java:121)
at
org.apache.arrow.flight.grpc.StatusUtils.fromGrpcRuntimeException(StatusUtils.java:161)
at
org.apache.arrow.flight.grpc.StatusUtils.fromThrowable(StatusUtils.java:182)
at
org.apache.arrow.flight.FlightStream$Observer.onError(FlightStream.java:489)
at org.apache.arrow.flight.FlightClient$1.onError(FlightClient.java:371)
at
io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onClose(ClientCalls.java:564)
at
io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
at
io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
at
io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
at
org.apache.arrow.flight.grpc.ClientInterceptorAdapter$FlightClientCallListener.onClose(ClientInterceptorAdapter.java:118)
at
io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:564)
at io.grpc.internal.ClientCallImpl.access$100(ClientCallImpl.java:72)
at
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:729)
at
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:710)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at
io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
... 3 common frames omitted
Caused by: java.lang.RuntimeException:
com.google.protobuf.InvalidProtocolBufferException: While parsing a
protocol message, the input ended unexpectedly in the middle of a field.
This could mean either that the input has been truncated or that an
embedded message misreported its own length.
at org.apache.arrow.flight.ArrowMessage.frame(ArrowMessage.java:363)
at
org.apache.arrow.flight.ArrowMessage$ArrowMessageHolderMarshaller.parse(ArrowMessage.java:575)
at
org.apache.arrow.flight.ArrowMessage$ArrowMessageHolderMarshaller.parse(ArrowMessage.java:560)
at io.grpc.MethodDescriptor.parseResponse(MethodDescriptor.java:284)
at
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInternal(ClientCallImpl.java:657)
at
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInContext(ClientCallImpl.java:644)
... 5 common frames omitted
Caused by: com.google.protobuf.InvalidProtocolBufferException: While
parsing a protocol message, the input ended unexpectedly in the middle
of a field. This could mean either that the input has been truncated or
that an embedded message misreported its own length.
at
com.google.protobuf.InvalidProtocolBufferException.truncatedMessage(InvalidProtocolBufferException.java:92)
at
com.google.protobuf.CodedInputStream.readRawVarint32(CodedInputStream.java:568)
at
org.apache.arrow.flight.ArrowMessage.readRawVarint32(ArrowMessage.java:369)
at org.apache.arrow.flight.ArrowMessage.frame(ArrowMessage.java:290)
... 10 common frames omitted
---
.../java/org/apache/arrow/flight/ArrowMessage.java | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/flight/flight-core/src/main/java/org/apache/arrow/flight/ArrowMessage.java b/flight/flight-core/src/main/java/org/apache/arrow/flight/ArrowMessage.java
index 9cefccb3fe..ab4eab3048 100644
--- a/flight/flight-core/src/main/java/org/apache/arrow/flight/ArrowMessage.java
+++ b/flight/flight-core/src/main/java/org/apache/arrow/flight/ArrowMessage.java
@@ -287,7 +287,11 @@ private static ArrowMessage frame(BufferAllocator allocator, final InputStream s
ArrowBuf body = null;
ArrowBuf appMetadata = null;
while (stream.available() > 0) {
- int tag = readRawVarint32(stream);
+ final int tagFirstByte = stream.read();
+ if (tagFirstByte == -1) {
+ break;
+ }
+ int tag = readRawVarint32(tagFirstByte, stream);
switch (tag) {
case DESCRIPTOR_TAG:
{
@@ -366,6 +370,10 @@ private static ArrowMessage frame(BufferAllocator allocator, final InputStream s
private static int readRawVarint32(InputStream is) throws IOException {
int firstByte = is.read();
+ return readRawVarint32(firstByte, is);
+ }
+
+ private static int readRawVarint32(int firstByte, InputStream is) throws IOException {
return CodedInputStream.readRawVarint32(firstByte, is);
}
From 7c25ce5d86490822600b49928d34a08b4dddad46 Mon Sep 17 00:00:00 2001
From: ViggoC
Date: Thu, 22 May 2025 21:40:44 +0800
Subject: [PATCH 039/271] GH-52: Make RangeEqualsVisitor of RunEndEncodedVector
more efficient (#761)
## What's Changed
Avoid doing a binary search on every step to make the RangeEqualsVisitor
of RunEndEncodedVector more efficient.
Closes #52 .
---
.../vector/compare/RangeEqualsVisitor.java | 44 ++++-----
.../vector/complex/RunEndEncodedVector.java | 98 +++++++++++++++++++
.../arrow/vector/TestRunEndEncodedVector.java | 18 ++--
.../compare/TestRangeEqualsVisitor.java | 52 ++++++++++
4 files changed, 181 insertions(+), 31 deletions(-)
diff --git a/vector/src/main/java/org/apache/arrow/vector/compare/RangeEqualsVisitor.java b/vector/src/main/java/org/apache/arrow/vector/compare/RangeEqualsVisitor.java
index abcf312c5e..bc2e3a6aab 100644
--- a/vector/src/main/java/org/apache/arrow/vector/compare/RangeEqualsVisitor.java
+++ b/vector/src/main/java/org/apache/arrow/vector/compare/RangeEqualsVisitor.java
@@ -43,6 +43,7 @@
import org.apache.arrow.vector.complex.ListViewVector;
import org.apache.arrow.vector.complex.NonNullableStructVector;
import org.apache.arrow.vector.complex.RunEndEncodedVector;
+import org.apache.arrow.vector.complex.RunEndEncodedVector.RangeIterator;
import org.apache.arrow.vector.complex.StructVector;
import org.apache.arrow.vector.complex.UnionVector;
@@ -270,42 +271,35 @@ protected boolean compareRunEndEncodedVectors(Range range) {
RunEndEncodedVector leftVector = (RunEndEncodedVector) left;
RunEndEncodedVector rightVector = (RunEndEncodedVector) right;
- final int leftRangeEnd = range.getLeftStart() + range.getLength();
- final int rightRangeEnd = range.getRightStart() + range.getLength();
+ final RunEndEncodedVector.RangeIterator leftIterator =
+ new RunEndEncodedVector.RangeIterator(leftVector, range.getLeftStart(), range.getLength());
+ final RunEndEncodedVector.RangeIterator rightIterator =
+ new RunEndEncodedVector.RangeIterator(
+ rightVector, range.getRightStart(), range.getLength());
FieldVector leftValuesVector = leftVector.getValuesVector();
FieldVector rightValuesVector = rightVector.getValuesVector();
RangeEqualsVisitor innerVisitor = createInnerVisitor(leftValuesVector, rightValuesVector, null);
- int leftLogicalIndex = range.getLeftStart();
- int rightLogicalIndex = range.getRightStart();
+ while (nextRun(leftIterator, rightIterator)) {
+ int leftPhysicalIndex = leftIterator.getRunIndex();
+ int rightPhysicalIndex = rightIterator.getRunIndex();
- while (leftLogicalIndex < leftRangeEnd) {
- // TODO: implement it more efficient
- // https://github.com/apache/arrow/issues/44157
- int leftPhysicalIndex = leftVector.getPhysicalIndex(leftLogicalIndex);
- int rightPhysicalIndex = rightVector.getPhysicalIndex(rightLogicalIndex);
- if (leftValuesVector.accept(
- innerVisitor, new Range(leftPhysicalIndex, rightPhysicalIndex, 1))) {
- int leftRunEnd = leftVector.getRunEnd(leftLogicalIndex);
- int rightRunEnd = rightVector.getRunEnd(rightLogicalIndex);
-
- int leftRunLength = Math.min(leftRunEnd, leftRangeEnd) - leftLogicalIndex;
- int rightRunLength = Math.min(rightRunEnd, rightRangeEnd) - rightLogicalIndex;
-
- if (leftRunLength != rightRunLength) {
- return false;
- } else {
- leftLogicalIndex = leftRunEnd;
- rightLogicalIndex = rightRunEnd;
- }
- } else {
+ if (leftIterator.getRunLength() != rightIterator.getRunLength()
+ || !leftValuesVector.accept(
+ innerVisitor, new Range(leftPhysicalIndex, rightPhysicalIndex, 1))) {
return false;
}
}
- return true;
+ return leftIterator.isEnd() && rightIterator.isEnd();
+ }
+
+ private static boolean nextRun(RangeIterator leftIterator, RangeIterator rightIterator) {
+ boolean left = leftIterator.nextRun();
+ boolean right = rightIterator.nextRun();
+ return left && right;
}
protected RangeEqualsVisitor createInnerVisitor(
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/RunEndEncodedVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/RunEndEncodedVector.java
index 1bb9a3d6c0..b83e13449a 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/RunEndEncodedVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/RunEndEncodedVector.java
@@ -28,6 +28,7 @@
import org.apache.arrow.memory.OutOfMemoryException;
import org.apache.arrow.memory.util.ByteFunctionHelpers;
import org.apache.arrow.memory.util.hash.ArrowBufHasher;
+import org.apache.arrow.util.Preconditions;
import org.apache.arrow.vector.BaseIntVector;
import org.apache.arrow.vector.BaseValueVector;
import org.apache.arrow.vector.BigIntVector;
@@ -820,4 +821,101 @@ static int getPhysicalIndex(FieldVector runEndVector, int logicalIndex) {
return result;
}
+
+ public static class RangeIterator {
+
+ private final RunEndEncodedVector runEndEncodedVector;
+ private final int rangeEnd;
+ private int runIndex;
+ private int runEnd;
+ private int logicalPos;
+
+ /**
+ * Constructs a new RangeIterator for iterating over a range of values in a RunEndEncodedVector.
+ *
+ * @param runEndEncodedVector The vector to iterate over
+ * @param startIndex The logical start index of the range (inclusive)
+ * @param length The number of values to include in the range
+ * @throws IllegalArgumentException if startIndex is negative or (startIndex + length) exceeds
+ * vector bounds
+ */
+ public RangeIterator(RunEndEncodedVector runEndEncodedVector, int startIndex, int length) {
+ int rangeEnd = startIndex + length;
+ Preconditions.checkArgument(
+ startIndex >= 0, "startIndex %s must be non negative.", startIndex);
+ Preconditions.checkArgument(
+ rangeEnd <= runEndEncodedVector.getValueCount(),
+ "(startIndex + length) %s out of range[0, %s].",
+ rangeEnd,
+ runEndEncodedVector.getValueCount());
+
+ this.rangeEnd = rangeEnd;
+ this.runEndEncodedVector = runEndEncodedVector;
+ this.runIndex = runEndEncodedVector.getPhysicalIndex(startIndex) - 1;
+ this.runEnd = startIndex;
+ this.logicalPos = -1;
+ }
+
+ /**
+ * Advances to the next run in the range.
+ *
+ * @return true if there is another run available, false if iteration has completed
+ */
+ public boolean nextRun() {
+ logicalPos = runEnd;
+ if (logicalPos >= rangeEnd) {
+ return false;
+ }
+ updateRun();
+ return true;
+ }
+
+ private void updateRun() {
+ runIndex++;
+ runEnd = (int) ((BaseIntVector) runEndEncodedVector.runEndsVector).getValueAsLong(runIndex);
+ }
+
+ /**
+ * Advances to the next value in the range.
+ *
+ * @return true if there is another value available, false if iteration has completed
+ */
+ public boolean nextValue() {
+ logicalPos++;
+ if (logicalPos >= rangeEnd) {
+ return false;
+ }
+ if (logicalPos == runEnd) {
+ updateRun();
+ }
+ return true;
+ }
+
+ /**
+ * Gets the current run index (physical position in the run-ends vector).
+ *
+ * @return the current run index
+ */
+ public int getRunIndex() {
+ return runIndex;
+ }
+
+ /**
+ * Gets the length of the current run within the iterator's range.
+ *
+ * @return the number of remaining values in current run within the iterator's range
+ */
+ public int getRunLength() {
+ return Math.min(runEnd, rangeEnd) - logicalPos;
+ }
+
+ /**
+ * Checks if iteration has completed.
+ *
+ * @return true if all values in the range have been processed, false otherwise
+ */
+ public boolean isEnd() {
+ return logicalPos >= rangeEnd;
+ }
+ }
}
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestRunEndEncodedVector.java b/vector/src/test/java/org/apache/arrow/vector/TestRunEndEncodedVector.java
index adf51c0730..9fa153e928 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestRunEndEncodedVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestRunEndEncodedVector.java
@@ -148,12 +148,18 @@ public void testRangeCompare() {
assertTrue(
constantVector.accept(
new RangeEqualsVisitor(constantVector, constantVector), new Range(1, 2, 13)));
- assertFalse(
- constantVector.accept(
- new RangeEqualsVisitor(constantVector, constantVector), new Range(1, 10, 10)));
- assertFalse(
- constantVector.accept(
- new RangeEqualsVisitor(constantVector, constantVector), new Range(10, 1, 10)));
+
+ // throws exception if the range end is out the bound of the vector
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ constantVector.accept(
+ new RangeEqualsVisitor(constantVector, constantVector), new Range(1, 10, 10)));
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ constantVector.accept(
+ new RangeEqualsVisitor(constantVector, constantVector), new Range(10, 1, 10)));
// Create REE vector representing: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5].
RunEndEncodedVector reeVector =
diff --git a/vector/src/test/java/org/apache/arrow/vector/compare/TestRangeEqualsVisitor.java b/vector/src/test/java/org/apache/arrow/vector/compare/TestRangeEqualsVisitor.java
index 08da786eb2..9624734356 100644
--- a/vector/src/test/java/org/apache/arrow/vector/compare/TestRangeEqualsVisitor.java
+++ b/vector/src/test/java/org/apache/arrow/vector/compare/TestRangeEqualsVisitor.java
@@ -22,6 +22,7 @@
import java.nio.charset.Charset;
import java.util.Arrays;
+import java.util.List;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.BigIntVector;
@@ -39,6 +40,7 @@
import org.apache.arrow.vector.complex.LargeListViewVector;
import org.apache.arrow.vector.complex.ListVector;
import org.apache.arrow.vector.complex.ListViewVector;
+import org.apache.arrow.vector.complex.RunEndEncodedVector;
import org.apache.arrow.vector.complex.StructVector;
import org.apache.arrow.vector.complex.UnionVector;
import org.apache.arrow.vector.complex.impl.NullableStructWriter;
@@ -53,7 +55,9 @@
import org.apache.arrow.vector.holders.NullableUInt4Holder;
import org.apache.arrow.vector.types.FloatingPointPrecision;
import org.apache.arrow.vector.types.Types;
+import org.apache.arrow.vector.types.Types.MinorType;
import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.ArrowType.RunEndEncoded;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
import org.junit.jupiter.api.AfterEach;
@@ -1003,6 +1007,54 @@ public void testLargeListViewVectorApproxEquals() {
}
}
+ @Test
+ public void testRunEndEncodedFloat8ApproxEquals() {
+ try (final Float8Vector vector1 = new Float8Vector("float", allocator);
+ final Float8Vector vector2 = new Float8Vector("float", allocator);
+ final Float8Vector vector3 = new Float8Vector("float", allocator);
+ final IntVector reeVector = new IntVector("ree", allocator)) {
+
+ final float epsilon = 1.0E-6f;
+ setVector(vector1, 1.1, 2.2);
+ setVector(vector2, 1.1 + epsilon / 2, 2.2 + epsilon / 2);
+ setVector(vector3, 1.1 + epsilon * 2, 2.2 + epsilon * 2);
+ setVector(reeVector, 1, 3);
+
+ ArrowType type = MinorType.FLOAT8.getType();
+ final FieldType valueType = FieldType.notNullable(type);
+ final FieldType runEndType = FieldType.notNullable(MinorType.INT.getType());
+
+ final Field valueField = new Field("value", valueType, null);
+ final Field runEndField = new Field("ree", runEndType, null);
+
+ Field field =
+ new Field(
+ "ree_float",
+ FieldType.notNullable(RunEndEncoded.INSTANCE),
+ List.of(runEndField, valueField));
+
+ try (final RunEndEncodedVector encodedVector1 =
+ new RunEndEncodedVector(field, allocator, reeVector, vector1, null);
+ final RunEndEncodedVector encodedVector2 =
+ new RunEndEncodedVector(field, allocator, reeVector, vector2, null);
+ final RunEndEncodedVector encodedVector3 =
+ new RunEndEncodedVector(field, allocator, reeVector, vector3, null)) {
+
+ encodedVector1.setValueCount(3);
+ encodedVector2.setValueCount(3);
+ encodedVector3.setValueCount(3);
+
+ Range range = new Range(0, 0, encodedVector1.getValueCount());
+ assertTrue(
+ new ApproxEqualsVisitor(encodedVector1, encodedVector2, epsilon, epsilon)
+ .rangeEquals(range));
+ assertFalse(
+ new ApproxEqualsVisitor(encodedVector1, encodedVector3, epsilon, epsilon)
+ .rangeEquals(range));
+ }
+ }
+ }
+
private void writeStructVector(NullableStructWriter writer, int value1, long value2) {
writer.start();
writer.integer("f0").writeInt(value1);
From 2aef66df8ce771b3e634e28c98e4442b8cb39995 Mon Sep 17 00:00:00 2001
From: Sutou Kouhei
Date: Sat, 24 May 2025 14:22:40 +0900
Subject: [PATCH 040/271] GH-768: Use apache/arrow-js for JS in integration
test (#769)
## What's Changed
`js/` in apache/arrow moved to apache/arrow-js. So let's use
apache/arrow-js for JS.
Closes #768.
---
.github/workflows/test.yml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 5db5c988eb..f1028ce029 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -176,6 +176,11 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: java
+ - name: Checkout Arrow JavaScript
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ repository: apache/arrow-js
+ path: js
- name: Free up disk space
run: |
ci/scripts/util_free_space.sh
@@ -199,6 +204,7 @@ jobs:
-e ARCHERY_INTEGRATION_TARGET_IMPLEMENTATIONS=java \
-e ARCHERY_INTEGRATION_WITH_GO=1 \
-e ARCHERY_INTEGRATION_WITH_JAVA=1 \
+ -e ARCHERY_INTEGRATION_WITH_JS=1 \
-e ARCHERY_INTEGRATION_WITH_NANOARROW=1 \
-e ARCHERY_INTEGRATION_WITH_RUST=1 \
conda-integration
From abef7af8490d706d52821e56afc6d1eb21e24faf Mon Sep 17 00:00:00 2001
From: Sutou Kouhei
Date: Mon, 26 May 2025 08:55:13 +0900
Subject: [PATCH 041/271] GH-770: Ensure updating Homebrew Python on macos-13
(#771)
## What's Changed
If we update older Python (e.g. Python 3.12) to newer Python (e.g.
Python 3.13), depended packages may update newer Python when we update
old Python.
Let's use newer Python -> older Python order instead to avoid the
situation.
Closes #770.
---
.github/workflows/rc.yml | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/rc.yml b/.github/workflows/rc.yml
index 7e3cf5f6f2..72456fa556 100644
--- a/.github/workflows/rc.yml
+++ b/.github/workflows/rc.yml
@@ -221,6 +221,10 @@ jobs:
# llvm@14 because llvm is newer than llvm@14.
brew uninstall llvm || :
+ # We can remove this when we drop support for
+ # macos-13. because macos-14 or later uses /opt/homebrew/
+ # not /usr/local/.
+ #
# Ensure updating python@XXX with the "--overwrite" option.
# If python@XXX is updated without "--overwrite", it causes
# a conflict error. Because Python 3 installed not by
@@ -229,10 +233,10 @@ jobs:
# tries to replace /usr/local/bin/2to3 and so on and causes
# a conflict error.
brew update
- for python_package in $(brew list | grep python@); do
+ for python_package in $(brew list | grep python@ | sort -r); do
brew install --overwrite ${python_package}
done
- brew install --overwrite python
+ brew install --overwrite python3
if [ "$(uname -m)" = "arm64" ]; then
# pkg-config formula is deprecated but it's still installed
From c6f608e863d3cefdfc41a433ed854e43b20a0925 Mon Sep 17 00:00:00 2001
From: Pepijn Van Eeckhoudt
Date: Wed, 28 May 2025 03:06:02 +0200
Subject: [PATCH 042/271] GH-765: Do not close/free imported BaseStruct objects
(#766)
## What's Changed
This PR removes the direct and indirect calls to `BaseStruct#close` from
`org.apache.arrow.c.Data`. By not eagerly closing/freeing these objects
callers can reuse instances multiple times.
Closes #765.
---
.../org/apache/arrow/c/ArrayImporter.java | 1 -
.../arrow/c/ArrowArrayStreamReader.java | 1 -
c/src/main/java/org/apache/arrow/c/Data.java | 219 ++++++++++++++++--
.../org/apache/arrow/c/RoundtripTest.java | 106 +++++++--
4 files changed, 287 insertions(+), 40 deletions(-)
diff --git a/c/src/main/java/org/apache/arrow/c/ArrayImporter.java b/c/src/main/java/org/apache/arrow/c/ArrayImporter.java
index b74fb1b473..f31a8a1faa 100644
--- a/c/src/main/java/org/apache/arrow/c/ArrayImporter.java
+++ b/c/src/main/java/org/apache/arrow/c/ArrayImporter.java
@@ -58,7 +58,6 @@ void importArray(ArrowArray src) {
ArrowArray ownedArray = ArrowArray.allocateNew(allocator);
ownedArray.save(snapshot);
src.markReleased();
- src.close();
recursionLevel = 0;
diff --git a/c/src/main/java/org/apache/arrow/c/ArrowArrayStreamReader.java b/c/src/main/java/org/apache/arrow/c/ArrowArrayStreamReader.java
index 07a88cd8d7..34a9c4ec03 100644
--- a/c/src/main/java/org/apache/arrow/c/ArrowArrayStreamReader.java
+++ b/c/src/main/java/org/apache/arrow/c/ArrowArrayStreamReader.java
@@ -44,7 +44,6 @@ final class ArrowArrayStreamReader extends ArrowReader {
this.ownedStream = ArrowArrayStream.allocateNew(allocator);
this.ownedStream.save(snapshot);
stream.markReleased();
- stream.close();
}
@Override
diff --git a/c/src/main/java/org/apache/arrow/c/Data.java b/c/src/main/java/org/apache/arrow/c/Data.java
index 0b4da33b4e..f9d2ee4542 100644
--- a/c/src/main/java/org/apache/arrow/c/Data.java
+++ b/c/src/main/java/org/apache/arrow/c/Data.java
@@ -231,6 +231,22 @@ public static void exportArrayStream(
new ArrayStreamExporter(allocator).export(out, reader);
}
+ /**
+ * Equivalent to calling {@link #importField(BufferAllocator, ArrowSchema,
+ * CDataDictionaryProvider, boolean) importField(allocator, schema, provider, true)}.
+ *
+ * @param allocator Buffer allocator for allocating dictionary vectors
+ * @param schema C data interface struct representing the field [inout]
+ * @param provider A dictionary provider will be initialized with empty dictionary vectors
+ * (optional)
+ * @return Imported field object
+ * @see #importField(BufferAllocator, ArrowSchema, CDataDictionaryProvider, boolean)
+ */
+ public static Field importField(
+ BufferAllocator allocator, ArrowSchema schema, CDataDictionaryProvider provider) {
+ return importField(allocator, schema, provider, true);
+ }
+
/**
* Import Java Field from the C data interface.
*
@@ -241,19 +257,42 @@ public static void exportArrayStream(
* @param schema C data interface struct representing the field [inout]
* @param provider A dictionary provider will be initialized with empty dictionary vectors
* (optional)
+ * @param closeImportedStructs if true, the ArrowSchema struct will be closed when this method
+ * completes.
* @return Imported field object
*/
public static Field importField(
- BufferAllocator allocator, ArrowSchema schema, CDataDictionaryProvider provider) {
+ BufferAllocator allocator,
+ ArrowSchema schema,
+ CDataDictionaryProvider provider,
+ boolean closeImportedStructs) {
try {
SchemaImporter importer = new SchemaImporter(allocator);
return importer.importField(schema, provider);
} finally {
schema.release();
- schema.close();
+ if (closeImportedStructs) {
+ schema.close();
+ }
}
}
+ /**
+ * Equivalent to calling {@link #importSchema(BufferAllocator, ArrowSchema,
+ * CDataDictionaryProvider, boolean) importSchema(allocator, schema, provider, true)}.
+ *
+ * @param allocator Buffer allocator for allocating dictionary vectors
+ * @param schema C data interface struct representing the field
+ * @param provider A dictionary provider will be initialized with empty dictionary vectors
+ * (optional)
+ * @return Imported schema object
+ * @see #importSchema(BufferAllocator, ArrowSchema, CDataDictionaryProvider, boolean)
+ */
+ public static Schema importSchema(
+ BufferAllocator allocator, ArrowSchema schema, CDataDictionaryProvider provider) {
+ return importSchema(allocator, schema, provider, true);
+ }
+
/**
* Import Java Schema from the C data interface.
*
@@ -264,11 +303,16 @@ public static Field importField(
* @param schema C data interface struct representing the field
* @param provider A dictionary provider will be initialized with empty dictionary vectors
* (optional)
+ * @param closeImportedStructs if true, the ArrowSchema struct will be closed when this method
+ * completes.
* @return Imported schema object
*/
public static Schema importSchema(
- BufferAllocator allocator, ArrowSchema schema, CDataDictionaryProvider provider) {
- Field structField = importField(allocator, schema, provider);
+ BufferAllocator allocator,
+ ArrowSchema schema,
+ CDataDictionaryProvider provider,
+ boolean closeImportedStructs) {
+ Field structField = importField(allocator, schema, provider, closeImportedStructs);
if (structField.getType().getTypeID() != ArrowTypeID.Struct) {
throw new IllegalArgumentException(
"Cannot import schema: ArrowSchema describes non-struct type");
@@ -276,24 +320,67 @@ public static Schema importSchema(
return new Schema(structField.getChildren(), structField.getMetadata());
}
+ /**
+ * Equivalent to calling {@link #importIntoVector(BufferAllocator, ArrowArray, FieldVector,
+ * DictionaryProvider, boolean)} importIntoVector(allocator, array, vector, provider, true)}.
+ *
+ * @param allocator Buffer allocator
+ * @param array C data interface struct holding the array data
+ * @param vector Imported vector object [out]
+ * @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @see #importIntoVector(BufferAllocator, ArrowArray, FieldVector, DictionaryProvider, boolean)
+ */
+ public static void importIntoVector(
+ BufferAllocator allocator,
+ ArrowArray array,
+ FieldVector vector,
+ DictionaryProvider provider) {
+ importIntoVector(allocator, array, vector, provider, true);
+ }
+
/**
* Import Java vector from the C data interface.
*
- *
The ArrowArray struct has its contents moved (as per the C data interface specification) to
- * a private object held alive by the resulting array.
+ *
On successful completion, the ArrowArray struct will have been moved (as per the C data
+ * interface specification) to a private object held alive by the resulting array.
*
* @param allocator Buffer allocator
* @param array C data interface struct holding the array data
* @param vector Imported vector object [out]
* @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @param closeImportedStructs if true, the ArrowArray struct will be closed when this method
+ * completes successfully.
*/
public static void importIntoVector(
BufferAllocator allocator,
ArrowArray array,
FieldVector vector,
- DictionaryProvider provider) {
+ DictionaryProvider provider,
+ boolean closeImportedStructs) {
ArrayImporter importer = new ArrayImporter(allocator, vector, provider);
importer.importArray(array);
+ if (closeImportedStructs) {
+ array.close();
+ }
+ }
+
+ /**
+ * Equivalent to calling {@link #importVector(BufferAllocator, ArrowArray, ArrowSchema,
+ * CDataDictionaryProvider, boolean) importVector(allocator, array, schema, provider, true)}.
+ *
+ * @param allocator Buffer allocator for allocating the output FieldVector
+ * @param array C data interface struct holding the array data
+ * @param schema C data interface struct holding the array type
+ * @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @return Imported vector object
+ * @see #importVector(BufferAllocator, ArrowArray, ArrowSchema, CDataDictionaryProvider, boolean)
+ */
+ public static FieldVector importVector(
+ BufferAllocator allocator,
+ ArrowArray array,
+ ArrowSchema schema,
+ CDataDictionaryProvider provider) {
+ return importVector(allocator, array, schema, provider, true);
}
/**
@@ -307,19 +394,42 @@ public static void importIntoVector(
* @param array C data interface struct holding the array data
* @param schema C data interface struct holding the array type
* @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @param closeImportedStructs if true, the ArrowArray struct will be closed when this method
+ * completes successfully and the ArrowSchema struct will be always be closed.
* @return Imported vector object
*/
public static FieldVector importVector(
BufferAllocator allocator,
ArrowArray array,
ArrowSchema schema,
- CDataDictionaryProvider provider) {
- Field field = importField(allocator, schema, provider);
+ CDataDictionaryProvider provider,
+ boolean closeImportedStructs) {
+ Field field = importField(allocator, schema, provider, closeImportedStructs);
FieldVector vector = field.createVector(allocator);
- importIntoVector(allocator, array, vector, provider);
+ importIntoVector(allocator, array, vector, provider, closeImportedStructs);
return vector;
}
+ /**
+ * Equivalent to calling {@link #importIntoVectorSchemaRoot(BufferAllocator, ArrowArray,
+ * VectorSchemaRoot, DictionaryProvider, boolean) importIntoVectorSchemaRoot(allocator, array,
+ * root, provider, true)}.
+ *
+ * @param allocator Buffer allocator
+ * @param array C data interface struct holding the record batch data
+ * @param root vector schema root to load into
+ * @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @see #importIntoVectorSchemaRoot(BufferAllocator, ArrowArray, VectorSchemaRoot,
+ * DictionaryProvider, boolean)
+ */
+ public static void importIntoVectorSchemaRoot(
+ BufferAllocator allocator,
+ ArrowArray array,
+ VectorSchemaRoot root,
+ DictionaryProvider provider) {
+ importIntoVectorSchemaRoot(allocator, array, root, provider, true);
+ }
+
/**
* Import record batch from the C data interface into vector schema root.
*
@@ -333,15 +443,18 @@ public static FieldVector importVector(
* @param array C data interface struct holding the record batch data
* @param root vector schema root to load into
* @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @param closeImportedStructs if true, the ArrowArray struct will be closed when this method
+ * completes successfully
*/
public static void importIntoVectorSchemaRoot(
BufferAllocator allocator,
ArrowArray array,
VectorSchemaRoot root,
- DictionaryProvider provider) {
+ DictionaryProvider provider,
+ boolean closeImportedStructs) {
try (StructVector structVector = StructVector.emptyWithDuplicates("", allocator)) {
structVector.initializeChildrenFromFields(root.getSchema().getFields());
- importIntoVector(allocator, array, structVector, provider);
+ importIntoVector(allocator, array, structVector, provider, closeImportedStructs);
StructVectorUnloader unloader = new StructVectorUnloader(structVector);
VectorLoader loader = new VectorLoader(root);
try (ArrowRecordBatch recordBatch = unloader.getRecordBatch()) {
@@ -350,6 +463,21 @@ public static void importIntoVectorSchemaRoot(
}
}
+ /**
+ * Equivalent to calling {@link #importVectorSchemaRoot(BufferAllocator, ArrowSchema,
+ * CDataDictionaryProvider, boolean) importVectorSchemaRoot(allocator, schema, provider, true)}.
+ *
+ * @param allocator Buffer allocator for allocating the output VectorSchemaRoot
+ * @param schema C data interface struct holding the record batch schema
+ * @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @return Imported vector schema root
+ * @see #importVectorSchemaRoot(BufferAllocator, ArrowSchema, CDataDictionaryProvider, boolean)
+ */
+ public static VectorSchemaRoot importVectorSchemaRoot(
+ BufferAllocator allocator, ArrowSchema schema, CDataDictionaryProvider provider) {
+ return importVectorSchemaRoot(allocator, schema, provider, true);
+ }
+
/**
* Import Java vector schema root from a C data interface Schema.
*
@@ -360,11 +488,37 @@ public static void importIntoVectorSchemaRoot(
* @param allocator Buffer allocator for allocating the output VectorSchemaRoot
* @param schema C data interface struct holding the record batch schema
* @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @param closeImportedStructs if true, the ArrowSchema struct will be closed when this method
+ * completes
* @return Imported vector schema root
*/
public static VectorSchemaRoot importVectorSchemaRoot(
- BufferAllocator allocator, ArrowSchema schema, CDataDictionaryProvider provider) {
- return importVectorSchemaRoot(allocator, null, schema, provider);
+ BufferAllocator allocator,
+ ArrowSchema schema,
+ CDataDictionaryProvider provider,
+ boolean closeImportedStructs) {
+ return importVectorSchemaRoot(allocator, null, schema, provider, closeImportedStructs);
+ }
+
+ /**
+ * Equivalent to calling {@link #importVectorSchemaRoot(BufferAllocator, ArrowArray, ArrowSchema,
+ * CDataDictionaryProvider, boolean) importVectorSchemaRoot(allocator, array, schema, provider,
+ * true)}.
+ *
+ * @param allocator Buffer allocator for allocating the output VectorSchemaRoot
+ * @param array C data interface struct holding the record batch data (optional)
+ * @param schema C data interface struct holding the record batch schema
+ * @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @return Imported vector schema root
+ * @see #importVectorSchemaRoot(BufferAllocator, ArrowArray, ArrowSchema, CDataDictionaryProvider,
+ * boolean)
+ */
+ public static VectorSchemaRoot importVectorSchemaRoot(
+ BufferAllocator allocator,
+ ArrowArray array,
+ ArrowSchema schema,
+ CDataDictionaryProvider provider) {
+ return importVectorSchemaRoot(allocator, array, schema, provider, true);
}
/**
@@ -383,29 +537,56 @@ public static VectorSchemaRoot importVectorSchemaRoot(
* @param array C data interface struct holding the record batch data (optional)
* @param schema C data interface struct holding the record batch schema
* @param provider Dictionary provider to load dictionary vectors to (optional)
+ * @param closeImportedStructs if true, the ArrowArray struct will be closed when this method
+ * completes successfully and the ArrowSchema struct will be always be closed.
* @return Imported vector schema root
*/
public static VectorSchemaRoot importVectorSchemaRoot(
BufferAllocator allocator,
ArrowArray array,
ArrowSchema schema,
- CDataDictionaryProvider provider) {
+ CDataDictionaryProvider provider,
+ boolean closeImportedStructs) {
VectorSchemaRoot vsr =
- VectorSchemaRoot.create(importSchema(allocator, schema, provider), allocator);
+ VectorSchemaRoot.create(
+ importSchema(allocator, schema, provider, closeImportedStructs), allocator);
if (array != null) {
- importIntoVectorSchemaRoot(allocator, array, vsr, provider);
+ importIntoVectorSchemaRoot(allocator, array, vsr, provider, closeImportedStructs);
}
return vsr;
}
/**
- * Import an ArrowArrayStream as an {@link ArrowReader}.
+ * Equivalent to calling {@link #importArrayStream(BufferAllocator, ArrowArrayStream, boolean)
+ * importArrayStream(allocator, stream, true)}.
*
* @param allocator Buffer allocator for allocating the output data.
* @param stream C stream interface struct to import.
* @return Imported reader
+ * @see #importArrayStream(BufferAllocator, ArrowArrayStream, boolean)
*/
public static ArrowReader importArrayStream(BufferAllocator allocator, ArrowArrayStream stream) {
- return new ArrowArrayStreamReader(allocator, stream);
+ return importArrayStream(allocator, stream, true);
+ }
+
+ /**
+ * Import an ArrowArrayStream as an {@link ArrowReader}.
+ *
+ *
On successful completion, the ArrowArrayStream struct will have been moved (as per the C
+ * data interface specification) to a private object held alive by the resulting ArrowReader.
+ *
+ * @param allocator Buffer allocator for allocating the output data.
+ * @param stream C stream interface struct to import.
+ * @param closeImportedStructs if true, the ArrowArrayStream struct will be closed when this
+ * method completes successfully
+ * @return Imported reader
+ */
+ public static ArrowReader importArrayStream(
+ BufferAllocator allocator, ArrowArrayStream stream, boolean closeImportedStructs) {
+ ArrowArrayStreamReader reader = new ArrowArrayStreamReader(allocator, stream);
+ if (closeImportedStructs) {
+ stream.close();
+ }
+ return reader;
}
}
diff --git a/c/src/test/java/org/apache/arrow/c/RoundtripTest.java b/c/src/test/java/org/apache/arrow/c/RoundtripTest.java
index 6d68449c0b..010a305495 100644
--- a/c/src/test/java/org/apache/arrow/c/RoundtripTest.java
+++ b/c/src/test/java/org/apache/arrow/c/RoundtripTest.java
@@ -17,9 +17,7 @@
package org.apache.arrow.c;
import static org.apache.arrow.vector.testing.ValueVectorDataPopulator.setVector;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -958,6 +956,50 @@ public void testVectorSchemaRootWithDuplicatedFieldNames() {
@Test
public void testSchema() {
+ Schema schema = createSchema();
+ // Consumer allocates empty ArrowSchema
+ try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator)) {
+ // Producer fills the schema with data
+ exportSchema(schema, consumerArrowSchema);
+
+ // Consumer imports schema
+ Schema importedSchema = Data.importSchema(allocator, consumerArrowSchema, null);
+ assertEquals(schema.toJson(), importedSchema.toJson());
+ }
+ }
+
+ @Test
+ public void testSchemaStructReuse() {
+ Schema schema = createSchema();
+ // Consumer allocates empty ArrowSchema
+ try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator)) {
+ // Producer fills the schema with data
+ exportSchema(schema, consumerArrowSchema);
+
+ // Consumer imports schema
+ Schema importedSchema = Data.importSchema(allocator, consumerArrowSchema, null, false);
+ assertEquals(schema.toJson(), importedSchema.toJson());
+
+ // Imported struct should be released but not closed
+ assertEquals(0, consumerArrowSchema.snapshot().release);
+ assertNotEquals(0, consumerArrowSchema.memoryAddress());
+
+ // Export and import again
+ exportSchema(schema, consumerArrowSchema);
+ importedSchema = Data.importSchema(allocator, consumerArrowSchema, null, false);
+ assertEquals(schema.toJson(), importedSchema.toJson());
+ assertEquals(0, consumerArrowSchema.snapshot().release);
+ assertNotEquals(0, consumerArrowSchema.memoryAddress());
+ }
+ }
+
+ private void exportSchema(Schema schema, ArrowSchema targetArrowSchema) {
+ try (ArrowSchema arrowSchema = ArrowSchema.wrap(targetArrowSchema.memoryAddress())) {
+ Data.exportSchema(allocator, schema, null, arrowSchema);
+ }
+ }
+
+ private static Schema createSchema() {
Field decimalField =
new Field("inner1", FieldType.nullable(new ArrowType.Decimal(19, 4, 128)), null);
Field strField = new Field("inner2", FieldType.nullable(new ArrowType.Utf8()), null);
@@ -968,16 +1010,7 @@ public void testSchema() {
Arrays.asList(decimalField, strField));
Field intField = new Field("col2", FieldType.nullable(new ArrowType.Int(32, true)), null);
Schema schema = new Schema(Arrays.asList(itemField, intField));
- // Consumer allocates empty ArrowSchema
- try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator)) {
- // Producer fills the schema with data
- try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress())) {
- Data.exportSchema(allocator, schema, null, arrowSchema);
- }
- // Consumer imports schema
- Schema importedSchema = Data.importSchema(allocator, consumerArrowSchema, null);
- assertEquals(schema.toJson(), importedSchema.toJson());
- }
+ return schema;
}
@Test
@@ -1002,12 +1035,8 @@ public void testImportReleasedArray() {
try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
// Producer creates structures from existing memory pointers
- try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress());
- ArrowArray arrowArray = ArrowArray.wrap(consumerArrowArray.memoryAddress())) {
- // Producer exports vector into the C Data Interface structures
- try (final NullVector vector = new NullVector()) {
- Data.exportVector(allocator, vector, null, arrowArray, arrowSchema);
- }
+ try (final NullVector vector = new NullVector()) {
+ exportFieldVector(vector, consumerArrowSchema, consumerArrowArray);
}
// Release array structure
@@ -1025,6 +1054,45 @@ public void testImportReleasedArray() {
}
}
+ @Test
+ public void testArrayStructReuse() {
+ // Consumer allocates empty structures
+ try (ArrowSchema consumerArrowSchema = ArrowSchema.allocateNew(allocator);
+ ArrowArray consumerArrowArray = ArrowArray.allocateNew(allocator)) {
+ // Producer creates structures from existing memory pointers
+ try (final NullVector vector = new NullVector()) {
+ exportFieldVector(vector, consumerArrowSchema, consumerArrowArray);
+ }
+ Data.importVector(allocator, consumerArrowArray, consumerArrowSchema, null, false);
+
+ // Imported structs should be released but not closed
+ assertEquals(0, consumerArrowSchema.snapshot().release);
+ assertNotEquals(0, consumerArrowSchema.memoryAddress());
+ assertEquals(0, consumerArrowArray.snapshot().release);
+ assertNotEquals(0, consumerArrowArray.memoryAddress());
+
+ try (final NullVector vector = new NullVector()) {
+ exportFieldVector(vector, consumerArrowSchema, consumerArrowArray);
+ }
+ Data.importVector(allocator, consumerArrowArray, consumerArrowSchema, null, false);
+
+ // Imported structs should be released but not closed
+ assertEquals(0, consumerArrowSchema.snapshot().release);
+ assertNotEquals(0, consumerArrowSchema.memoryAddress());
+ assertEquals(0, consumerArrowArray.snapshot().release);
+ assertNotEquals(0, consumerArrowArray.memoryAddress());
+ }
+ }
+
+ private void exportFieldVector(
+ FieldVector vector, ArrowSchema consumerArrowSchema, ArrowArray consumerArrowArray) {
+ try (ArrowSchema arrowSchema = ArrowSchema.wrap(consumerArrowSchema.memoryAddress());
+ ArrowArray arrowArray = ArrowArray.wrap(consumerArrowArray.memoryAddress())) {
+ // Producer exports vector into the C Data Interface structures
+ Data.exportVector(allocator, vector, null, arrowArray, arrowSchema);
+ }
+ }
+
private VectorSchemaRoot createTestVSR() {
BitVector bitVector = new BitVector("boolean", allocator);
From 17f85a1fba64aef4552ed2fabc5a76bc038a1ed6 Mon Sep 17 00:00:00 2001
From: rtadepalli <105760760+rtadepalli@users.noreply.github.com>
Date: Tue, 27 May 2025 21:06:55 -0400
Subject: [PATCH 043/271] GH-70: Move from `hamcrest` to `assertj` in
`flight-sql` (#772)
## What's Changed
Series of PRs to consolidate on using `assertj` in tests as part of
https://github.com/apache/arrow-java/issues/70.
---
flight/flight-sql/pom.xml | 10 +-
.../arrow/flight/sql/test/TestFlightSql.java | 264 +++++++++---------
.../sql/test/TestFlightSqlStateless.java | 9 +-
.../flight/sql/test/TestFlightSqlStreams.java | 29 +-
4 files changed, 150 insertions(+), 162 deletions(-)
diff --git a/flight/flight-sql/pom.xml b/flight/flight-sql/pom.xml
index 5f06a5e9eb..15d00e3e18 100644
--- a/flight/flight-sql/pom.xml
+++ b/flight/flight-sql/pom.xml
@@ -116,16 +116,16 @@ under the License.
1.13.1test
-
- org.hamcrest
- hamcrest
- test
- commons-clicommons-cli1.9.0true
+
+ org.assertj
+ assertj-core
+ test
+
diff --git a/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSql.java b/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSql.java
index 3f769363fb..e2934ab1e9 100644
--- a/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSql.java
+++ b/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSql.java
@@ -21,10 +21,7 @@
import static java.util.Collections.singletonList;
import static org.apache.arrow.flight.sql.util.FlightStreamUtils.getResults;
import static org.apache.arrow.util.AutoCloseables.close;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -40,6 +37,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.stream.IntStream;
import org.apache.arrow.flight.CancelFlightInfoRequest;
@@ -76,8 +74,7 @@
import org.apache.arrow.vector.types.pojo.Schema;
import org.apache.arrow.vector.util.Text;
import org.apache.arrow.vector.util.VectorBatchAppender;
-import org.hamcrest.Matcher;
-import org.hamcrest.MatcherAssert;
+import org.assertj.core.api.Condition;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -247,16 +244,15 @@ private static List> getNonConformingResultsForGetSqlInfo(
@Test
public void testGetTablesSchema() {
final FlightInfo info = sqlClient.getTables(null, null, null, null, true);
- MatcherAssert.assertThat(
- info.getSchemaOptional(), is(Optional.of(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA)));
+ assertThat(info.getSchemaOptional())
+ .isEqualTo(Optional.of(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA));
}
@Test
public void testGetTablesSchemaExcludeSchema() {
final FlightInfo info = sqlClient.getTables(null, null, null, null, false);
- MatcherAssert.assertThat(
- info.getSchemaOptional(),
- is(Optional.of(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA_NO_SCHEMA)));
+ assertThat(info.getSchemaOptional())
+ .isEqualTo(Optional.of(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA_NO_SCHEMA));
}
@Test
@@ -266,8 +262,8 @@ public void testGetTablesResultNoSchema() throws Exception {
sqlClient.getTables(null, null, null, null, false).getEndpoints().get(0).getTicket())) {
assertAll(
() -> {
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA_NO_SCHEMA));
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA_NO_SCHEMA);
},
() -> {
final List> results = getResults(stream);
@@ -301,7 +297,7 @@ public void testGetTablesResultNoSchema() throws Exception {
asList(null /* TODO No catalog yet */, "SYSIBM", "SYSDUMMY1", "SYSTEM TABLE"),
asList(null /* TODO No catalog yet */, "APP", "FOREIGNTABLE", "TABLE"),
asList(null /* TODO No catalog yet */, "APP", "INTTABLE", "TABLE"));
- MatcherAssert.assertThat(results, is(expectedResults));
+ assertThat(results).isEqualTo(expectedResults);
});
}
}
@@ -318,8 +314,8 @@ public void testGetTablesResultFilteredNoSchema() throws Exception {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA_NO_SCHEMA)),
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA_NO_SCHEMA),
() -> {
final List> results = getResults(stream);
final List> expectedResults =
@@ -327,7 +323,7 @@ public void testGetTablesResultFilteredNoSchema() throws Exception {
// catalog_name | schema_name | table_name | table_type | table_schema
asList(null /* TODO No catalog yet */, "APP", "FOREIGNTABLE", "TABLE"),
asList(null /* TODO No catalog yet */, "APP", "INTTABLE", "TABLE"));
- MatcherAssert.assertThat(results, is(expectedResults));
+ assertThat(results).isEqualTo(expectedResults);
});
}
}
@@ -343,11 +339,9 @@ public void testGetTablesResultFilteredWithSchema() throws Exception {
.getTicket())) {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA)),
+ assertThat(stream.getSchema()).isEqualTo(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA),
() -> {
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA));
+ assertThat(stream.getSchema()).isEqualTo(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA);
final List> results = getResults(stream);
final List> expectedResults =
ImmutableList.of(
@@ -487,7 +481,7 @@ public void testGetTablesResultFilteredWithSchema() throws Exception {
.getMetadataMap()),
null)))
.toJson()));
- MatcherAssert.assertThat(results, is(expectedResults));
+ assertThat(results).isEqualTo(expectedResults);
});
}
}
@@ -498,11 +492,11 @@ public void testSimplePreparedStatementSchema() throws Exception {
assertAll(
() -> {
final Schema actualSchema = preparedStatement.getResultSetSchema();
- MatcherAssert.assertThat(actualSchema, is(SCHEMA_INT_TABLE));
+ assertThat(actualSchema).isEqualTo(SCHEMA_INT_TABLE);
},
() -> {
final FlightInfo info = preparedStatement.execute();
- MatcherAssert.assertThat(info.getSchemaOptional(), is(Optional.of(SCHEMA_INT_TABLE)));
+ assertThat(info.getSchemaOptional()).isEqualTo(Optional.of(SCHEMA_INT_TABLE));
});
}
}
@@ -513,10 +507,8 @@ public void testSimplePreparedStatementResults() throws Exception {
final FlightStream stream =
sqlClient.getStream(preparedStatement.execute().getEndpoints().get(0).getTicket())) {
assertAll(
- () -> MatcherAssert.assertThat(stream.getSchema(), is(SCHEMA_INT_TABLE)),
- () ->
- MatcherAssert.assertThat(
- getResults(stream), is(EXPECTED_RESULTS_FOR_STAR_SELECT_QUERY)));
+ () -> assertThat(stream.getSchema()).isEqualTo(SCHEMA_INT_TABLE),
+ () -> assertThat(getResults(stream)).isEqualTo(EXPECTED_RESULTS_FOR_STAR_SELECT_QUERY));
}
}
@@ -538,10 +530,8 @@ public void testSimplePreparedStatementResultsWithParameterBinding() throws Exce
FlightStream stream = sqlClient.getStream(flightInfo.getEndpoints().get(0).getTicket());
assertAll(
- () -> MatcherAssert.assertThat(stream.getSchema(), is(SCHEMA_INT_TABLE)),
- () ->
- MatcherAssert.assertThat(
- getResults(stream), is(EXPECTED_RESULTS_FOR_PARAMETER_BINDING)));
+ () -> assertThat(stream.getSchema()).isEqualTo(SCHEMA_INT_TABLE),
+ () -> assertThat(getResults(stream)).isEqualTo(EXPECTED_RESULTS_FOR_PARAMETER_BINDING));
}
}
}
@@ -579,8 +569,8 @@ public void testSimplePreparedStatementUpdateResults() throws SQLException {
deletedRows = deletePrepare.executeUpdate();
}
assertAll(
- () -> MatcherAssert.assertThat(updatedRows, is(10L)),
- () -> MatcherAssert.assertThat(deletedRows, is(10L)));
+ () -> assertThat(updatedRows).isEqualTo(10L),
+ () -> assertThat(deletedRows).isEqualTo(10L));
}
}
}
@@ -647,7 +637,7 @@ public void testBulkIngest() throws IOException {
null,
null));
- MatcherAssert.assertThat(updatedRows, is(-1L));
+ assertThat(updatedRows).isEqualTo(-1L);
// Ingest directly using VectorSchemaRoot
populateNext10RowsInIngestRootBatch(
@@ -672,7 +662,7 @@ public void testBulkIngest() throws IOException {
deletedRows = deletePrepare.executeUpdate();
}
- MatcherAssert.assertThat(deletedRows, is(30L));
+ assertThat(deletedRows).isEqualTo(30L);
}
}
}
@@ -709,8 +699,7 @@ public void testSimplePreparedStatementUpdateResultsWithoutParameters() throws S
final long deletedRows = deletePrepare.executeUpdate();
assertAll(
- () -> MatcherAssert.assertThat(updatedRows, is(1L)),
- () -> MatcherAssert.assertThat(deletedRows, is(1L)));
+ () -> assertThat(updatedRows).isEqualTo(1L), () -> assertThat(deletedRows).isEqualTo(1L));
}
}
@@ -719,19 +708,19 @@ public void testSimplePreparedStatementClosesProperly() {
final PreparedStatement preparedStatement = sqlClient.prepare("SELECT * FROM intTable");
assertAll(
() -> {
- MatcherAssert.assertThat(preparedStatement.isClosed(), is(false));
+ assertThat(preparedStatement.isClosed()).isEqualTo(false);
},
() -> {
preparedStatement.close();
- MatcherAssert.assertThat(preparedStatement.isClosed(), is(true));
+ assertThat(preparedStatement.isClosed()).isEqualTo(true);
});
}
@Test
public void testGetCatalogsSchema() {
final FlightInfo info = sqlClient.getCatalogs();
- MatcherAssert.assertThat(
- info.getSchemaOptional(), is(Optional.of(FlightSqlProducer.Schemas.GET_CATALOGS_SCHEMA)));
+ assertThat(info.getSchemaOptional())
+ .isEqualTo(Optional.of(FlightSqlProducer.Schemas.GET_CATALOGS_SCHEMA));
}
@Test
@@ -740,11 +729,11 @@ public void testGetCatalogsResults() throws Exception {
sqlClient.getStream(sqlClient.getCatalogs().getEndpoints().get(0).getTicket())) {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_CATALOGS_SCHEMA)),
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_CATALOGS_SCHEMA),
() -> {
List> catalogs = getResults(stream);
- MatcherAssert.assertThat(catalogs, is(emptyList()));
+ assertThat(catalogs).isEqualTo(emptyList());
});
}
}
@@ -752,9 +741,8 @@ public void testGetCatalogsResults() throws Exception {
@Test
public void testGetTableTypesSchema() {
final FlightInfo info = sqlClient.getTableTypes();
- MatcherAssert.assertThat(
- info.getSchemaOptional(),
- is(Optional.of(FlightSqlProducer.Schemas.GET_TABLE_TYPES_SCHEMA)));
+ assertThat(info.getSchemaOptional())
+ .isEqualTo(Optional.of(FlightSqlProducer.Schemas.GET_TABLE_TYPES_SCHEMA));
}
@Test
@@ -763,8 +751,8 @@ public void testGetTableTypesResult() throws Exception {
sqlClient.getStream(sqlClient.getTableTypes().getEndpoints().get(0).getTicket())) {
assertAll(
() -> {
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_TABLE_TYPES_SCHEMA));
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_TABLE_TYPES_SCHEMA);
},
() -> {
final List> tableTypes = getResults(stream);
@@ -775,7 +763,7 @@ public void testGetTableTypesResult() throws Exception {
singletonList("SYSTEM TABLE"),
singletonList("TABLE"),
singletonList("VIEW"));
- MatcherAssert.assertThat(tableTypes, is(expectedTableTypes));
+ assertThat(tableTypes).isEqualTo(expectedTableTypes);
});
}
}
@@ -783,8 +771,8 @@ public void testGetTableTypesResult() throws Exception {
@Test
public void testGetSchemasSchema() {
final FlightInfo info = sqlClient.getSchemas(null, null);
- MatcherAssert.assertThat(
- info.getSchemaOptional(), is(Optional.of(FlightSqlProducer.Schemas.GET_SCHEMAS_SCHEMA)));
+ assertThat(info.getSchemaOptional())
+ .isEqualTo(Optional.of(FlightSqlProducer.Schemas.GET_SCHEMAS_SCHEMA));
}
@Test
@@ -793,8 +781,7 @@ public void testGetSchemasResult() throws Exception {
sqlClient.getStream(sqlClient.getSchemas(null, null).getEndpoints().get(0).getTicket())) {
assertAll(
() -> {
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_SCHEMAS_SCHEMA));
+ assertThat(stream.getSchema()).isEqualTo(FlightSqlProducer.Schemas.GET_SCHEMAS_SCHEMA);
},
() -> {
final List> schemas = getResults(stream);
@@ -812,7 +799,7 @@ public void testGetSchemasResult() throws Exception {
asList(null /* TODO Add catalog. */, "SYSIBM"),
asList(null /* TODO Add catalog. */, "SYSPROC"),
asList(null /* TODO Add catalog. */, "SYSSTAT"));
- MatcherAssert.assertThat(schemas, is(expectedSchemas));
+ assertThat(schemas).isEqualTo(expectedSchemas);
});
}
}
@@ -825,24 +812,24 @@ public void testGetPrimaryKey() {
final List> results = getResults(stream);
assertAll(
- () -> MatcherAssert.assertThat(results.size(), is(1)),
+ () -> assertThat(results.size()).isEqualTo(1),
() -> {
final List result = results.get(0);
assertAll(
- () -> MatcherAssert.assertThat(result.get(0), is("")),
- () -> MatcherAssert.assertThat(result.get(1), is("APP")),
- () -> MatcherAssert.assertThat(result.get(2), is("INTTABLE")),
- () -> MatcherAssert.assertThat(result.get(3), is("ID")),
- () -> MatcherAssert.assertThat(result.get(4), is("1")),
- () -> MatcherAssert.assertThat(result.get(5), notNullValue()));
+ () -> assertThat(result.get(0)).isEqualTo(""),
+ () -> assertThat(result.get(1)).isEqualTo("APP"),
+ () -> assertThat(result.get(2)).isEqualTo("INTTABLE"),
+ () -> assertThat(result.get(3)).isEqualTo("ID"),
+ () -> assertThat(result.get(4)).isEqualTo("1"),
+ () -> assertThat(result.get(5)).isNotNull());
});
}
@Test
public void testGetSqlInfoSchema() {
final FlightInfo info = sqlClient.getSqlInfo();
- MatcherAssert.assertThat(
- info.getSchemaOptional(), is(Optional.of(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA)));
+ assertThat(info.getSchemaOptional())
+ .isEqualTo(Optional.of(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA));
}
@Test
@@ -851,11 +838,11 @@ public void testGetSqlInfoResults() throws Exception {
try (final FlightStream stream = sqlClient.getStream(info.getEndpoints().get(0).getTicket())) {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA)),
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA),
() ->
- MatcherAssert.assertThat(
- getNonConformingResultsForGetSqlInfo(getResults(stream)), is(emptyList())));
+ assertThat(getNonConformingResultsForGetSqlInfo(getResults(stream)))
+ .isEqualTo(emptyList()));
}
}
@@ -866,11 +853,11 @@ public void testGetSqlInfoResultsWithSingleArg() throws Exception {
try (final FlightStream stream = sqlClient.getStream(info.getEndpoints().get(0).getTicket())) {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA)),
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA),
() ->
- MatcherAssert.assertThat(
- getNonConformingResultsForGetSqlInfo(getResults(stream), arg), is(emptyList())));
+ assertThat(getNonConformingResultsForGetSqlInfo(getResults(stream), arg))
+ .isEqualTo(emptyList()));
}
}
@@ -895,11 +882,11 @@ public void testGetSqlInfoResultsWithManyArgs() throws Exception {
try (final FlightStream stream = sqlClient.getStream(info.getEndpoints().get(0).getTicket())) {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA)),
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA),
() ->
- MatcherAssert.assertThat(
- getNonConformingResultsForGetSqlInfo(getResults(stream), args), is(emptyList())));
+ assertThat(getNonConformingResultsForGetSqlInfo(getResults(stream), args))
+ .isEqualTo(emptyList()));
}
}
@@ -915,28 +902,30 @@ public void testGetCommandExportedKeys() throws Exception {
final List> results = getResults(stream);
- final List> matchers =
+ final List> matchers =
asList(
- nullValue(String.class), // pk_catalog_name
- is("APP"), // pk_schema_name
- is("FOREIGNTABLE"), // pk_table_name
- is("ID"), // pk_column_name
- nullValue(String.class), // fk_catalog_name
- is("APP"), // fk_schema_name
- is("INTTABLE"), // fk_table_name
- is("FOREIGNID"), // fk_column_name
- is("1"), // key_sequence
- containsString("SQL"), // fk_key_name
- containsString("SQL"), // pk_key_name
- is("3"), // update_rule
- is("3")); // delete_rule
+ new Condition<>(Objects::isNull, "pk_catalog_name expected to be null"),
+ new Condition<>(c -> c.equals("APP"), "pk_schema_name expected to equal APP"),
+ new Condition<>(
+ c -> c.equals("FOREIGNTABLE"), "pk_table_name should equal FOREIGNTABLE"),
+ new Condition<>(c -> c.equals("ID"), "pk_column_name should equal ID"),
+ new Condition<>(Objects::isNull, "fk_catalog_name expected to be null"),
+ new Condition<>(c -> c.equals("APP"), "fk_schema_name expected to be APP"),
+ new Condition<>(c -> c.equals("INTTABLE"), "fk_table_name expeced to be INTTABLE"),
+ new Condition<>(
+ c -> c.equals("FOREIGNID"), "fk_column_name expected to equal FOREIGNID"),
+ new Condition<>(c -> c.equals("1"), "key_sequence expected to equal 1"),
+ new Condition<>(c -> c.contains("SQL"), "fk_key_name expected to contain SQL"),
+ new Condition<>(c -> c.contains("SQL"), "pk_key_name expected to contain SQL"),
+ new Condition<>(c -> c.equals("3"), "update_rule expected to equal 3"),
+ new Condition<>(c -> c.equals("3"), "delete_rule expected to equal 3"));
final List assertions = new ArrayList<>();
assertEquals(1, results.size());
for (int i = 0; i < matchers.size(); i++) {
final String actual = results.get(0).get(i);
- final Matcher expected = matchers.get(i);
- assertions.add(() -> MatcherAssert.assertThat(actual, expected));
+ final Condition expected = matchers.get(i);
+ assertions.add(() -> assertThat(actual).satisfies(expected));
}
assertAll(assertions);
}
@@ -954,28 +943,30 @@ public void testGetCommandImportedKeys() throws Exception {
final List> results = getResults(stream);
- final List> matchers =
+ final List> matchers =
asList(
- nullValue(String.class), // pk_catalog_name
- is("APP"), // pk_schema_name
- is("FOREIGNTABLE"), // pk_table_name
- is("ID"), // pk_column_name
- nullValue(String.class), // fk_catalog_name
- is("APP"), // fk_schema_name
- is("INTTABLE"), // fk_table_name
- is("FOREIGNID"), // fk_column_name
- is("1"), // key_sequence
- containsString("SQL"), // fk_key_name
- containsString("SQL"), // pk_key_name
- is("3"), // update_rule
- is("3")); // delete_rule
+ new Condition<>(Objects::isNull, "pk_catalog_name expected to be null"),
+ new Condition<>(c -> c.equals("APP"), "pk_schema_name expected to equal APP"),
+ new Condition<>(
+ c -> c.equals("FOREIGNTABLE"), "pk_table_name should equal FOREIGNTABLE"),
+ new Condition<>(c -> c.equals("ID"), "pk_column_name should equal ID"),
+ new Condition<>(Objects::isNull, "fk_catalog_name expected to be null"),
+ new Condition<>(c -> c.equals("APP"), "fk_schema_name expected to be APP"),
+ new Condition<>(c -> c.equals("INTTABLE"), "fk_table_name expeced to be INTTABLE"),
+ new Condition<>(
+ c -> c.equals("FOREIGNID"), "fk_column_name expected to equal FOREIGNID"),
+ new Condition<>(c -> c.equals("1"), "key_sequence expected to equal 1"),
+ new Condition<>(c -> c.contains("SQL"), "fk_key_name expected to contain SQL"),
+ new Condition<>(c -> c.contains("SQL"), "pk_key_name expected to contain SQL"),
+ new Condition<>(c -> c.equals("3"), "update_rule expected to equal 3"),
+ new Condition<>(c -> c.equals("3"), "delete_rule expected to equal 3"));
assertEquals(1, results.size());
final List assertions = new ArrayList<>();
for (int i = 0; i < matchers.size(); i++) {
final String actual = results.get(0).get(i);
- final Matcher expected = matchers.get(i);
- assertions.add(() -> MatcherAssert.assertThat(actual, expected));
+ final Condition expected = matchers.get(i);
+ assertions.add(() -> assertThat(actual).satisfies(expected));
}
assertAll(assertions);
}
@@ -1431,7 +1422,7 @@ public void testGetTypeInfo() throws Exception {
null,
null,
null));
- MatcherAssert.assertThat(results, is(matchers));
+ assertThat(results).isEqualTo(matchers);
}
}
@@ -1465,7 +1456,7 @@ public void testGetTypeInfoWithFiltering() throws Exception {
null,
"10",
null));
- MatcherAssert.assertThat(results, is(matchers));
+ assertThat(results).isEqualTo(matchers);
}
}
@@ -1479,28 +1470,30 @@ public void testGetCommandCrossReference() throws Exception {
final List> results = getResults(stream);
- final List> matchers =
+ final List> matchers =
asList(
- nullValue(String.class), // pk_catalog_name
- is("APP"), // pk_schema_name
- is("FOREIGNTABLE"), // pk_table_name
- is("ID"), // pk_column_name
- nullValue(String.class), // fk_catalog_name
- is("APP"), // fk_schema_name
- is("INTTABLE"), // fk_table_name
- is("FOREIGNID"), // fk_column_name
- is("1"), // key_sequence
- containsString("SQL"), // fk_key_name
- containsString("SQL"), // pk_key_name
- is("3"), // update_rule
- is("3")); // delete_rule
+ new Condition<>(Objects::isNull, "pk_catalog_name expected to be null"),
+ new Condition<>(c -> c.equals("APP"), "pk_schema_name expected to equal APP"),
+ new Condition<>(
+ c -> c.equals("FOREIGNTABLE"), "pk_table_name should equal FOREIGNTABLE"),
+ new Condition<>(c -> c.equals("ID"), "pk_column_name should equal ID"),
+ new Condition<>(Objects::isNull, "fk_catalog_name expected to be null"),
+ new Condition<>(c -> c.equals("APP"), "fk_schema_name expected to be APP"),
+ new Condition<>(c -> c.equals("INTTABLE"), "fk_table_name expeced to be INTTABLE"),
+ new Condition<>(
+ c -> c.equals("FOREIGNID"), "fk_column_name expected to equal FOREIGNID"),
+ new Condition<>(c -> c.equals("1"), "key_sequence expected to equal 1"),
+ new Condition<>(c -> c.contains("SQL"), "fk_key_name expected to contain SQL"),
+ new Condition<>(c -> c.contains("SQL"), "pk_key_name expected to contain SQL"),
+ new Condition<>(c -> c.equals("3"), "update_rule expected to equal 3"),
+ new Condition<>(c -> c.equals("3"), "delete_rule expected to equal 3"));
assertEquals(1, results.size());
final List assertions = new ArrayList<>();
for (int i = 0; i < matchers.size(); i++) {
final String actual = results.get(0).get(i);
- final Matcher expected = matchers.get(i);
- assertions.add(() -> MatcherAssert.assertThat(actual, expected));
+ final Condition expected = matchers.get(i);
+ assertions.add(() -> assertThat(actual).satisfies(expected));
}
assertAll(assertions);
}
@@ -1509,7 +1502,7 @@ public void testGetCommandCrossReference() throws Exception {
@Test
public void testCreateStatementSchema() throws Exception {
final FlightInfo info = sqlClient.execute("SELECT * FROM intTable");
- MatcherAssert.assertThat(info.getSchemaOptional(), is(Optional.of(SCHEMA_INT_TABLE)));
+ assertThat(info.getSchemaOptional()).isEqualTo(Optional.of(SCHEMA_INT_TABLE));
// Consume statement to close connection before cache eviction
try (FlightStream stream = sqlClient.getStream(info.getEndpoints().get(0).getTicket())) {
@@ -1526,11 +1519,10 @@ public void testCreateStatementResults() throws Exception {
sqlClient.execute("SELECT * FROM intTable").getEndpoints().get(0).getTicket())) {
assertAll(
() -> {
- MatcherAssert.assertThat(stream.getSchema(), is(SCHEMA_INT_TABLE));
+ assertThat(stream.getSchema()).isEqualTo(SCHEMA_INT_TABLE);
},
() -> {
- MatcherAssert.assertThat(
- getResults(stream), is(EXPECTED_RESULTS_FOR_STAR_SELECT_QUERY));
+ assertThat(getResults(stream)).isEqualTo(EXPECTED_RESULTS_FOR_STAR_SELECT_QUERY);
});
}
}
@@ -1543,19 +1535,19 @@ public void testExecuteUpdate() {
sqlClient.executeUpdate(
"INSERT INTO INTTABLE (keyName, value) VALUES "
+ "('KEYNAME1', 1001), ('KEYNAME2', 1002), ('KEYNAME3', 1003)");
- MatcherAssert.assertThat(insertedCount, is(3L));
+ assertThat(insertedCount).isEqualTo(3L);
},
() -> {
long updatedCount =
sqlClient.executeUpdate(
"UPDATE INTTABLE SET keyName = 'KEYNAME1' "
+ "WHERE keyName = 'KEYNAME2' OR keyName = 'KEYNAME3'");
- MatcherAssert.assertThat(updatedCount, is(2L));
+ assertThat(updatedCount).isEqualTo(2L);
},
() -> {
long deletedCount =
sqlClient.executeUpdate("DELETE FROM INTTABLE WHERE keyName = 'KEYNAME1'");
- MatcherAssert.assertThat(deletedCount, is(3L));
+ assertThat(deletedCount).isEqualTo(3L);
});
}
@@ -1566,10 +1558,10 @@ public void testQueryWithNoResultsShouldNotHang() throws Exception {
final FlightStream stream =
sqlClient.getStream(preparedStatement.execute().getEndpoints().get(0).getTicket())) {
assertAll(
- () -> MatcherAssert.assertThat(stream.getSchema(), is(SCHEMA_INT_TABLE)),
+ () -> assertThat(stream.getSchema()).isEqualTo(SCHEMA_INT_TABLE),
() -> {
final List> result = getResults(stream);
- MatcherAssert.assertThat(result, is(emptyList()));
+ assertThat(result).isEqualTo(emptyList());
});
}
}
diff --git a/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSqlStateless.java b/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSqlStateless.java
index 36d621ad64..ee1507b6af 100644
--- a/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSqlStateless.java
+++ b/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSqlStateless.java
@@ -18,7 +18,7 @@
import static org.apache.arrow.flight.sql.util.FlightStreamUtils.getResults;
import static org.apache.arrow.util.AutoCloseables.close;
-import static org.hamcrest.CoreMatchers.is;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import org.apache.arrow.flight.FlightClient;
@@ -34,7 +34,6 @@
import org.apache.arrow.vector.IntVector;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.types.pojo.Schema;
-import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -89,10 +88,10 @@ public void testSimplePreparedStatementResultsWithParameterBinding() throws Exce
for (FlightEndpoint endpoint : flightInfo.getEndpoints()) {
try (FlightStream stream = sqlClient.getStream(endpoint.getTicket())) {
assertAll(
- () -> MatcherAssert.assertThat(stream.getSchema(), is(SCHEMA_INT_TABLE)),
+ () -> assertThat(stream.getSchema()).isEqualTo(SCHEMA_INT_TABLE),
() ->
- MatcherAssert.assertThat(
- getResults(stream), is(EXPECTED_RESULTS_FOR_PARAMETER_BINDING)));
+ assertThat(getResults(stream))
+ .isEqualTo(EXPECTED_RESULTS_FOR_PARAMETER_BINDING));
}
}
}
diff --git a/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSqlStreams.java b/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSqlStreams.java
index 71c0dc88e4..3f527f961e 100644
--- a/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSqlStreams.java
+++ b/flight/flight-sql/src/test/java/org/apache/arrow/flight/sql/test/TestFlightSqlStreams.java
@@ -22,7 +22,7 @@
import static org.apache.arrow.flight.sql.util.FlightStreamUtils.getResults;
import static org.apache.arrow.util.AutoCloseables.close;
import static org.apache.arrow.vector.types.Types.MinorType.INT;
-import static org.hamcrest.CoreMatchers.is;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import com.google.common.collect.ImmutableList;
@@ -53,7 +53,6 @@
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.Schema;
import org.apache.arrow.vector.util.Text;
-import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -245,15 +244,15 @@ public void testGetTablesResultNoSchema() throws Exception {
sqlClient.getTables(null, null, null, null, false).getEndpoints().get(0).getTicket())) {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA_NO_SCHEMA)),
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_TABLES_SCHEMA_NO_SCHEMA),
() -> {
final List> results = getResults(stream);
final List> expectedResults =
ImmutableList.of(
// catalog_name | schema_name | table_name | table_type | table_schema
asList(null, null, "test_table", "TABLE"));
- MatcherAssert.assertThat(results, is(expectedResults));
+ assertThat(results).isEqualTo(expectedResults);
});
}
}
@@ -264,15 +263,15 @@ public void testGetTableTypesResult() throws Exception {
sqlClient.getStream(sqlClient.getTableTypes().getEndpoints().get(0).getTicket())) {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_TABLE_TYPES_SCHEMA)),
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_TABLE_TYPES_SCHEMA),
() -> {
final List> tableTypes = getResults(stream);
final List> expectedTableTypes =
ImmutableList.of(
// table_type
singletonList("TABLE"));
- MatcherAssert.assertThat(tableTypes, is(expectedTableTypes));
+ assertThat(tableTypes).isEqualTo(expectedTableTypes);
});
}
}
@@ -283,9 +282,9 @@ public void testGetSqlInfoResults() throws Exception {
try (final FlightStream stream = sqlClient.getStream(info.getEndpoints().get(0).getTicket())) {
assertAll(
() ->
- MatcherAssert.assertThat(
- stream.getSchema(), is(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA)),
- () -> MatcherAssert.assertThat(getResults(stream), is(emptyList())));
+ assertThat(stream.getSchema())
+ .isEqualTo(FlightSqlProducer.Schemas.GET_SQL_INFO_SCHEMA),
+ () -> assertThat(getResults(stream)).isEqualTo(emptyList()));
}
}
@@ -303,7 +302,7 @@ public void testGetTypeInfo() throws Exception {
"Integer", "4", "400", null, null, "3", "true", null, "true", null, "true",
"Integer", null, null, "4", null, "10", null));
- MatcherAssert.assertThat(results, is(matchers));
+ assertThat(results).isEqualTo(matchers);
}
}
@@ -317,10 +316,8 @@ public void testExecuteQuery() throws Exception {
.get(0)
.getTicket())) {
assertAll(
- () ->
- MatcherAssert.assertThat(stream.getSchema(), is(FlightSqlTestProducer.FIXED_SCHEMA)),
- () ->
- MatcherAssert.assertThat(getResults(stream), is(singletonList(singletonList("1")))));
+ () -> assertThat(stream.getSchema()).isEqualTo(FlightSqlTestProducer.FIXED_SCHEMA),
+ () -> assertThat(getResults(stream)).isEqualTo(singletonList(singletonList("1"))));
}
}
}
From fad4e142f398492533bb1dea32accfcad0a33be8 Mon Sep 17 00:00:00 2001
From: David Li
Date: Mon, 2 Jun 2025 11:42:45 +0900
Subject: [PATCH 044/271] MINOR: Add missing permission to milestone assignment
bot (#673)
## What's Changed
This step needs permissions to write to issues so we can set the
milestone.
---
.github/workflows/dev_pr.yml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/.github/workflows/dev_pr.yml b/.github/workflows/dev_pr.yml
index 34b3363c50..7352137b09 100644
--- a/.github/workflows/dev_pr.yml
+++ b/.github/workflows/dev_pr.yml
@@ -80,5 +80,9 @@ jobs:
if: '! github.event.pull_request.draft'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ permissions:
+ contents: read
+ issues: write
+ pull-requests: write
run: |
./.github/workflows/dev_pr_milestone.sh "${GITHUB_REPOSITORY}" ${{ github.event.number }}
From 17958324c65e0c2109733ed4022ccd25b23e1ff2 Mon Sep 17 00:00:00 2001
From: rtadepalli <105760760+rtadepalli@users.noreply.github.com>
Date: Wed, 4 Jun 2025 08:15:34 -0400
Subject: [PATCH 045/271] GH-774: Consoliate
`BitVectorHelper.getValidityBufferSize` and
`BaseValueVector.getValidityBufferSizeFromCount` (#775)
## What's Changed
Just removing some duplicate functions in anticipation of cleaning out
some transferPair duplication across complex vectors.
Closes #774
---------
Co-authored-by: David Li
---
.../arrow/vector/BaseFixedWidthVector.java | 10 ++++----
.../vector/BaseLargeVariableWidthVector.java | 6 ++---
.../apache/arrow/vector/BaseValueVector.java | 9 +++++++-
.../arrow/vector/BaseVariableWidthVector.java | 6 ++---
.../vector/BaseVariableWidthViewVector.java | 6 ++---
.../org/apache/arrow/vector/BitVector.java | 6 ++---
.../apache/arrow/vector/BitVectorHelper.java | 12 +++++-----
.../vector/complex/FixedSizeListVector.java | 16 +++++++------
.../arrow/vector/complex/LargeListVector.java | 18 ++++++++-------
.../vector/complex/LargeListViewVector.java | 20 ++++++++--------
.../arrow/vector/complex/ListVector.java | 20 ++++++++--------
.../arrow/vector/complex/ListViewVector.java | 20 ++++++++--------
.../arrow/vector/complex/MapVector.java | 2 +-
.../arrow/vector/complex/StructVector.java | 23 ++++++++++---------
.../arrow/vector/ipc/JsonFileReader.java | 3 ++-
.../arrow/vector/TestLargeListVector.java | 3 ++-
.../arrow/vector/TestLargeListViewVector.java | 3 ++-
.../apache/arrow/vector/TestListVector.java | 3 ++-
.../arrow/vector/TestListViewVector.java | 3 ++-
.../apache/arrow/vector/TestValueVector.java | 13 ++++++-----
.../vector/TestVariableWidthViewVector.java | 3 ++-
.../arrow/vector/TestVectorUnloadLoad.java | 3 ++-
22 files changed, 117 insertions(+), 91 deletions(-)
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
index 4be55396b7..d126266cf5 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
@@ -359,7 +359,7 @@ public int getBufferSizeFor(final int count) {
if (count == 0) {
return 0;
}
- return (count * typeWidth) + getValidityBufferSizeFromCount(count);
+ return (count * typeWidth) + BitVectorHelper.getValidityBufferSizeFromCount(count);
}
/**
@@ -372,7 +372,7 @@ public int getBufferSize() {
if (valueCount == 0) {
return 0;
}
- return (valueCount * typeWidth) + getValidityBufferSizeFromCount(valueCount);
+ return (valueCount * typeWidth) + BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
}
/**
@@ -536,10 +536,10 @@ private void setReaderAndWriterIndex() {
validityBuffer.writerIndex(0);
valueBuffer.writerIndex(0);
} else {
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
if (typeWidth == 0) {
/* specialized handling for BitVector */
- valueBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ valueBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
} else {
valueBuffer.writerIndex((long) valueCount * typeWidth);
}
@@ -664,7 +664,7 @@ private void splitAndTransferValidityBuffer(
int startIndex, int length, BaseFixedWidthVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
index 7e0d0affc6..4245e0053b 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
@@ -379,7 +379,7 @@ private void setReaderAndWriterIndex() {
valueBuffer.writerIndex(0);
} else {
final long lastDataOffset = getStartOffset(valueCount);
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
offsetBuffer.writerIndex((long) (valueCount + 1) * OFFSET_WIDTH);
valueBuffer.writerIndex(lastDataOffset);
}
@@ -633,7 +633,7 @@ public int getBufferSizeFor(final int valueCount) {
return 0;
}
- final long validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final long validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
final long offsetBufferSize = (long) (valueCount + 1) * OFFSET_WIDTH;
/* get the end offset for this valueCount */
final long dataBufferSize = getStartOffset(valueCount);
@@ -816,7 +816,7 @@ private void splitAndTransferValidityBuffer(
int startIndex, int length, BaseLargeVariableWidthVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseValueVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseValueVector.java
index 9befcb890f..7bff431e40 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseValueVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseValueVector.java
@@ -110,7 +110,14 @@ protected ArrowBuf releaseBuffer(ArrowBuf buffer) {
return buffer;
}
- /* number of bytes for the validity buffer for the given valueCount */
+ /**
+ * Compute the size of validity buffer required to manage a given number of elements in a vector.
+ *
+ * @param valueCount number of elements in the vector
+ * @return buffer size
+ * @deprecated -- use {@link BitVectorHelper#getValidityBufferSizeFromCount} instead.
+ */
+ @Deprecated(forRemoval = true, since = "18.4.0")
protected static int getValidityBufferSizeFromCount(final int valueCount) {
return DataSizeRoundingUtil.divideBy8Ceil(valueCount);
}
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
index 1609e64ca5..4f681311ed 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
@@ -395,7 +395,7 @@ private void setReaderAndWriterIndex() {
valueBuffer.writerIndex(0);
} else {
final int lastDataOffset = getStartOffset(valueCount);
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
offsetBuffer.writerIndex((long) (valueCount + 1) * OFFSET_WIDTH);
valueBuffer.writerIndex(lastDataOffset);
}
@@ -673,7 +673,7 @@ public int getBufferSizeFor(final int valueCount) {
return 0;
}
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
final int offsetBufferSize = (valueCount + 1) * OFFSET_WIDTH;
/* get the end offset for this valueCount */
final int dataBufferSize = offsetBuffer.getInt((long) valueCount * OFFSET_WIDTH);
@@ -867,7 +867,7 @@ private void splitAndTransferValidityBuffer(
final int firstByteSource = BitVectorHelper.byteIndex(startIndex);
final int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- final int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ final int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
final int offset = startIndex % 8;
if (offset == 0) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
index beda91dc3f..5e25ffa568 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
@@ -400,7 +400,7 @@ private void setReaderAndWriterIndex() {
validityBuffer.writerIndex(0);
viewBuffer.writerIndex(0);
} else {
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
viewBuffer.writerIndex(valueCount * ELEMENT_SIZE);
}
}
@@ -683,7 +683,7 @@ public int getBufferSizeFor(final int valueCount) {
return 0;
}
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
final int viewBufferSize = valueCount * ELEMENT_SIZE;
final int dataBufferSize = getDataBufferSize();
return validityBufferSize + viewBufferSize + dataBufferSize;
@@ -872,7 +872,7 @@ private void splitAndTransferValidityBuffer(
final int firstByteSource = BitVectorHelper.byteIndex(startIndex);
final int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- final int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ final int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
final int offset = startIndex % 8;
if (offset == 0) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/BitVector.java b/vector/src/main/java/org/apache/arrow/vector/BitVector.java
index f8e3342625..ecee02f665 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BitVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BitVector.java
@@ -98,7 +98,7 @@ public MinorType getMinorType() {
*/
@Override
public void setInitialCapacity(int valueCount) {
- final int size = getValidityBufferSizeFromCount(valueCount);
+ final int size = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
if (size * 2L > MAX_ALLOCATION_SIZE) {
throw new OversizedAllocationException("Requested amount of memory is more than max allowed");
}
@@ -121,7 +121,7 @@ public int getBufferSizeFor(final int count) {
if (count == 0) {
return 0;
}
- return 2 * getValidityBufferSizeFromCount(count);
+ return 2 * BitVectorHelper.getValidityBufferSizeFromCount(count);
}
/**
@@ -165,7 +165,7 @@ private ArrowBuf splitAndTransferBuffer(
int startIndex, int length, ArrowBuf sourceBuffer, ArrowBuf destBuffer) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/BitVectorHelper.java b/vector/src/main/java/org/apache/arrow/vector/BitVectorHelper.java
index 0ac56691a6..bc2c3da98f 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BitVectorHelper.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BitVectorHelper.java
@@ -135,11 +135,11 @@ public static void setValidityBit(ArrowBuf validityBuffer, int index, int value)
public static ArrowBuf setValidityBit(
ArrowBuf validityBuffer, BufferAllocator allocator, int valueCount, int index, int value) {
if (validityBuffer == null) {
- validityBuffer = allocator.buffer(getValidityBufferSize(valueCount));
+ validityBuffer = allocator.buffer(getValidityBufferSizeFromCount(valueCount));
}
setValidityBit(validityBuffer, index, value);
if (index == (valueCount - 1)) {
- validityBuffer.writerIndex(getValidityBufferSize(valueCount));
+ validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
}
return validityBuffer;
@@ -165,7 +165,7 @@ public static int get(final ArrowBuf buffer, int index) {
* @param valueCount number of elements in the vector
* @return buffer size
*/
- public static int getValidityBufferSize(int valueCount) {
+ public static int getValidityBufferSizeFromCount(int valueCount) {
return DataSizeRoundingUtil.divideBy8Ceil(valueCount);
}
@@ -182,7 +182,7 @@ public static int getNullCount(final ArrowBuf validityBuffer, final int valueCou
return 0;
}
int count = 0;
- final int sizeInBytes = getValidityBufferSize(valueCount);
+ final int sizeInBytes = getValidityBufferSizeFromCount(valueCount);
// If value count is not a multiple of 8, then calculate number of used bits in the last byte
final int remainder = valueCount % 8;
final int fullBytesCount = remainder == 0 ? sizeInBytes : sizeInBytes - 1;
@@ -233,7 +233,7 @@ public static boolean checkAllBitsEqualTo(
if (valueCount == 0) {
return true;
}
- final int sizeInBytes = getValidityBufferSize(valueCount);
+ final int sizeInBytes = getValidityBufferSizeFromCount(valueCount);
// boundary check
validityBuffer.checkBytes(0, sizeInBytes);
@@ -325,7 +325,7 @@ public static ArrowBuf loadValidityBuffer(
sourceValidityBuffer == null || sourceValidityBuffer.capacity() == 0;
if (isValidityBufferNull
&& (fieldNode.getNullCount() == 0 || fieldNode.getNullCount() == valueCount)) {
- newBuffer = allocator.buffer(getValidityBufferSize(valueCount));
+ newBuffer = allocator.buffer(getValidityBufferSizeFromCount(valueCount));
newBuffer.setZero(0, newBuffer.capacity());
if (fieldNode.getNullCount() != 0) {
/* all NULLs */
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java
index c762eb5172..36d9ff40ed 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java
@@ -110,7 +110,8 @@ public FixedSizeListVector(
this.listSize = ((ArrowType.FixedSizeList) field.getFieldType().getType()).getListSize();
Preconditions.checkArgument(listSize >= 0, "list size must be non-negative");
this.valueCount = 0;
- this.validityAllocationSizeInBytes = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
+ this.validityAllocationSizeInBytes =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
}
@Override
@@ -189,7 +190,7 @@ public List getFieldBuffers() {
private void setReaderAndWriterIndex() {
validityBuffer.readerIndex(0);
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
}
/**
@@ -268,7 +269,8 @@ private void reallocValidityBuffer() {
if (validityAllocationSizeInBytes > 0) {
newAllocationSize = validityAllocationSizeInBytes;
} else {
- newAllocationSize = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
+ newAllocationSize =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
}
}
@@ -311,7 +313,7 @@ public UnionFixedSizeListWriter getWriter() {
@Override
public void setInitialCapacity(int numRecords) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
vector.setInitialCapacity(numRecords * listSize);
}
@@ -328,7 +330,7 @@ public int getBufferSize() {
if (getValueCount() == 0) {
return 0;
}
- return getValidityBufferSizeFromCount(valueCount) + vector.getBufferSize();
+ return BitVectorHelper.getValidityBufferSizeFromCount(valueCount) + vector.getBufferSize();
}
@Override
@@ -336,7 +338,7 @@ public int getBufferSizeFor(int valueCount) {
if (valueCount == 0) {
return 0;
}
- return getValidityBufferSizeFromCount(valueCount)
+ return BitVectorHelper.getValidityBufferSizeFromCount(valueCount)
+ vector.getBufferSizeFor(valueCount * listSize);
}
@@ -654,7 +656,7 @@ private void splitAndTransferValidityBuffer(
int startIndex, int length, FixedSizeListVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
index ed075352c9..71633441cb 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
@@ -131,7 +131,8 @@ public LargeListVector(Field field, BufferAllocator allocator, CallBack callBack
this.field = field;
this.validityBuffer = allocator.getEmpty();
this.callBack = callBack;
- this.validityAllocationSizeInBytes = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
+ this.validityAllocationSizeInBytes =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
this.lastSet = -1;
this.offsetBuffer = allocator.getEmpty();
this.vector = vector == null ? DEFAULT_DATA_VECTOR : vector;
@@ -156,7 +157,7 @@ public void initializeChildrenFromFields(List children) {
@Override
public void setInitialCapacity(int numRecords) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
offsetAllocationSizeInBytes = (long) (numRecords + 1) * OFFSET_WIDTH;
if (vector instanceof BaseFixedWidthVector || vector instanceof BaseVariableWidthVector) {
vector.setInitialCapacity(numRecords * RepeatedValueVector.DEFAULT_REPEAT_PER_RECORD);
@@ -184,7 +185,7 @@ public void setInitialCapacity(int numRecords) {
*/
@Override
public void setInitialCapacity(int numRecords, double density) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
if ((numRecords * density) >= Integer.MAX_VALUE) {
throw new OversizedAllocationException("Requested amount of memory is more than max allowed");
}
@@ -311,7 +312,7 @@ private void setReaderAndWriterIndex() {
validityBuffer.writerIndex(0);
offsetBuffer.writerIndex(0);
} else {
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
offsetBuffer.writerIndex((valueCount + 1) * OFFSET_WIDTH);
}
}
@@ -442,7 +443,8 @@ private void reallocValidityBuffer() {
if (validityAllocationSizeInBytes > 0) {
newAllocationSize = validityAllocationSizeInBytes;
} else {
- newAllocationSize = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
+ newAllocationSize =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
}
}
newAllocationSize = CommonUtil.nextPowerOfTwo(newAllocationSize);
@@ -699,7 +701,7 @@ private void splitAndTransferValidityBuffer(
int startIndex, int length, LargeListVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
@@ -821,7 +823,7 @@ public int getBufferSize() {
return 0;
}
final int offsetBufferSize = (valueCount + 1) * OFFSET_WIDTH;
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
return offsetBufferSize + validityBufferSize + vector.getBufferSize();
}
@@ -830,7 +832,7 @@ public int getBufferSizeFor(int valueCount) {
if (valueCount == 0) {
return 0;
}
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
long innerVectorValueCount = offsetBuffer.getLong((long) valueCount * OFFSET_WIDTH);
return ((valueCount + 1) * OFFSET_WIDTH)
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java
index 84c6f03edb..1b7e6b2280 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java
@@ -113,7 +113,8 @@ public LargeListViewVector(Field field, BufferAllocator allocator, CallBack call
this.validityBuffer = allocator.getEmpty();
this.field = field;
this.callBack = callBack;
- this.validityAllocationSizeInBytes = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
+ this.validityAllocationSizeInBytes =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
}
@Override
@@ -134,7 +135,7 @@ public void initializeChildrenFromFields(List children) {
@Override
public void setInitialCapacity(int numRecords) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialCapacity(numRecords);
}
@@ -157,7 +158,7 @@ public void setInitialCapacity(int numRecords) {
*/
@Override
public void setInitialCapacity(int numRecords, double density) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialCapacity(numRecords, density);
}
@@ -176,7 +177,7 @@ public void setInitialCapacity(int numRecords, double density) {
*/
@Override
public void setInitialTotalCapacity(int numRecords, int totalNumberOfElements) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialTotalCapacity(numRecords, totalNumberOfElements);
}
@@ -226,7 +227,7 @@ private void setReaderAndWriterIndex() {
offsetBuffer.writerIndex(0);
sizeBuffer.writerIndex(0);
} else {
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
offsetBuffer.writerIndex((long) valueCount * OFFSET_WIDTH);
sizeBuffer.writerIndex((long) valueCount * SIZE_WIDTH);
}
@@ -323,7 +324,8 @@ private long getNewAllocationSize(int currentBufferCapacity) {
if (validityAllocationSizeInBytes > 0) {
newAllocationSize = validityAllocationSizeInBytes;
} else {
- newAllocationSize = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
+ newAllocationSize =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
}
}
newAllocationSize = CommonUtil.nextPowerOfTwo(newAllocationSize);
@@ -536,7 +538,7 @@ private void splitAndTransferValidityBuffer(
int startIndex, int length, LargeListViewVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
@@ -629,7 +631,7 @@ public int getBufferSize() {
}
final int offsetBufferSize = valueCount * OFFSET_WIDTH;
final int sizeBufferSize = valueCount * SIZE_WIDTH;
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
return offsetBufferSize + sizeBufferSize + validityBufferSize + vector.getBufferSize();
}
@@ -644,7 +646,7 @@ public int getBufferSizeFor(int valueCount) {
if (valueCount == 0) {
return 0;
}
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
return super.getBufferSizeFor(valueCount) + validityBufferSize;
}
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java
index 3daeb6d77b..a8e8dcc436 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java
@@ -108,7 +108,8 @@ public ListVector(Field field, BufferAllocator allocator, CallBack callBack) {
this.validityBuffer = allocator.getEmpty();
this.field = field;
this.callBack = callBack;
- this.validityAllocationSizeInBytes = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
+ this.validityAllocationSizeInBytes =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
this.lastSet = -1;
}
@@ -130,7 +131,7 @@ public void initializeChildrenFromFields(List children) {
@Override
public void setInitialCapacity(int numRecords) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialCapacity(numRecords);
}
@@ -153,7 +154,7 @@ public void setInitialCapacity(int numRecords) {
*/
@Override
public void setInitialCapacity(int numRecords, double density) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialCapacity(numRecords, density);
}
@@ -172,7 +173,7 @@ public void setInitialCapacity(int numRecords, double density) {
*/
@Override
public void setInitialTotalCapacity(int numRecords, int totalNumberOfElements) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialTotalCapacity(numRecords, totalNumberOfElements);
}
@@ -269,7 +270,7 @@ private void setReaderAndWriterIndex() {
validityBuffer.writerIndex(0);
offsetBuffer.writerIndex(0);
} else {
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
offsetBuffer.writerIndex((valueCount + 1) * OFFSET_WIDTH);
}
}
@@ -366,7 +367,8 @@ private long getNewAllocationSize(int currentBufferCapacity) {
if (validityAllocationSizeInBytes > 0) {
newAllocationSize = validityAllocationSizeInBytes;
} else {
- newAllocationSize = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
+ newAllocationSize =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
}
}
newAllocationSize = CommonUtil.nextPowerOfTwo(newAllocationSize);
@@ -579,7 +581,7 @@ public void splitAndTransfer(int startIndex, int length) {
private void splitAndTransferValidityBuffer(int startIndex, int length, ListVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
@@ -678,7 +680,7 @@ public int getBufferSize() {
return 0;
}
final int offsetBufferSize = (valueCount + 1) * OFFSET_WIDTH;
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
return offsetBufferSize + validityBufferSize + vector.getBufferSize();
}
@@ -687,7 +689,7 @@ public int getBufferSizeFor(int valueCount) {
if (valueCount == 0) {
return 0;
}
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
return super.getBufferSizeFor(valueCount) + validityBufferSize;
}
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java
index 9b4e6b4c0c..ada25bbaf5 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java
@@ -112,7 +112,8 @@ public ListViewVector(Field field, BufferAllocator allocator, CallBack callBack)
this.validityBuffer = allocator.getEmpty();
this.field = field;
this.callBack = callBack;
- this.validityAllocationSizeInBytes = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
+ this.validityAllocationSizeInBytes =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION);
}
@Override
@@ -133,7 +134,7 @@ public void initializeChildrenFromFields(List children) {
@Override
public void setInitialCapacity(int numRecords) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialCapacity(numRecords);
}
@@ -156,7 +157,7 @@ public void setInitialCapacity(int numRecords) {
*/
@Override
public void setInitialCapacity(int numRecords, double density) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialCapacity(numRecords, density);
}
@@ -175,7 +176,7 @@ public void setInitialCapacity(int numRecords, double density) {
*/
@Override
public void setInitialTotalCapacity(int numRecords, int totalNumberOfElements) {
- validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
+ validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSizeFromCount(numRecords);
super.setInitialTotalCapacity(numRecords, totalNumberOfElements);
}
@@ -225,7 +226,7 @@ private void setReaderAndWriterIndex() {
offsetBuffer.writerIndex(0);
sizeBuffer.writerIndex(0);
} else {
- validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
+ validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSizeFromCount(valueCount));
offsetBuffer.writerIndex(valueCount * OFFSET_WIDTH);
sizeBuffer.writerIndex(valueCount * SIZE_WIDTH);
}
@@ -322,7 +323,8 @@ private long getNewAllocationSize(int currentBufferCapacity) {
if (validityAllocationSizeInBytes > 0) {
newAllocationSize = validityAllocationSizeInBytes;
} else {
- newAllocationSize = getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
+ newAllocationSize =
+ BitVectorHelper.getValidityBufferSizeFromCount(INITIAL_VALUE_ALLOCATION) * 2L;
}
}
newAllocationSize = CommonUtil.nextPowerOfTwo(newAllocationSize);
@@ -542,7 +544,7 @@ public void splitAndTransfer(int startIndex, int length) {
private void splitAndTransferValidityBuffer(int startIndex, int length, ListViewVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
@@ -634,7 +636,7 @@ public int getBufferSize() {
}
final int offsetBufferSize = valueCount * OFFSET_WIDTH;
final int sizeBufferSize = valueCount * SIZE_WIDTH;
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
return offsetBufferSize + sizeBufferSize + validityBufferSize + vector.getBufferSize();
}
@@ -649,7 +651,7 @@ public int getBufferSizeFor(int valueCount) {
if (valueCount == 0) {
return 0;
}
- final int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
+ final int validityBufferSize = BitVectorHelper.getValidityBufferSizeFromCount(valueCount);
return super.getBufferSizeFor(valueCount) + validityBufferSize;
}
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java
index 23cda8401b..5eb857ab94 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java
@@ -238,7 +238,7 @@ public void splitAndTransfer(int startIndex, int length) {
private void splitAndTransferValidityBuffer(int startIndex, int length, MapVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/StructVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/StructVector.java
index ca5f572034..5e5bb7fc21 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/StructVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/StructVector.java
@@ -18,6 +18,7 @@
import static org.apache.arrow.memory.util.LargeMemoryUtil.checkedCastToInt;
import static org.apache.arrow.util.Preconditions.checkNotNull;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import java.util.ArrayList;
import java.util.Arrays;
@@ -89,7 +90,7 @@ public StructVector(
super(name, checkNotNull(allocator), fieldType, callBack);
this.validityBuffer = allocator.getEmpty();
this.validityAllocationSizeInBytes =
- BitVectorHelper.getValidityBufferSize(BaseValueVector.INITIAL_VALUE_ALLOCATION);
+ getValidityBufferSizeFromCount(BaseValueVector.INITIAL_VALUE_ALLOCATION);
}
/**
@@ -118,7 +119,7 @@ public StructVector(
allowConflictPolicyChanges);
this.validityBuffer = allocator.getEmpty();
this.validityAllocationSizeInBytes =
- BitVectorHelper.getValidityBufferSize(BaseValueVector.INITIAL_VALUE_ALLOCATION);
+ getValidityBufferSizeFromCount(BaseValueVector.INITIAL_VALUE_ALLOCATION);
}
/**
@@ -132,7 +133,7 @@ public StructVector(Field field, BufferAllocator allocator, CallBack callBack) {
super(field, checkNotNull(allocator), callBack);
this.validityBuffer = allocator.getEmpty();
this.validityAllocationSizeInBytes =
- BitVectorHelper.getValidityBufferSize(BaseValueVector.INITIAL_VALUE_ALLOCATION);
+ getValidityBufferSizeFromCount(BaseValueVector.INITIAL_VALUE_ALLOCATION);
}
/**
@@ -153,7 +154,7 @@ public StructVector(
super(field, checkNotNull(allocator), callBack, conflictPolicy, allowConflictPolicyChanges);
this.validityBuffer = allocator.getEmpty();
this.validityAllocationSizeInBytes =
- BitVectorHelper.getValidityBufferSize(BaseValueVector.INITIAL_VALUE_ALLOCATION);
+ getValidityBufferSizeFromCount(BaseValueVector.INITIAL_VALUE_ALLOCATION);
}
@Override
@@ -182,7 +183,7 @@ public List getFieldBuffers() {
private void setReaderAndWriterIndex() {
validityBuffer.readerIndex(0);
- validityBuffer.writerIndex(BitVectorHelper.getValidityBufferSize(valueCount));
+ validityBuffer.writerIndex(getValidityBufferSizeFromCount(valueCount));
}
/**
@@ -318,7 +319,7 @@ public void splitAndTransfer(int startIndex, int length) {
private void splitAndTransferValidityBuffer(int startIndex, int length, StructVector target) {
int firstByteSource = BitVectorHelper.byteIndex(startIndex);
int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSize(length);
+ int byteSizeTarget = getValidityBufferSizeFromCount(length);
int offset = startIndex % 8;
if (length > 0) {
@@ -464,7 +465,7 @@ public int getBufferSize() {
if (valueCount == 0) {
return 0;
}
- return super.getBufferSize() + BitVectorHelper.getValidityBufferSize(valueCount);
+ return super.getBufferSize() + getValidityBufferSizeFromCount(valueCount);
}
/**
@@ -478,18 +479,18 @@ public int getBufferSizeFor(final int valueCount) {
if (valueCount == 0) {
return 0;
}
- return super.getBufferSizeFor(valueCount) + BitVectorHelper.getValidityBufferSize(valueCount);
+ return super.getBufferSizeFor(valueCount) + getValidityBufferSizeFromCount(valueCount);
}
@Override
public void setInitialCapacity(int numRecords) {
- validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSize(numRecords);
+ validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
super.setInitialCapacity(numRecords);
}
@Override
public void setInitialCapacity(int numRecords, double density) {
- validityAllocationSizeInBytes = BitVectorHelper.getValidityBufferSize(numRecords);
+ validityAllocationSizeInBytes = getValidityBufferSizeFromCount(numRecords);
super.setInitialCapacity(numRecords, density);
}
@@ -547,7 +548,7 @@ private long getNewAllocationSize(int currentBufferCapacity) {
newAllocationSize = validityAllocationSizeInBytes;
} else {
newAllocationSize =
- BitVectorHelper.getValidityBufferSize(BaseValueVector.INITIAL_VALUE_ALLOCATION) * 2L;
+ getValidityBufferSizeFromCount(BaseValueVector.INITIAL_VALUE_ALLOCATION) * 2L;
}
}
newAllocationSize = CommonUtil.nextPowerOfTwo(newAllocationSize);
diff --git a/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileReader.java b/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileReader.java
index fe0803d298..e4bab7eb80 100644
--- a/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileReader.java
+++ b/vector/src/main/java/org/apache/arrow/vector/ipc/JsonFileReader.java
@@ -20,6 +20,7 @@
import static com.fasterxml.jackson.core.JsonToken.END_OBJECT;
import static com.fasterxml.jackson.core.JsonToken.START_ARRAY;
import static com.fasterxml.jackson.core.JsonToken.START_OBJECT;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import static org.apache.arrow.vector.BufferLayout.BufferType.DATA;
import static org.apache.arrow.vector.BufferLayout.BufferType.OFFSET;
import static org.apache.arrow.vector.BufferLayout.BufferType.SIZE;
@@ -381,7 +382,7 @@ private class BufferHelper {
new BufferReader() {
@Override
protected ArrowBuf read(BufferAllocator allocator, int count) throws IOException {
- final int bufferSize = BitVectorHelper.getValidityBufferSize(count);
+ final int bufferSize = getValidityBufferSizeFromCount(count);
ArrowBuf buf = allocator.buffer(bufferSize);
// C++ integration test fails without this.
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java b/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
index 101d942d2a..d5cbf925b2 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.vector;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -943,7 +944,7 @@ public void testGetBufferSizeFor() {
int[] indices = new int[] {0, 2, 4, 6, 10, 14};
for (int valueCount = 1; valueCount <= 5; valueCount++) {
- int validityBufferSize = BitVectorHelper.getValidityBufferSize(valueCount);
+ int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
int offsetBufferSize = (valueCount + 1) * LargeListVector.OFFSET_WIDTH;
int expectedSize =
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestLargeListViewVector.java b/vector/src/test/java/org/apache/arrow/vector/TestLargeListViewVector.java
index 26e7bb4a0d..256aa99687 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestLargeListViewVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestLargeListViewVector.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.vector;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
@@ -1062,7 +1063,7 @@ public void testGetBufferSizeFor() {
int[] indices = new int[] {0, 2, 4, 6, 10, 14};
for (int valueCount = 1; valueCount <= 5; valueCount++) {
- int validityBufferSize = BitVectorHelper.getValidityBufferSize(valueCount);
+ int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
int offsetBufferSize = valueCount * BaseLargeRepeatedValueViewVector.OFFSET_WIDTH;
int sizeBufferSize = valueCount * BaseLargeRepeatedValueViewVector.SIZE_WIDTH;
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestListVector.java b/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
index 1d6fa39f9e..5b2043a014 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.vector;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -1123,7 +1124,7 @@ public void testGetBufferSizeFor() {
int[] indices = new int[] {0, 2, 4, 6, 10, 14};
for (int valueCount = 1; valueCount <= 5; valueCount++) {
- int validityBufferSize = BitVectorHelper.getValidityBufferSize(valueCount);
+ int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
int offsetBufferSize = (valueCount + 1) * BaseRepeatedValueVector.OFFSET_WIDTH;
int expectedSize =
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestListViewVector.java b/vector/src/test/java/org/apache/arrow/vector/TestListViewVector.java
index 639585fc48..2f282e1988 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestListViewVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestListViewVector.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.vector;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -1075,7 +1076,7 @@ public void testGetBufferSizeFor() {
int[] indices = new int[] {0, 2, 4, 6, 10, 14};
for (int valueCount = 1; valueCount <= 5; valueCount++) {
- int validityBufferSize = BitVectorHelper.getValidityBufferSize(valueCount);
+ int validityBufferSize = getValidityBufferSizeFromCount(valueCount);
int offsetBufferSize = valueCount * BaseRepeatedValueViewVector.OFFSET_WIDTH;
int sizeBufferSize = valueCount * BaseRepeatedValueViewVector.SIZE_WIDTH;
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java b/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
index daec331831..ac82246671 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestValueVector.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.vector;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import static org.apache.arrow.vector.TestUtils.newVarBinaryVector;
import static org.apache.arrow.vector.TestUtils.newVarCharVector;
import static org.apache.arrow.vector.TestUtils.newVector;
@@ -1233,7 +1234,7 @@ public void testSplitAndTransfer3() {
// the size needed for the validity buffer
final long validitySize =
DefaultRoundingPolicy.DEFAULT_ROUNDING_POLICY.getRoundedSize(
- BaseValueVector.getValidityBufferSizeFromCount(2));
+ getValidityBufferSizeFromCount(2));
assertEquals(allocatedMem + validitySize, allocator.getAllocatedMemory());
// The validity and offset buffers are sliced from a same buffer.See
// BaseFixedWidthVector#allocateBytes.
@@ -2464,7 +2465,7 @@ public void testDefaultAllocNewAll() {
assertTrue(intVector.getValueCapacity() >= defaultCapacity);
expectedSize =
(defaultCapacity * IntVector.TYPE_WIDTH)
- + BaseFixedWidthVector.getValidityBufferSizeFromCount(defaultCapacity);
+ + getValidityBufferSizeFromCount(defaultCapacity);
assertTrue(childAllocator.getAllocatedMemory() - beforeSize <= expectedSize * 1.05);
// verify that the wastage is within bounds for BigIntVector.
@@ -2473,7 +2474,7 @@ public void testDefaultAllocNewAll() {
assertTrue(bigIntVector.getValueCapacity() >= defaultCapacity);
expectedSize =
(defaultCapacity * bigIntVector.TYPE_WIDTH)
- + BaseFixedWidthVector.getValidityBufferSizeFromCount(defaultCapacity);
+ + getValidityBufferSizeFromCount(defaultCapacity);
assertTrue(childAllocator.getAllocatedMemory() - beforeSize <= expectedSize * 1.05);
// verify that the wastage is within bounds for DecimalVector.
@@ -2482,7 +2483,7 @@ public void testDefaultAllocNewAll() {
assertTrue(decimalVector.getValueCapacity() >= defaultCapacity);
expectedSize =
(defaultCapacity * decimalVector.TYPE_WIDTH)
- + BaseFixedWidthVector.getValidityBufferSizeFromCount(defaultCapacity);
+ + getValidityBufferSizeFromCount(defaultCapacity);
assertTrue(childAllocator.getAllocatedMemory() - beforeSize <= expectedSize * 1.05);
// verify that the wastage is within bounds for VarCharVector.
@@ -2492,7 +2493,7 @@ public void testDefaultAllocNewAll() {
assertTrue(varCharVector.getValueCapacity() >= defaultCapacity - 1);
expectedSize =
(defaultCapacity * VarCharVector.OFFSET_WIDTH)
- + BaseFixedWidthVector.getValidityBufferSizeFromCount(defaultCapacity)
+ + getValidityBufferSizeFromCount(defaultCapacity)
+ defaultCapacity * 8;
// wastage should be less than 5%.
assertTrue(childAllocator.getAllocatedMemory() - beforeSize <= expectedSize * 1.05);
@@ -2501,7 +2502,7 @@ public void testDefaultAllocNewAll() {
beforeSize = childAllocator.getAllocatedMemory();
bitVector.allocateNew();
assertTrue(bitVector.getValueCapacity() >= defaultCapacity);
- expectedSize = BaseFixedWidthVector.getValidityBufferSizeFromCount(defaultCapacity) * 2;
+ expectedSize = getValidityBufferSizeFromCount(defaultCapacity) * 2;
assertTrue(childAllocator.getAllocatedMemory() - beforeSize <= expectedSize * 1.05);
}
}
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestVariableWidthViewVector.java b/vector/src/test/java/org/apache/arrow/vector/TestVariableWidthViewVector.java
index 7a3a1bae63..f7c66a00be 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestVariableWidthViewVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestVariableWidthViewVector.java
@@ -16,6 +16,7 @@
*/
package org.apache.arrow.vector;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import static org.apache.arrow.vector.TestUtils.newVector;
import static org.apache.arrow.vector.TestUtils.newViewVarBinaryVector;
import static org.apache.arrow.vector.TestUtils.newViewVarCharVector;
@@ -2367,7 +2368,7 @@ private void testSplitAndTransferOnValiditySplitHelper(
// the allocation only consists in the size needed for the validity buffer
final long validitySize =
DefaultRoundingPolicy.DEFAULT_ROUNDING_POLICY.getRoundedSize(
- BaseValueVector.getValidityBufferSizeFromCount(2));
+ getValidityBufferSizeFromCount(2));
// we allocate view and data buffers for the target vector
assertTrue(allocatedMem + validitySize < allocator.getAllocatedMemory());
// The validity is sliced from the same buffer.See BaseFixedWidthViewVector#allocateBytes.
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestVectorUnloadLoad.java b/vector/src/test/java/org/apache/arrow/vector/TestVectorUnloadLoad.java
index 6121fb67fe..782535fccc 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestVectorUnloadLoad.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestVectorUnloadLoad.java
@@ -17,6 +17,7 @@
package org.apache.arrow.vector;
import static java.util.Arrays.asList;
+import static org.apache.arrow.vector.BitVectorHelper.getValidityBufferSizeFromCount;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -215,7 +216,7 @@ public void testLoadValidityBuffer() throws IOException {
int count = 10;
ArrowBuf[] values = new ArrowBuf[4];
for (int i = 0; i < 4; i += 2) {
- ArrowBuf buf1 = allocator.buffer(BitVectorHelper.getValidityBufferSize(count));
+ ArrowBuf buf1 = allocator.buffer(getValidityBufferSizeFromCount(count));
ArrowBuf buf2 = allocator.buffer(count * 4); // integers
buf1.setZero(0, buf1.capacity());
buf2.setZero(0, buf2.capacity());
From acbc138f75711fc94e842f9b306a48a5a233bca8 Mon Sep 17 00:00:00 2001
From: rtadepalli <105760760+rtadepalli@users.noreply.github.com>
Date: Wed, 4 Jun 2025 22:57:25 -0400
Subject: [PATCH 046/271] GH-79: Move `splitAndTransferValidityBuffer` to
`BaseValueVector` (#777)
## What's Changed
Move `splitAndTransferValidityBuffer` up to `BaseValueVector`.
This PR is not touching the implementation of this function in
`StructVector` -- that is not being derived from `BaseValueVector` so
some amount of duplication is probably fine.
Closes #79
---
.../arrow/vector/BaseFixedWidthVector.java | 86 +++----------
.../vector/BaseLargeVariableWidthVector.java | 79 ++----------
.../apache/arrow/vector/BaseValueVector.java | 116 ++++++++++++++++++
.../arrow/vector/BaseVariableWidthVector.java | 79 ++----------
.../vector/BaseVariableWidthViewVector.java | 85 ++-----------
.../BaseLargeRepeatedValueViewVector.java | 1 -
.../complex/BaseRepeatedValueVector.java | 1 -
.../complex/BaseRepeatedValueViewVector.java | 1 -
.../vector/complex/FixedSizeListVector.java | 77 +-----------
.../arrow/vector/complex/LargeListVector.java | 77 +-----------
.../vector/complex/LargeListViewVector.java | 74 +----------
.../arrow/vector/complex/ListVector.java | 73 +----------
.../arrow/vector/complex/ListViewVector.java | 73 +----------
.../arrow/vector/complex/MapVector.java | 65 ----------
14 files changed, 182 insertions(+), 705 deletions(-)
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
index d126266cf5..f6e2a3b225 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java
@@ -49,9 +49,7 @@ public abstract class BaseFixedWidthVector extends BaseValueVector
protected final Field field;
private int allocationMonitor;
- protected ArrowBuf validityBuffer;
protected ArrowBuf valueBuffer;
- protected int valueCount;
/**
* Constructs a new instance.
@@ -87,7 +85,7 @@ public String getName() {
/* TODO:
* Once the entire hierarchy has been refactored, move common functions
- * like getNullCount(), splitAndTransferValidityBuffer to top level
+ * like getNullCount() to top level
* base class BaseValueVector.
*
* Along with this, some class members (validityBuffer) can also be
@@ -342,9 +340,9 @@ private void allocateBytes(int valueCount) {
* slice the source buffer so we have to explicitly allocate the validityBuffer of the target
* vector. This is unlike the databuffer which we can always slice for the target vector.
*/
- private void allocateValidityBuffer(final int validityBufferSize) {
- validityBuffer = allocator.buffer(validityBufferSize);
- validityBuffer.readerIndex(0);
+ @Override
+ protected void allocateValidityBuffer(final long validityBufferSize) {
+ super.allocateValidityBuffer(validityBufferSize);
refreshValueCapacity();
}
@@ -656,72 +654,18 @@ private void splitAndTransferValueBuffer(
target.refreshValueCapacity();
}
- /**
- * Validity buffer has multiple cases of split and transfer depending on the starting position of
- * the source index.
- */
- private void splitAndTransferValidityBuffer(
- int startIndex, int length, BaseFixedWidthVector target) {
- int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- int offset = startIndex % 8;
-
- if (length > 0) {
- if (offset == 0) {
- /* slice */
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- ArrowBuf slicedValidityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer = transferBuffer(slicedValidityBuffer, target.allocator);
- target.refreshValueCapacity();
- } else {
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- this.validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
-
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- this.validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
- }
- }
+ @Override
+ protected void sliceAndTransferValidityBuffer(
+ int startIndex, int length, BaseValueVector target) {
+ final int firstByteSource = BitVectorHelper.byteIndex(startIndex);
+ final int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
+
+ if (target.validityBuffer != null) {
+ target.validityBuffer.getReferenceManager().release();
}
+ ArrowBuf slicedValidityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
+ target.validityBuffer = transferBuffer(slicedValidityBuffer, target.allocator);
+ ((BaseFixedWidthVector) target).refreshValueCapacity();
}
/*----------------------------------------------------------------*
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
index 4245e0053b..6c451f10a7 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseLargeVariableWidthVector.java
@@ -52,10 +52,8 @@ public abstract class BaseLargeVariableWidthVector extends BaseValueVector
/* protected members */
public static final int OFFSET_WIDTH = 8; /* 8 byte unsigned int to track offsets */
protected static final byte[] emptyByteArray = new byte[] {};
- protected ArrowBuf validityBuffer;
protected ArrowBuf valueBuffer;
protected ArrowBuf offsetBuffer;
- protected int valueCount;
protected int lastSet;
protected final Field field;
@@ -501,10 +499,9 @@ private ArrowBuf allocateOffsetBuffer(final long size) {
}
/* allocate validity buffer */
- private void allocateValidityBuffer(final long size) {
- validityBuffer = allocator.buffer(size);
- validityBuffer.readerIndex(0);
- initValidityBuffer();
+ @Override
+ protected void allocateValidityBuffer(final long size) {
+ super.allocateValidityBuffer(size);
}
/**
@@ -809,69 +806,17 @@ private void splitAndTransferOffsetBuffer(
target.valueBuffer = transferBuffer(slicedBuffer, target.allocator);
}
- /*
- * Transfer the validity.
- */
- private void splitAndTransferValidityBuffer(
- int startIndex, int length, BaseLargeVariableWidthVector target) {
- int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- int offset = startIndex % 8;
+ @Override
+ protected void sliceAndTransferValidityBuffer(
+ int startIndex, int length, BaseValueVector target) {
+ final int firstByteSource = BitVectorHelper.byteIndex(startIndex);
+ final int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- if (length > 0) {
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer.getReferenceManager().retain();
- } else {
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- this.validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- this.validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
- }
- }
+ if (target.validityBuffer != null) {
+ target.validityBuffer.getReferenceManager().release();
}
+ target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
+ target.validityBuffer.getReferenceManager().retain();
}
/*----------------------------------------------------------------*
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseValueVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseValueVector.java
index 7bff431e40..37dfa20616 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseValueVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseValueVector.java
@@ -48,6 +48,10 @@ public abstract class BaseValueVector implements ValueVector {
protected volatile FieldReader fieldReader;
+ protected ArrowBuf validityBuffer;
+
+ protected int valueCount;
+
protected BaseValueVector(BufferAllocator allocator) {
this.allocator = Preconditions.checkNotNull(allocator, "allocator cannot be null");
}
@@ -255,4 +259,116 @@ public void copyFrom(int fromIndex, int thisIndex, ValueVector from) {
public void copyFromSafe(int fromIndex, int thisIndex, ValueVector from) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * Transfer the validity buffer from `validityBuffer` to the target vector's `validityBuffer`.
+ * Start at `startIndex` and copy `length` number of elements. If the starting index is 8 byte
+ * aligned, then the buffer is sliced from that index and ownership is transferred. If not,
+ * individual bytes are copied.
+ *
+ * @param startIndex starting index
+ * @param length number of elements to be copied
+ * @param target target vector
+ */
+ protected void splitAndTransferValidityBuffer(
+ int startIndex, int length, BaseValueVector target) {
+ int offset = startIndex % 8;
+
+ if (length <= 0) {
+ return;
+ }
+ if (offset == 0) {
+ sliceAndTransferValidityBuffer(startIndex, length, target);
+ } else {
+ copyValidityBuffer(startIndex, length, target);
+ }
+ }
+
+ /**
+ * If the start index is 8 byte aligned, slice `validityBuffer` and transfer ownership to
+ * `target`'s `validityBuffer`.
+ *
+ * @param startIndex starting index
+ * @param length number of elements to be copied
+ * @param target target vector
+ */
+ protected void sliceAndTransferValidityBuffer(
+ int startIndex, int length, BaseValueVector target) {
+ final int firstByteSource = BitVectorHelper.byteIndex(startIndex);
+ final int byteSizeTarget = getValidityBufferSizeFromCount(length);
+
+ if (target.validityBuffer != null) {
+ target.validityBuffer.getReferenceManager().release();
+ }
+ target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
+ target.validityBuffer.getReferenceManager().retain(1);
+ }
+
+ /**
+ * Allocate new validity buffer for `target` and copy bytes from `validityBuffer`. Precise details
+ * in the comments below.
+ *
+ * @param startIndex starting index
+ * @param length number of elements to be copied
+ * @param target target vector
+ */
+ protected void copyValidityBuffer(int startIndex, int length, BaseValueVector target) {
+ final int firstByteSource = BitVectorHelper.byteIndex(startIndex);
+ final int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
+ final int byteSizeTarget = getValidityBufferSizeFromCount(length);
+ final int offset = startIndex % 8;
+
+ /* Copy data
+ * When the first bit starts from the middle of a byte (offset != 0),
+ * copy data from src BitVector.
+ * Each byte in the target is composed by a part in i-th byte,
+ * another part in (i+1)-th byte.
+ */
+ target.allocateValidityBuffer(byteSizeTarget);
+
+ for (int i = 0; i < byteSizeTarget - 1; i++) {
+ byte b1 =
+ BitVectorHelper.getBitsFromCurrentByte(this.validityBuffer, firstByteSource + i, offset);
+ byte b2 =
+ BitVectorHelper.getBitsFromNextByte(this.validityBuffer, firstByteSource + i + 1, offset);
+
+ target.validityBuffer.setByte(i, (b1 + b2));
+ }
+
+ /* Copying the last piece is done in the following manner:
+ * if the source vector has 1 or more bytes remaining, we copy
+ * the last piece as a byte formed by shifting data
+ * from the current byte and the next byte.
+ *
+ * if the source vector has no more bytes remaining
+ * (we are at the last byte), we copy the last piece as a byte
+ * by shifting data from the current byte.
+ */
+ if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
+ byte b1 =
+ BitVectorHelper.getBitsFromCurrentByte(
+ this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
+ byte b2 =
+ BitVectorHelper.getBitsFromNextByte(
+ this.validityBuffer, firstByteSource + byteSizeTarget, offset);
+
+ target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
+ } else {
+ byte b1 =
+ BitVectorHelper.getBitsFromCurrentByte(
+ this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
+ target.validityBuffer.setByte(byteSizeTarget - 1, b1);
+ }
+ }
+
+ /**
+ * Allocate new validity buffer for when the bytes need to be copied over.
+ *
+ * @param byteSizeTarget desired size of the buffer
+ */
+ protected void allocateValidityBuffer(long byteSizeTarget) {
+ validityBuffer = allocator.buffer(byteSizeTarget);
+ validityBuffer.readerIndex(0);
+ validityBuffer.setZero(0, validityBuffer.capacity());
+ }
}
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
index 4f681311ed..96e2afbd29 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthVector.java
@@ -50,10 +50,8 @@ public abstract class BaseVariableWidthVector extends BaseValueVector
/* protected members */
public static final int OFFSET_WIDTH = 4; /* 4 byte unsigned int to track offsets */
protected static final byte[] emptyByteArray = new byte[] {};
- protected ArrowBuf validityBuffer;
protected ArrowBuf valueBuffer;
protected ArrowBuf offsetBuffer;
- protected int valueCount;
protected int lastSet;
protected final Field field;
@@ -87,7 +85,7 @@ public String getName() {
/* TODO:
* Once the entire hierarchy has been refactored, move common functions
- * like getNullCount(), splitAndTransferValidityBuffer to top level
+ * like getNullCount() to top level
* base class BaseValueVector.
*
* Along with this, some class members (validityBuffer) can also be
@@ -519,11 +517,9 @@ private ArrowBuf allocateOffsetBuffer(final long size) {
}
/* allocate validity buffer */
- private void allocateValidityBuffer(final long size) {
- final int curSize = (int) size;
- validityBuffer = allocator.buffer(curSize);
- validityBuffer.readerIndex(0);
- initValidityBuffer();
+ @Override
+ protected void allocateValidityBuffer(final long size) {
+ super.allocateValidityBuffer(size);
}
/**
@@ -856,70 +852,17 @@ private void splitAndTransferOffsetBuffer(
target.valueBuffer = transferBuffer(slicedBuffer, target.allocator);
}
- /*
- * Transfer the validity.
- */
- private void splitAndTransferValidityBuffer(
- int startIndex, int length, BaseVariableWidthVector target) {
- if (length <= 0) {
- return;
- }
-
+ @Override
+ protected void sliceAndTransferValidityBuffer(
+ int startIndex, int length, BaseValueVector target) {
final int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- final int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
final int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- final int offset = startIndex % 8;
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- final ArrowBuf slicedValidityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer = transferBuffer(slicedValidityBuffer, target.allocator);
- return;
- }
-
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(this.validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(this.validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- this.validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
+ if (target.validityBuffer != null) {
+ target.validityBuffer.getReferenceManager().release();
}
+ final ArrowBuf slicedValidityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
+ target.validityBuffer = transferBuffer(slicedValidityBuffer, target.allocator);
}
/*----------------------------------------------------------------*
diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
index 5e25ffa568..ea9de8320e 100644
--- a/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/BaseVariableWidthViewVector.java
@@ -78,13 +78,11 @@ public abstract class BaseVariableWidthViewVector extends BaseValueVector
// The third 4 bytes of view are allocated for buffer index
public static final int BUF_INDEX_WIDTH = 4;
public static final byte[] EMPTY_BYTE_ARRAY = new byte[] {};
- protected ArrowBuf validityBuffer;
// The view buffer is used to store the variable width view elements
protected ArrowBuf viewBuffer;
// The external buffer which stores the long strings
protected List dataBuffers;
protected int initialDataBufferSize;
- protected int valueCount;
protected int lastSet;
protected final Field field;
@@ -117,7 +115,7 @@ public String getName() {
/* TODO:
* Once the entire hierarchy has been refactored, move common functions
- * like getNullCount(), splitAndTransferValidityBuffer to top level
+ * like getNullCount() to top level
* base class BaseValueVector.
*
* Along with this, some class members (validityBuffer) can also be
@@ -129,12 +127,6 @@ public String getName() {
* the top class as of now is not a good idea.
*/
- /* TODO:
- * Implement TransferPair functionality
- * https://github.com/apache/arrow/issues/40932
- *
- */
-
/**
* Get buffer that manages the validity (NULL or NON-NULL nature) of elements in the vector.
* Consider it as a buffer for internal bit vector data structure.
@@ -854,77 +846,22 @@ public void splitAndTransferTo(int startIndex, int length, BaseVariableWidthView
}
/* allocate validity buffer */
- private void allocateValidityBuffer(final long size) {
- final int curSize = (int) size;
- validityBuffer = allocator.buffer(curSize);
- validityBuffer.readerIndex(0);
- initValidityBuffer();
+ @Override
+ protected void allocateValidityBuffer(final long size) {
+ super.allocateValidityBuffer(size);
}
- /*
- * Transfer the validity.
- */
- private void splitAndTransferValidityBuffer(
- int startIndex, int length, BaseVariableWidthViewVector target) {
- if (length <= 0) {
- return;
- }
-
+ @Override
+ protected void sliceAndTransferValidityBuffer(
+ int startIndex, int length, BaseValueVector target) {
final int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- final int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
final int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- final int offset = startIndex % 8;
-
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- final ArrowBuf slicedValidityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer = transferBuffer(slicedValidityBuffer, target.allocator);
- return;
- }
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(this.validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(this.validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- this.validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- this.validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
+ if (target.validityBuffer != null) {
+ target.validityBuffer.getReferenceManager().release();
}
+ final ArrowBuf slicedValidityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
+ target.validityBuffer = transferBuffer(slicedValidityBuffer, target.allocator);
}
/**
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/BaseLargeRepeatedValueViewVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/BaseLargeRepeatedValueViewVector.java
index 12edd6557b..fac3f86bba 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/BaseLargeRepeatedValueViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/BaseLargeRepeatedValueViewVector.java
@@ -52,7 +52,6 @@ public abstract class BaseLargeRepeatedValueViewVector extends BaseValueVector
protected ArrowBuf sizeBuffer;
protected FieldVector vector;
protected final CallBack repeatedCallBack;
- protected int valueCount;
protected long offsetAllocationSizeInBytes = INITIAL_VALUE_ALLOCATION * OFFSET_WIDTH;
protected long sizeAllocationSizeInBytes = INITIAL_VALUE_ALLOCATION * SIZE_WIDTH;
private final String name;
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/BaseRepeatedValueVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/BaseRepeatedValueVector.java
index fbe83bad52..ee1d65d3e3 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/BaseRepeatedValueVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/BaseRepeatedValueVector.java
@@ -54,7 +54,6 @@ public abstract class BaseRepeatedValueVector extends BaseValueVector
protected ArrowBuf offsetBuffer;
protected FieldVector vector;
protected final CallBack repeatedCallBack;
- protected int valueCount;
protected long offsetAllocationSizeInBytes = INITIAL_VALUE_ALLOCATION * OFFSET_WIDTH;
private final String name;
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/BaseRepeatedValueViewVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/BaseRepeatedValueViewVector.java
index e6213316b5..fd7a4ff2c6 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/BaseRepeatedValueViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/BaseRepeatedValueViewVector.java
@@ -52,7 +52,6 @@ public abstract class BaseRepeatedValueViewVector extends BaseValueVector
protected ArrowBuf sizeBuffer;
protected FieldVector vector;
protected final CallBack repeatedCallBack;
- protected int valueCount;
protected long offsetAllocationSizeInBytes = INITIAL_VALUE_ALLOCATION * OFFSET_WIDTH;
protected long sizeAllocationSizeInBytes = INITIAL_VALUE_ALLOCATION * SIZE_WIDTH;
private final String name;
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java
index 36d9ff40ed..e3b4ab477f 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/FixedSizeListVector.java
@@ -69,12 +69,10 @@ public static FixedSizeListVector empty(String name, int size, BufferAllocator a
}
private FieldVector vector;
- private ArrowBuf validityBuffer;
private final int listSize;
private Field field;
private UnionFixedSizeListReader reader;
- private int valueCount;
private int validityAllocationSizeInBytes;
/**
@@ -248,12 +246,10 @@ public boolean allocateNewSafe() {
return success;
}
- private void allocateValidityBuffer(final long size) {
- final int curSize = (int) size;
- validityBuffer = allocator.buffer(curSize);
- validityBuffer.readerIndex(0);
- validityAllocationSizeInBytes = curSize;
- validityBuffer.setZero(0, validityBuffer.capacity());
+ @Override
+ protected void allocateValidityBuffer(final long size) {
+ super.allocateValidityBuffer(size);
+ validityAllocationSizeInBytes = (int) size;
}
@Override
@@ -649,71 +645,6 @@ public void splitAndTransfer(int startIndex, int length) {
to.setValueCount(length);
}
- /*
- * transfer the validity.
- */
- private void splitAndTransferValidityBuffer(
- int startIndex, int length, FixedSizeListVector target) {
- int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- int offset = startIndex % 8;
-
- if (length > 0) {
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer.getReferenceManager().retain(1);
- } else {
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
-
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
- }
- }
- }
- }
-
@Override
public ValueVector getTo() {
return to;
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
index 71633441cb..835d3468f3 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListVector.java
@@ -94,11 +94,9 @@ public static LargeListVector empty(String name, BufferAllocator allocator) {
protected ArrowBuf offsetBuffer;
protected FieldVector vector;
protected final CallBack callBack;
- protected int valueCount;
protected long offsetAllocationSizeInBytes = INITIAL_VALUE_ALLOCATION * OFFSET_WIDTH;
protected String defaultDataVectorName = DATA_VECTOR_NAME;
- protected ArrowBuf validityBuffer;
protected UnionLargeListReader reader;
private Field field;
private int validityAllocationSizeInBytes;
@@ -375,12 +373,10 @@ public boolean allocateNewSafe() {
return success;
}
- private void allocateValidityBuffer(final long size) {
- final int curSize = (int) size;
- validityBuffer = allocator.buffer(curSize);
- validityBuffer.readerIndex(0);
- validityAllocationSizeInBytes = curSize;
- validityBuffer.setZero(0, validityBuffer.capacity());
+ @Override
+ protected void allocateValidityBuffer(final long size) {
+ super.allocateValidityBuffer(size);
+ validityAllocationSizeInBytes = (int) size;
}
protected ArrowBuf allocateOffsetBuffer(final long size) {
@@ -694,71 +690,6 @@ public void splitAndTransfer(int startIndex, int length) {
to.setValueCount(length);
}
- /*
- * transfer the validity.
- */
- private void splitAndTransferValidityBuffer(
- int startIndex, int length, LargeListVector target) {
- int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- int offset = startIndex % 8;
-
- if (length > 0) {
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer.getReferenceManager().retain(1);
- } else {
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
-
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
- }
- }
- }
- }
-
@Override
public ValueVector getTo() {
return to;
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java
index 1b7e6b2280..394c3c67bb 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/LargeListViewVector.java
@@ -77,7 +77,6 @@
public class LargeListViewVector extends BaseLargeRepeatedValueViewVector
implements PromotableVector, ValueIterableVector> {
- protected ArrowBuf validityBuffer;
protected UnionLargeListViewReader reader;
private CallBack callBack;
protected Field field;
@@ -285,12 +284,10 @@ public boolean allocateNewSafe() {
return success;
}
+ @Override
protected void allocateValidityBuffer(final long size) {
- final int curSize = (int) size;
- validityBuffer = allocator.buffer(curSize);
- validityBuffer.readerIndex(0);
- validityAllocationSizeInBytes = curSize;
- validityBuffer.setZero(0, validityBuffer.capacity());
+ super.allocateValidityBuffer(size);
+ validityAllocationSizeInBytes = (int) size;
}
@Override
@@ -531,71 +528,6 @@ public void splitAndTransfer(int startIndex, int length) {
}
}
- /*
- * transfer the validity.
- */
- private void splitAndTransferValidityBuffer(
- int startIndex, int length, LargeListViewVector target) {
- int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- int offset = startIndex % 8;
-
- if (length > 0) {
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer.getReferenceManager().retain(1);
- } else {
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
-
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
- }
- }
- }
- }
-
@Override
public ValueVector getTo() {
return to;
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java
index a8e8dcc436..2b2817515f 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/ListVector.java
@@ -74,7 +74,6 @@ public static ListVector empty(String name, BufferAllocator allocator) {
return new ListVector(name, allocator, FieldType.nullable(ArrowType.List.INSTANCE), null);
}
- protected ArrowBuf validityBuffer;
protected UnionListReader reader;
private CallBack callBack;
protected Field field;
@@ -324,12 +323,10 @@ public boolean allocateNewSafe() {
return success;
}
+ @Override
protected void allocateValidityBuffer(final long size) {
- final int curSize = (int) size;
- validityBuffer = allocator.buffer(curSize);
- validityBuffer.readerIndex(0);
- validityAllocationSizeInBytes = curSize;
- validityBuffer.setZero(0, validityBuffer.capacity());
+ super.allocateValidityBuffer(size);
+ validityAllocationSizeInBytes = (int) size;
}
/**
@@ -575,70 +572,6 @@ public void splitAndTransfer(int startIndex, int length) {
}
}
- /*
- * transfer the validity.
- */
- private void splitAndTransferValidityBuffer(int startIndex, int length, ListVector target) {
- int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- int offset = startIndex % 8;
-
- if (length > 0) {
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer.getReferenceManager().retain(1);
- } else {
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
-
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
- }
- }
- }
- }
-
@Override
public ValueVector getTo() {
return to;
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java
index ada25bbaf5..2b80101926 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/ListViewVector.java
@@ -76,7 +76,6 @@
public class ListViewVector extends BaseRepeatedValueViewVector
implements PromotableVector, ValueIterableVector> {
- protected ArrowBuf validityBuffer;
protected UnionListViewReader reader;
private CallBack callBack;
protected Field field;
@@ -284,12 +283,10 @@ public boolean allocateNewSafe() {
return success;
}
+ @Override
protected void allocateValidityBuffer(final long size) {
- final int curSize = (int) size;
- validityBuffer = allocator.buffer(curSize);
- validityBuffer.readerIndex(0);
- validityAllocationSizeInBytes = curSize;
- validityBuffer.setZero(0, validityBuffer.capacity());
+ super.allocateValidityBuffer(size);
+ validityAllocationSizeInBytes = (int) size;
}
@Override
@@ -538,70 +535,6 @@ public void splitAndTransfer(int startIndex, int length) {
}
}
- /*
- * transfer the validity.
- */
- private void splitAndTransferValidityBuffer(int startIndex, int length, ListViewVector target) {
- int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- int offset = startIndex % 8;
-
- if (length > 0) {
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer.getReferenceManager().retain(1);
- } else {
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
-
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
- }
- }
- }
- }
-
@Override
public ValueVector getTo() {
return to;
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java b/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java
index 5eb857ab94..3f98322ba9 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/MapVector.java
@@ -22,7 +22,6 @@
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.util.Preconditions;
import org.apache.arrow.vector.AddOrGetResult;
-import org.apache.arrow.vector.BitVectorHelper;
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.ValueVector;
import org.apache.arrow.vector.ZeroVector;
@@ -232,70 +231,6 @@ public void splitAndTransfer(int startIndex, int length) {
}
}
- /*
- * transfer the validity.
- */
- private void splitAndTransferValidityBuffer(int startIndex, int length, MapVector target) {
- int firstByteSource = BitVectorHelper.byteIndex(startIndex);
- int lastByteSource = BitVectorHelper.byteIndex(valueCount - 1);
- int byteSizeTarget = BitVectorHelper.getValidityBufferSizeFromCount(length);
- int offset = startIndex % 8;
-
- if (length > 0) {
- if (offset == 0) {
- // slice
- if (target.validityBuffer != null) {
- target.validityBuffer.getReferenceManager().release();
- }
- target.validityBuffer = validityBuffer.slice(firstByteSource, byteSizeTarget);
- target.validityBuffer.getReferenceManager().retain(1);
- } else {
- /* Copy data
- * When the first bit starts from the middle of a byte (offset != 0),
- * copy data from src BitVector.
- * Each byte in the target is composed by a part in i-th byte,
- * another part in (i+1)-th byte.
- */
- target.allocateValidityBuffer(byteSizeTarget);
-
- for (int i = 0; i < byteSizeTarget - 1; i++) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(validityBuffer, firstByteSource + i, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + i + 1, offset);
-
- target.validityBuffer.setByte(i, (b1 + b2));
- }
-
- /* Copying the last piece is done in the following manner:
- * if the source vector has 1 or more bytes remaining, we copy
- * the last piece as a byte formed by shifting data
- * from the current byte and the next byte.
- *
- * if the source vector has no more bytes remaining
- * (we are at the last byte), we copy the last piece as a byte
- * by shifting data from the current byte.
- */
- if ((firstByteSource + byteSizeTarget - 1) < lastByteSource) {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- byte b2 =
- BitVectorHelper.getBitsFromNextByte(
- validityBuffer, firstByteSource + byteSizeTarget, offset);
-
- target.validityBuffer.setByte(byteSizeTarget - 1, b1 + b2);
- } else {
- byte b1 =
- BitVectorHelper.getBitsFromCurrentByte(
- validityBuffer, firstByteSource + byteSizeTarget - 1, offset);
- target.validityBuffer.setByte(byteSizeTarget - 1, b1);
- }
- }
- }
- }
-
@Override
public ValueVector getTo() {
return to;
From 43b6b6ccf9107353d22cd2a90632ef2608d872c5 Mon Sep 17 00:00:00 2001
From: wangyunlai
Date: Tue, 1 Jul 2025 09:16:52 +0800
Subject: [PATCH 047/271] GH-759: Get length of byte[] in TryCopyLastError
(#760)
## What's Changed
We should get the length of byte[] by `GetArrayLength`, not `strlen`
which may cause invalid memory access.
Closes #759.
---
c/src/main/cpp/jni_wrapper.cc | 3 +-
.../org/apache/arrow/c/ExceptionTest.java | 150 ++++++++++++++++++
2 files changed, 152 insertions(+), 1 deletion(-)
create mode 100644 c/src/test/java/org/apache/arrow/c/ExceptionTest.java
diff --git a/c/src/main/cpp/jni_wrapper.cc b/c/src/main/cpp/jni_wrapper.cc
index 35c2b7787e..436cbdc806 100644
--- a/c/src/main/cpp/jni_wrapper.cc
+++ b/c/src/main/cpp/jni_wrapper.cc
@@ -205,8 +205,9 @@ void TryCopyLastError(JNIEnv* env, InnerPrivateData* private_data) {
return;
}
+ jsize error_bytes_len = env->GetArrayLength(arr);
char* error_str = reinterpret_cast(error_bytes);
- private_data->last_error_ = std::string(error_str, std::strlen(error_str));
+ private_data->last_error_ = std::string(error_str, error_bytes_len);
env->ReleaseByteArrayElements(arr, error_bytes, JNI_ABORT);
}
diff --git a/c/src/test/java/org/apache/arrow/c/ExceptionTest.java b/c/src/test/java/org/apache/arrow/c/ExceptionTest.java
new file mode 100644
index 0000000000..5bc96a8f99
--- /dev/null
+++ b/c/src/test/java/org/apache/arrow/c/ExceptionTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.c;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.catchThrowableOfType;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.VectorLoader;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.dictionary.Dictionary;
+import org.apache.arrow.vector.dictionary.DictionaryProvider;
+import org.apache.arrow.vector.ipc.ArrowReader;
+import org.apache.arrow.vector.ipc.message.ArrowRecordBatch;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.Schema;
+import org.junit.jupiter.api.Test;
+
+// Regression test for https://github.com/apache/arrow-java/issues/759
+final class ExceptionTest {
+ @Test
+ public void testException() throws IOException {
+ final Schema schema =
+ new Schema(Collections.singletonList(Field.nullable("ints", new ArrowType.Int(32, true))));
+ final List