Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,12 +32,22 @@ 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 if (value instanceof Integer) {
days = (Integer) value;
} else {
return false;
}

if (vector instanceof DateMilliVector) {
((DateMilliVector) vector).setSafe(index, value);
((DateMilliVector) vector).setSafe(index, days * 24 * 60 * 60 * 1000);
return true;
} else if (vector instanceof DateDayVector) {
((DateDayVector) vector).setSafe(index, value);
((DateDayVector) vector).setSafe(index, days);
return true;
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,48 @@ 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);
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 false;
}
}

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 TimeMilliVector) {
} 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, value);
((TimeNanoVector) vector).setSafe(index, (long) value);
return true;
} else if (vector instanceof TimeSecVector) {
((TimeSecVector) vector).setSafe(index, value);
Expand Down
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so we only allow timestamps with the Z suffix - but we can bind to naive timestamp vectors too. And we don't allow timestamps without the suffix. (We could always relax that later, though.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct. Currently, we only accept timestamps with the Z suffix, but we can easily relax this restriction later to support naive timestamps if needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn't allow binding timestamps with timezones to columns that don't have timezones in principle. Although what happens the Z suffix is probably unambiguous in this case I think if we ever allow other timezones then it becomes a problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbonofre or @aiguofer any opinions on the behavior here?

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -37,7 +38,34 @@ public TimestampAvaticaParameterConverter(ArrowType.Timestamp type) {}

@Override
public boolean bindParameter(FieldVector vector, TypedValue typedValue, int index) {
long value = (long) typedValue.toLocal();
Object valueObj = typedValue.toLocal();
if (valueObj instanceof String) {
return bindTimestampAsString(vector, (String) valueObj, index);
} else if (valueObj instanceof Long) {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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() * 24 * 60 * 60 * 1000), 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* 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.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() {
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));
vector.close();
}

@Test
void testBindParameterWithIsoStringMicro() {
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));
vector.close();
}

@Test
void testBindParameterWithIsoStringNano() {
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));
vector.close();
}

@Test
void testBindParameterWithIsoStringSec() {
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));
vector.close();
}

@Test
void testBindParameterWithIntValueMilli() {
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));
vector.close();
}

@Test
void testBindParameterWithIntValueSec() {
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));
vector.close();
}
}
Loading