From 79b65319b983729a229a5c0ee301d56343bb1de9 Mon Sep 17 00:00:00 2001 From: Bora <223137607@ge.com> Date: Tue, 16 Sep 2025 12:59:52 -0700 Subject: [PATCH 1/3] Allow-ISO-String-Binding-for-Date-Time-TimeStamp --- .../impl/DateAvaticaParameterConverter.java | 14 +- .../impl/TimeAvaticaParameterConverter.java | 48 +++++-- .../TimestampAvaticaParameterConverter.java | 26 +++- .../DateAvaticaParameterConverterTest.java | 96 ++++++++++++++ .../TimeAvaticaParameterConverterTest.java | 120 ++++++++++++++++++ ...imestampAvaticaParameterConverterTest.java | 69 ++++++++++ 6 files changed, 356 insertions(+), 17 deletions(-) create mode 100644 flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverterTest.java create mode 100644 flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverterTest.java create mode 100644 flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverterTest.java diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverter.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverter.java index 8795213530..f630115c1e 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverter.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverter.java @@ -16,6 +16,7 @@ */ package org.apache.arrow.driver.jdbc.converter.impl; +import java.time.LocalDate; import org.apache.arrow.vector.DateDayVector; import org.apache.arrow.vector.DateMilliVector; import org.apache.arrow.vector.FieldVector; @@ -31,12 +32,19 @@ public DateAvaticaParameterConverter(ArrowType.Date type) {} @Override public boolean bindParameter(FieldVector vector, TypedValue typedValue, int index) { - int value = (int) typedValue.toLocal(); + Object value = typedValue.value; + int days; + if (value instanceof String) { + LocalDate localDate = LocalDate.parse((String) value); + days = (int) localDate.toEpochDay(); + } else { + days = (int) typedValue.toLocal(); + } if (vector instanceof DateMilliVector) { - ((DateMilliVector) vector).setSafe(index, value); + ((DateMilliVector) vector).setSafe(index, days); return true; } else if (vector instanceof DateDayVector) { - ((DateDayVector) vector).setSafe(index, value); + ((DateDayVector) vector).setSafe(index, days); return true; } return false; diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java index 0340eb6099..4086e15c15 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java @@ -33,19 +33,41 @@ public TimeAvaticaParameterConverter(ArrowType.Time type) {} @Override public boolean bindParameter(FieldVector vector, TypedValue typedValue, int index) { - int value = (int) typedValue.toLocal(); - if (vector instanceof TimeMicroVector) { - ((TimeMicroVector) vector).setSafe(index, value); - return true; - } else if (vector instanceof TimeMilliVector) { - ((TimeMilliVector) vector).setSafe(index, value); - return true; - } else if (vector instanceof TimeNanoVector) { - ((TimeNanoVector) vector).setSafe(index, value); - return true; - } else if (vector instanceof TimeSecVector) { - ((TimeSecVector) vector).setSafe(index, value); - return true; + if (typedValue.value instanceof String) { + java.time.LocalTime localTime = java.time.LocalTime.parse((String) typedValue.value); + long nanos = localTime.toNanoOfDay(); + if (vector instanceof TimeMilliVector) { + int value = (int) (nanos / 1_000_000); + ((TimeMilliVector) vector).setSafe(index, value); + return true; + } else if (vector instanceof TimeMicroVector) { + long value = nanos / 1_000; + ((TimeMicroVector) vector).setSafe(index, value); + return true; + } else if (vector instanceof TimeNanoVector) { + long value = nanos; + ((TimeNanoVector) vector).setSafe(index, value); + return true; + } else if (vector instanceof TimeSecVector) { + int value = (int) (nanos / 1_000_000_000); + ((TimeSecVector) vector).setSafe(index, value); + return true; + } + } else { + int value = (int) typedValue.toLocal(); + if (vector instanceof TimeMilliVector) { + ((TimeMilliVector) vector).setSafe(index, value); + return true; + } else if (vector instanceof TimeMicroVector) { + ((TimeMicroVector) vector).setSafe(index, (long) value); + return true; + } else if (vector instanceof TimeNanoVector) { + ((TimeNanoVector) vector).setSafe(index, (long) value); + return true; + } else if (vector instanceof TimeSecVector) { + ((TimeSecVector) vector).setSafe(index, value); + return true; + } } return false; } diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverter.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverter.java index add3e30598..1a24d207c2 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverter.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverter.java @@ -16,6 +16,7 @@ */ package org.apache.arrow.driver.jdbc.converter.impl; +import java.time.Instant; import org.apache.arrow.vector.FieldVector; import org.apache.arrow.vector.TimeStampMicroTZVector; import org.apache.arrow.vector.TimeStampMicroVector; @@ -37,7 +38,30 @@ public TimestampAvaticaParameterConverter(ArrowType.Timestamp type) {} @Override public boolean bindParameter(FieldVector vector, TypedValue typedValue, int index) { - long value = (long) typedValue.toLocal(); + Object valueObj = typedValue.toLocal(); + long value; + if (valueObj instanceof String) { + // Parse ISO 8601 string to epoch millis + Instant instant = Instant.parse((String) valueObj); + if (vector instanceof TimeStampSecVector || vector instanceof TimeStampSecTZVector) { + value = instant.getEpochSecond(); + } else if (vector instanceof TimeStampMilliVector + || vector instanceof TimeStampMilliTZVector) { + value = instant.toEpochMilli(); + } else if (vector instanceof TimeStampMicroVector + || vector instanceof TimeStampMicroTZVector) { + value = instant.toEpochMilli() * 1000; + } else if (vector instanceof TimeStampNanoVector || vector instanceof TimeStampNanoTZVector) { + value = instant.toEpochMilli() * 1000000; + } else { + return false; + } + } else if (valueObj instanceof Long) { + value = (Long) valueObj; + } else { + return false; + } + if (vector instanceof TimeStampSecVector) { ((TimeStampSecVector) vector).setSafe(index, value); return true; diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverterTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverterTest.java new file mode 100644 index 0000000000..656c547638 --- /dev/null +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverterTest.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.arrow.driver.jdbc.converter.impl; + +import static org.junit.jupiter.api.Assertions.*; + +import java.time.LocalDate; +import org.apache.arrow.memory.RootAllocator; +import org.apache.arrow.vector.DateDayVector; +import org.apache.arrow.vector.DateMilliVector; +import org.apache.arrow.vector.IntVector; +import org.apache.arrow.vector.types.DateUnit; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.calcite.avatica.ColumnMetaData; +import org.apache.calcite.avatica.remote.TypedValue; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DateAvaticaParameterConverterTest { + private RootAllocator allocator; + + @BeforeEach + void setUp() { + allocator = new RootAllocator(Long.MAX_VALUE); + } + + @AfterEach + void tearDown() { + allocator.close(); + } + + @Test + void testBindParameterWithStringDateMilliVector() { + try (DateMilliVector vector = new DateMilliVector("date", allocator)) { + DateAvaticaParameterConverter converter = + new DateAvaticaParameterConverter(new ArrowType.Date(DateUnit.DAY)); + String dateStr = "2023-09-15"; + TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.STRING.toString(), dateStr); + boolean result = converter.bindParameter(vector, typedValue, 0); + assertTrue(result); + assertEquals((int) LocalDate.parse(dateStr).toEpochDay(), vector.get(0)); + } + } + + @Test + void testBindParameterWithStringDateDayVector() { + try (DateDayVector vector = new DateDayVector("date", allocator)) { + DateAvaticaParameterConverter converter = + new DateAvaticaParameterConverter(new ArrowType.Date(DateUnit.DAY)); + String dateStr = "2023-09-15"; + TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.STRING.toString(), dateStr); + boolean result = converter.bindParameter(vector, typedValue, 0); + assertTrue(result); + assertEquals((int) LocalDate.parse(dateStr).toEpochDay(), vector.get(0)); + } + } + + @Test + void testBindParameterWithEpochDays() { + try (DateDayVector vector = new DateDayVector("date", allocator)) { + DateAvaticaParameterConverter converter = + new DateAvaticaParameterConverter(new ArrowType.Date(DateUnit.DAY)); + int days = 19500; + TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.INTEGER.toString(), days); + boolean result = converter.bindParameter(vector, typedValue, 0); + assertTrue(result); + assertEquals(days, vector.get(0)); + } + } + + @Test + void testBindParameterWithUnsupportedVector() { + try (IntVector vector = new IntVector("int", allocator)) { + DateAvaticaParameterConverter converter = + new DateAvaticaParameterConverter(new ArrowType.Date(DateUnit.DAY)); + TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.STRING.toString(), "2023-09-15"); + boolean result = converter.bindParameter(vector, typedValue, 0); + assertFalse(result); + } + } +} diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverterTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverterTest.java new file mode 100644 index 0000000000..5bc88c5d22 --- /dev/null +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverterTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.arrow.driver.jdbc.converter.impl; + +import static org.junit.jupiter.api.Assertions.*; + +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.memory.RootAllocator; +import org.apache.arrow.vector.TimeMicroVector; +import org.apache.arrow.vector.TimeMilliVector; +import org.apache.arrow.vector.TimeNanoVector; +import org.apache.arrow.vector.TimeSecVector; +import org.apache.arrow.vector.types.TimeUnit; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.calcite.avatica.ColumnMetaData; +import org.apache.calcite.avatica.remote.TypedValue; +import org.junit.jupiter.api.Test; + +public class TimeAvaticaParameterConverterTest { + + @Test + void testBindParameterWithIsoStringMilli() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeMilliVector vector = new TimeMilliVector("t", allocator); + vector.allocateNew(1); + TimeAvaticaParameterConverter converter = + new TimeAvaticaParameterConverter(new ArrowType.Time(TimeUnit.MILLISECOND, 32)); + boolean result = + converter.bindParameter( + vector, TypedValue.create(ColumnMetaData.Rep.STRING.toString(), "21:39:50"), 0); + assertTrue(result); + assertEquals(77990000, vector.get(0)); + } + + @Test + void testBindParameterWithIsoStringMicro() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeMicroVector vector = new TimeMicroVector("t", allocator); + vector.allocateNew(1); + TimeAvaticaParameterConverter converter = + new TimeAvaticaParameterConverter(new ArrowType.Time(TimeUnit.MICROSECOND, 32)); + boolean result = + converter.bindParameter( + vector, TypedValue.create(ColumnMetaData.Rep.STRING.toString(), "21:39:50.123456"), 0); + assertTrue(result); + assertEquals(77990123456L, (long) vector.get(0)); + } + + @Test + void testBindParameterWithIsoStringNano() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeNanoVector vector = new TimeNanoVector("t", allocator); + vector.allocateNew(1); + TimeAvaticaParameterConverter converter = + new TimeAvaticaParameterConverter(new ArrowType.Time(TimeUnit.NANOSECOND, 64)); + boolean result = + converter.bindParameter( + vector, + TypedValue.create(ColumnMetaData.Rep.STRING.toString(), "21:39:50.123456789"), + 0); + assertTrue(result); + assertEquals(77990123456789L, (long) vector.get(0)); + } + + @Test + void testBindParameterWithIsoStringSec() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeSecVector vector = new TimeSecVector("t", allocator); + vector.allocateNew(1); + TimeAvaticaParameterConverter converter = + new TimeAvaticaParameterConverter(new ArrowType.Time(TimeUnit.SECOND, 32)); + boolean result = + converter.bindParameter( + vector, TypedValue.create(ColumnMetaData.Rep.STRING.toString(), "21:39:50"), 0); + assertTrue(result); + assertEquals(77990, vector.get(0)); + } + + @Test + void testBindParameterWithIntValueMilli() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeMilliVector vector = new TimeMilliVector("t", allocator); + vector.allocateNew(1); + TimeAvaticaParameterConverter converter = + new TimeAvaticaParameterConverter(new ArrowType.Time(TimeUnit.MILLISECOND, 32)); + boolean result = + converter.bindParameter( + vector, TypedValue.create(ColumnMetaData.Rep.INTEGER.toString(), 123456), 0); + assertTrue(result); + assertEquals(123456, vector.get(0)); + } + + @Test + void testBindParameterWithIntValueSec() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeSecVector vector = new TimeSecVector("t", allocator); + vector.allocateNew(1); + TimeAvaticaParameterConverter converter = + new TimeAvaticaParameterConverter(new ArrowType.Time(TimeUnit.SECOND, 32)); + boolean result = + converter.bindParameter( + vector, TypedValue.create(ColumnMetaData.Rep.INTEGER.toString(), 42), 0); + assertTrue(result); + assertEquals(42, vector.get(0)); + } +} diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverterTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverterTest.java new file mode 100644 index 0000000000..68b5f0bae3 --- /dev/null +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverterTest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.arrow.driver.jdbc.converter.impl; + +import static org.junit.jupiter.api.Assertions.*; + +import java.time.Instant; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.memory.RootAllocator; +import org.apache.arrow.vector.TimeStampMilliVector; +import org.apache.arrow.vector.types.TimeUnit; +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.calcite.avatica.ColumnMetaData; +import org.apache.calcite.avatica.remote.TypedValue; +import org.junit.jupiter.api.Test; + +public class TimestampAvaticaParameterConverterTest { + + @Test + void testBindIsoStringToMilliVector() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeStampMilliVector vector = new TimeStampMilliVector("ts", allocator); + vector.allocateNew(); + TimestampAvaticaParameterConverter converter = + new TimestampAvaticaParameterConverter(new ArrowType.Timestamp(TimeUnit.MILLISECOND, null)); + String isoString = "2025-08-14T15:53:00.000Z"; + TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.STRING.toString(), isoString); + assertTrue(converter.bindParameter(vector, typedValue, 0)); + assertEquals(Instant.parse(isoString).toEpochMilli(), vector.get(0)); + } + + @Test + void testBindLongToMilliVector() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeStampMilliVector vector = new TimeStampMilliVector("ts", allocator); + vector.allocateNew(); + TimestampAvaticaParameterConverter converter = + new TimestampAvaticaParameterConverter(new ArrowType.Timestamp(TimeUnit.MILLISECOND, null)); + long millis = 1755253980000L; + TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.LONG.toString(), millis); + assertTrue(converter.bindParameter(vector, typedValue, 0)); + assertEquals(millis, vector.get(0)); + } + + @Test + void testUnsupportedValueType() { + BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); + TimeStampMilliVector vector = new TimeStampMilliVector("ts", allocator); + vector.allocateNew(); + TimestampAvaticaParameterConverter converter = + new TimestampAvaticaParameterConverter(new ArrowType.Timestamp(TimeUnit.MILLISECOND, null)); + TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.DOUBLE.toString(), 3.14); + assertFalse(converter.bindParameter(vector, typedValue, 0)); + } +} From 9937c0defe64b2a2926903a7926bff638b11aa3c Mon Sep 17 00:00:00 2001 From: Bora <223137607@ge.com> Date: Wed, 17 Sep 2025 13:43:32 -0700 Subject: [PATCH 2/3] Refactored code for better readability --- .../impl/TimeAvaticaParameterConverter.java | 74 ++++++++++--------- .../TimestampAvaticaParameterConverter.java | 38 +++++----- 2 files changed, 62 insertions(+), 50 deletions(-) diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java index 4086e15c15..f9129f0a14 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java @@ -34,40 +34,48 @@ public TimeAvaticaParameterConverter(ArrowType.Time type) {} @Override public boolean bindParameter(FieldVector vector, TypedValue typedValue, int index) { if (typedValue.value instanceof String) { - java.time.LocalTime localTime = java.time.LocalTime.parse((String) typedValue.value); - long nanos = localTime.toNanoOfDay(); - if (vector instanceof TimeMilliVector) { - int value = (int) (nanos / 1_000_000); - ((TimeMilliVector) vector).setSafe(index, value); - return true; - } else if (vector instanceof TimeMicroVector) { - long value = nanos / 1_000; - ((TimeMicroVector) vector).setSafe(index, value); - return true; - } else if (vector instanceof TimeNanoVector) { - long value = nanos; - ((TimeNanoVector) vector).setSafe(index, value); - return true; - } else if (vector instanceof TimeSecVector) { - int value = (int) (nanos / 1_000_000_000); - ((TimeSecVector) vector).setSafe(index, value); - return true; - } + return bindTimeAsString(vector, (String) typedValue.value, index); } else { - int value = (int) typedValue.toLocal(); - if (vector instanceof TimeMilliVector) { - ((TimeMilliVector) vector).setSafe(index, value); - return true; - } else if (vector instanceof TimeMicroVector) { - ((TimeMicroVector) vector).setSafe(index, (long) value); - return true; - } else if (vector instanceof TimeNanoVector) { - ((TimeNanoVector) vector).setSafe(index, (long) value); - return true; - } else if (vector instanceof TimeSecVector) { - ((TimeSecVector) vector).setSafe(index, value); - return true; - } + return bindTimeAsInt(vector, (int) typedValue.toLocal(), index); + } + } + + private boolean bindTimeAsString(FieldVector vector, String value, int index) { + java.time.LocalTime localTime = java.time.LocalTime.parse(value); + long nanos = localTime.toNanoOfDay(); + if (vector instanceof TimeMilliVector) { + int v = (int) (nanos / 1_000_000); + ((TimeMilliVector) vector).setSafe(index, v); + return true; + } else if (vector instanceof TimeMicroVector) { + long v = nanos / 1_000; + ((TimeMicroVector) vector).setSafe(index, v); + return true; + } else if (vector instanceof TimeNanoVector) { + long v = nanos; + ((TimeNanoVector) vector).setSafe(index, v); + return true; + } else if (vector instanceof TimeSecVector) { + int v = (int) (nanos / 1_000_000_000); + ((TimeSecVector) vector).setSafe(index, v); + return true; + } + return false; + } + + private boolean bindTimeAsInt(FieldVector vector, int value, int index) { + if (vector instanceof TimeMilliVector) { + ((TimeMilliVector) vector).setSafe(index, value); + return true; + } else if (vector instanceof TimeMicroVector) { + ((TimeMicroVector) vector).setSafe(index, (long) value); + return true; + } else if (vector instanceof TimeNanoVector) { + ((TimeNanoVector) vector).setSafe(index, (long) value); + return true; + } else if (vector instanceof TimeSecVector) { + ((TimeSecVector) vector).setSafe(index, value); + return true; } return false; } diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverter.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverter.java index 1a24d207c2..02e0b5f1b6 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverter.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverter.java @@ -39,29 +39,33 @@ public TimestampAvaticaParameterConverter(ArrowType.Timestamp type) {} @Override public boolean bindParameter(FieldVector vector, TypedValue typedValue, int index) { Object valueObj = typedValue.toLocal(); - long value; if (valueObj instanceof String) { - // Parse ISO 8601 string to epoch millis - Instant instant = Instant.parse((String) valueObj); - if (vector instanceof TimeStampSecVector || vector instanceof TimeStampSecTZVector) { - value = instant.getEpochSecond(); - } else if (vector instanceof TimeStampMilliVector - || vector instanceof TimeStampMilliTZVector) { - value = instant.toEpochMilli(); - } else if (vector instanceof TimeStampMicroVector - || vector instanceof TimeStampMicroTZVector) { - value = instant.toEpochMilli() * 1000; - } else if (vector instanceof TimeStampNanoVector || vector instanceof TimeStampNanoTZVector) { - value = instant.toEpochMilli() * 1000000; - } else { - return false; - } + return bindTimestampAsString(vector, (String) valueObj, index); } else if (valueObj instanceof Long) { - value = (Long) valueObj; + return bindTimestampAsLong(vector, (Long) valueObj, index); } else { return false; } + } + + private boolean bindTimestampAsString(FieldVector vector, String value, int index) { + Instant instant = Instant.parse(value); + long result; + if (vector instanceof TimeStampSecVector || vector instanceof TimeStampSecTZVector) { + result = instant.getEpochSecond(); + } else if (vector instanceof TimeStampMilliVector || vector instanceof TimeStampMilliTZVector) { + result = instant.toEpochMilli(); + } else if (vector instanceof TimeStampMicroVector || vector instanceof TimeStampMicroTZVector) { + result = instant.toEpochMilli() * 1000; + } else if (vector instanceof TimeStampNanoVector || vector instanceof TimeStampNanoTZVector) { + result = instant.toEpochMilli() * 1000000; + } else { + return false; + } + return bindTimestampAsLong(vector, result, index); + } + private boolean bindTimestampAsLong(FieldVector vector, long value, int index) { if (vector instanceof TimeStampSecVector) { ((TimeStampSecVector) vector).setSafe(index, value); return true; From cc77d165f44b89f430563f22aa0505e5f878f0fd Mon Sep 17 00:00:00 2001 From: Bora <223137607@ge.com> Date: Thu, 18 Sep 2025 11:43:04 -0700 Subject: [PATCH 3/3] Addressed review comments and refactored --- .../impl/DateAvaticaParameterConverter.java | 7 ++++-- .../impl/TimeAvaticaParameterConverter.java | 9 ++++--- .../DateAvaticaParameterConverterTest.java | 3 ++- .../TimeAvaticaParameterConverterTest.java | 25 ++++++++++++++----- ...imestampAvaticaParameterConverterTest.java | 19 +++++++++++--- 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverter.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverter.java index f630115c1e..b31641e58f 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverter.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverter.java @@ -37,11 +37,14 @@ public boolean bindParameter(FieldVector vector, TypedValue typedValue, int inde if (value instanceof String) { LocalDate localDate = LocalDate.parse((String) value); days = (int) localDate.toEpochDay(); + } else if (value instanceof Integer) { + days = (Integer) value; } else { - days = (int) typedValue.toLocal(); + return false; } + if (vector instanceof DateMilliVector) { - ((DateMilliVector) vector).setSafe(index, days); + ((DateMilliVector) vector).setSafe(index, days * 24 * 60 * 60 * 1000); return true; } else if (vector instanceof DateDayVector) { ((DateDayVector) vector).setSafe(index, days); diff --git a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java index f9129f0a14..33add1c461 100644 --- a/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java +++ b/flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverter.java @@ -33,10 +33,13 @@ public TimeAvaticaParameterConverter(ArrowType.Time type) {} @Override public boolean bindParameter(FieldVector vector, TypedValue typedValue, int index) { - if (typedValue.value instanceof String) { - return bindTimeAsString(vector, (String) typedValue.value, index); + Object value = typedValue.value; + if (value instanceof String) { + return bindTimeAsString(vector, (String) value, index); + } else if (value instanceof Integer) { + return bindTimeAsInt(vector, (int) value, index); } else { - return bindTimeAsInt(vector, (int) typedValue.toLocal(), index); + return false; } } diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverterTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverterTest.java index 656c547638..e2b9c100b0 100644 --- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverterTest.java +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/DateAvaticaParameterConverterTest.java @@ -53,7 +53,8 @@ void testBindParameterWithStringDateMilliVector() { TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.STRING.toString(), dateStr); boolean result = converter.bindParameter(vector, typedValue, 0); assertTrue(result); - assertEquals((int) LocalDate.parse(dateStr).toEpochDay(), vector.get(0)); + assertEquals( + (int) (LocalDate.parse(dateStr).toEpochDay() * 24 * 60 * 60 * 1000), vector.get(0)); } } diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverterTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverterTest.java index 5bc88c5d22..9c7516a71f 100644 --- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverterTest.java +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimeAvaticaParameterConverterTest.java @@ -28,13 +28,25 @@ import org.apache.arrow.vector.types.pojo.ArrowType; import org.apache.calcite.avatica.ColumnMetaData; import org.apache.calcite.avatica.remote.TypedValue; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TimeAvaticaParameterConverterTest { + private BufferAllocator allocator; + + @BeforeEach + void setUp() { + allocator = new RootAllocator(Long.MAX_VALUE); + } + + @AfterEach + void tearDown() { + allocator.close(); + } @Test void testBindParameterWithIsoStringMilli() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeMilliVector vector = new TimeMilliVector("t", allocator); vector.allocateNew(1); TimeAvaticaParameterConverter converter = @@ -44,11 +56,11 @@ void testBindParameterWithIsoStringMilli() { vector, TypedValue.create(ColumnMetaData.Rep.STRING.toString(), "21:39:50"), 0); assertTrue(result); assertEquals(77990000, vector.get(0)); + vector.close(); } @Test void testBindParameterWithIsoStringMicro() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeMicroVector vector = new TimeMicroVector("t", allocator); vector.allocateNew(1); TimeAvaticaParameterConverter converter = @@ -58,11 +70,11 @@ void testBindParameterWithIsoStringMicro() { vector, TypedValue.create(ColumnMetaData.Rep.STRING.toString(), "21:39:50.123456"), 0); assertTrue(result); assertEquals(77990123456L, (long) vector.get(0)); + vector.close(); } @Test void testBindParameterWithIsoStringNano() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeNanoVector vector = new TimeNanoVector("t", allocator); vector.allocateNew(1); TimeAvaticaParameterConverter converter = @@ -74,11 +86,11 @@ void testBindParameterWithIsoStringNano() { 0); assertTrue(result); assertEquals(77990123456789L, (long) vector.get(0)); + vector.close(); } @Test void testBindParameterWithIsoStringSec() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeSecVector vector = new TimeSecVector("t", allocator); vector.allocateNew(1); TimeAvaticaParameterConverter converter = @@ -88,11 +100,11 @@ void testBindParameterWithIsoStringSec() { vector, TypedValue.create(ColumnMetaData.Rep.STRING.toString(), "21:39:50"), 0); assertTrue(result); assertEquals(77990, vector.get(0)); + vector.close(); } @Test void testBindParameterWithIntValueMilli() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeMilliVector vector = new TimeMilliVector("t", allocator); vector.allocateNew(1); TimeAvaticaParameterConverter converter = @@ -102,11 +114,11 @@ void testBindParameterWithIntValueMilli() { vector, TypedValue.create(ColumnMetaData.Rep.INTEGER.toString(), 123456), 0); assertTrue(result); assertEquals(123456, vector.get(0)); + vector.close(); } @Test void testBindParameterWithIntValueSec() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeSecVector vector = new TimeSecVector("t", allocator); vector.allocateNew(1); TimeAvaticaParameterConverter converter = @@ -116,5 +128,6 @@ void testBindParameterWithIntValueSec() { vector, TypedValue.create(ColumnMetaData.Rep.INTEGER.toString(), 42), 0); assertTrue(result); assertEquals(42, vector.get(0)); + vector.close(); } } diff --git a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverterTest.java b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverterTest.java index 68b5f0bae3..0954fed3b3 100644 --- a/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverterTest.java +++ b/flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/converter/impl/TimestampAvaticaParameterConverterTest.java @@ -26,13 +26,25 @@ import org.apache.arrow.vector.types.pojo.ArrowType; import org.apache.calcite.avatica.ColumnMetaData; import org.apache.calcite.avatica.remote.TypedValue; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TimestampAvaticaParameterConverterTest { + private BufferAllocator allocator; + + @BeforeEach + void setUp() { + allocator = new RootAllocator(Long.MAX_VALUE); + } + + @AfterEach + void tearDown() { + allocator.close(); + } @Test void testBindIsoStringToMilliVector() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeStampMilliVector vector = new TimeStampMilliVector("ts", allocator); vector.allocateNew(); TimestampAvaticaParameterConverter converter = @@ -41,11 +53,11 @@ void testBindIsoStringToMilliVector() { TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.STRING.toString(), isoString); assertTrue(converter.bindParameter(vector, typedValue, 0)); assertEquals(Instant.parse(isoString).toEpochMilli(), vector.get(0)); + vector.close(); } @Test void testBindLongToMilliVector() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeStampMilliVector vector = new TimeStampMilliVector("ts", allocator); vector.allocateNew(); TimestampAvaticaParameterConverter converter = @@ -54,16 +66,17 @@ void testBindLongToMilliVector() { TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.LONG.toString(), millis); assertTrue(converter.bindParameter(vector, typedValue, 0)); assertEquals(millis, vector.get(0)); + vector.close(); } @Test void testUnsupportedValueType() { - BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); TimeStampMilliVector vector = new TimeStampMilliVector("ts", allocator); vector.allocateNew(); TimestampAvaticaParameterConverter converter = new TimestampAvaticaParameterConverter(new ArrowType.Timestamp(TimeUnit.MILLISECOND, null)); TypedValue typedValue = TypedValue.create(ColumnMetaData.Rep.DOUBLE.toString(), 3.14); assertFalse(converter.bindParameter(vector, typedValue, 0)); + vector.close(); } }