From 53729bb8ca94ea5e470b702d87b8ae05e8dab7ae Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Wed, 12 Aug 2026 20:08:59 -0400 Subject: [PATCH 1/6] refactor(jdbc): migrate Read Path to BigQueryTypeRegistry --- .../bigquery/jdbc/BigQueryArrowArray.java | 11 +++---- .../bigquery/jdbc/BigQueryArrowResultSet.java | 19 +++++------ .../bigquery/jdbc/BigQueryArrowStruct.java | 10 +++--- .../bigquery/jdbc/BigQueryBaseArray.java | 4 +-- .../bigquery/jdbc/BigQueryBaseResultSet.java | 32 +++++++++---------- .../bigquery/jdbc/BigQueryJsonArray.java | 12 +++---- .../bigquery/jdbc/BigQueryJsonResultSet.java | 6 ++-- .../bigquery/jdbc/BigQueryJsonStruct.java | 14 ++++---- 8 files changed, 54 insertions(+), 54 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java index f25523a45e70..81ef40d04d0e 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java @@ -28,8 +28,7 @@ * An implementation of {@link BigQueryBaseArray} used to represent Array values from Arrow data. */ class BigQueryArrowArray extends BigQueryBaseArray { - private static final BigQueryTypeCoercer BIGQUERY_TYPE_COERCER = - BigQueryTypeCoercionUtility.INSTANCE; + private JsonStringArrayList values; public BigQueryArrowArray(Field schema, JsonStringArrayList values) { @@ -43,7 +42,7 @@ public BigQueryArrowArray( } @Override - public Object getArray() { + public Object getArray() throws SQLException { LOG.finestTrace("getArray"); ensureValid(); if (values == null) { @@ -53,7 +52,7 @@ public Object getArray() { } @Override - public Object getArray(long index, int count) { + public Object getArray(long index, int count) throws SQLException { LOG.finestTrace("getArray"); ensureValid(); if (values == null) { @@ -98,12 +97,12 @@ public void free() { } @Override - Object getCoercedValue(int index) { + Object getCoercedValue(int index) throws SQLException { LOG.finestTrace("getCoercedValue"); Object value = this.values.get(index); return this.arrayOfStruct ? new BigQueryArrowStruct( schema.getSubFields(), (JsonStringHashMap) value, this.LOG.getArrowStructLogger()) - : BIGQUERY_TYPE_COERCER.coerceTo(getTargetClass(), value, this.LOG); + : BigQueryTypeRegistry.convert(value, getTargetClass()); } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java index 3123b6c09b40..15f6976436aa 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java @@ -357,7 +357,7 @@ public Object getObject(int columnIndex) throws SQLException { } if (this.isNested && columnIndex == 1) { - return this.bigQueryTypeCoercer.coerceTo(Integer.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Integer.class); } if (this.isNested && columnIndex == 2) { @@ -371,7 +371,7 @@ public Object getObject(int columnIndex) throws SQLException { Class targetClass = BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( arrayField.getType().getStandardType()); - return this.bigQueryTypeCoercer.coerceTo(targetClass, value, this.LOG); + return BigQueryTypeRegistry.convert(value, targetClass); } int fieldIndex = this.isNested ? 0 : columnIndex - 1; @@ -440,7 +440,7 @@ public Object getObject(int columnIndex) throws SQLException { Class targetClass = BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( fieldSchema.getType().getStandardType()); - return this.bigQueryTypeCoercer.coerceTo(targetClass, value, this.LOG); + return BigQueryTypeRegistry.convert(value, targetClass); } } @@ -460,24 +460,25 @@ private StandardSQLTypeName getElementTypeFromValue(Object element) { return StandardSQLTypeName.STRING; } - private String formatRangeElement(Object element, StandardSQLTypeName elementType) { + private String formatRangeElement(Object element, StandardSQLTypeName elementType) + throws SQLException { if (element == null) { return "UNBOUNDED"; } switch (elementType) { case DATE: // Arrow gives DATE as an Integer (days since epoch) - Date date = this.bigQueryTypeCoercer.coerceTo(Date.class, (Integer) element, this.LOG); + Date date = BigQueryTypeRegistry.convert((Integer) element, Date.class); return date.toString(); case DATETIME: // Arrow gives DATETIME as a LocalDateTime Timestamp dtTs = - this.bigQueryTypeCoercer.coerceTo(Timestamp.class, (LocalDateTime) element, this.LOG); - return this.bigQueryTypeCoercer.coerceTo(String.class, dtTs, this.LOG); + BigQueryTypeRegistry.convert((LocalDateTime) element, Timestamp.class); + return BigQueryTypeRegistry.convert(dtTs, String.class); case TIMESTAMP: // Arrow gives TIMESTAMP as a Long (microseconds since epoch) - Timestamp ts = this.bigQueryTypeCoercer.coerceTo(Timestamp.class, (Long) element, this.LOG); - return this.bigQueryTypeCoercer.coerceTo(String.class, ts, this.LOG); + Timestamp ts = BigQueryTypeRegistry.convert((Long) element, Timestamp.class); + return BigQueryTypeRegistry.convert(ts, String.class); default: // Fallback for any other unexpected type return element.toString(); diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java index e07406d996df..d9f0160b3f5e 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java @@ -19,6 +19,7 @@ import static com.google.cloud.bigquery.jdbc.BigQueryBaseArray.isArray; import com.google.cloud.bigquery.Field; +import java.sql.SQLException; import com.google.cloud.bigquery.FieldList; import java.lang.reflect.Array; import java.util.ArrayList; @@ -30,8 +31,7 @@ * An implementation of {@link BigQueryBaseStruct} used to represent Struct values from Arrow data. */ class BigQueryArrowStruct extends BigQueryBaseStruct { - private static final BigQueryTypeCoercer BIGQUERY_TYPE_COERCER = - BigQueryTypeCoercionUtility.INSTANCE; + private final FieldList schema; @@ -54,7 +54,7 @@ FieldList getSchema() { } @Override - public Object[] getAttributes() { + public Object[] getAttributes() throws SQLException { LOG.finestTrace("getAttributes"); int size = this.schema.size(); Object[] attributes = (Object[]) Array.newInstance(Object.class, size); @@ -73,7 +73,7 @@ public Object[] getAttributes() { return attributes; } - private Object getValue(Field currentSchema, Object currentValue) { + private Object getValue(Field currentSchema, Object currentValue) throws SQLException { LOG.finestTrace("getValue"); if (isArray(currentSchema)) { return new BigQueryArrowArray( @@ -87,7 +87,7 @@ private Object getValue(Field currentSchema, Object currentValue) { Class targetClass = BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( currentSchema.getType().getStandardType()); - return BIGQUERY_TYPE_COERCER.coerceTo(targetClass, currentValue, this.LOG); + return BigQueryTypeRegistry.convert(currentValue, targetClass); } } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java index e001e35c6a21..d58847ab4946 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java @@ -91,7 +91,7 @@ public final ResultSet getResultSet(long index, int count, Map> throw new BigQueryJdbcSqlFeatureNotSupportedException(CUSTOMER_TYPE_MAPPING_NOT_SUPPORTED); } - protected Object getArrayInternal(int fromIndex, int toIndexExclusive) { + protected Object getArrayInternal(int fromIndex, int toIndexExclusive) throws SQLException { LOG.finestTrace("getArrayInternal"); Class targetClass = getTargetClass(); int size = toIndexExclusive - fromIndex; @@ -149,7 +149,7 @@ protected Class getTargetClass() { this.schema.getType().getStandardType()); } - abstract Object getCoercedValue(int index); + abstract Object getCoercedValue(int index) throws SQLException; static boolean isArray(Field currentSchema) { return currentSchema.getMode() == REPEATED; diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java index 9216732b49b2..ee52d7504738 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseResultSet.java @@ -70,7 +70,7 @@ public abstract class BigQueryBaseResultSet extends BigQueryNoOpsResultSet private Job job; private SQLWarning warnings; private boolean warningsLoaded = false; - protected final BigQueryTypeCoercer bigQueryTypeCoercer = BigQueryTypeCoercionUtility.INSTANCE; + protected final SpanContext originalSpanContext; protected BigQueryBaseResultSet( @@ -297,7 +297,7 @@ public T getObject(int columnIndex, Class type) throws SQLException { if (value == null) { return null; } - return this.bigQueryTypeCoercer.coerceTo(type, value, this.LOG); + return BigQueryTypeRegistry.convert(value, type); } catch (RuntimeException e) { throw createCoercionException(columnIndex, type, e); } @@ -323,7 +323,7 @@ public String getString(int columnIndex) throws SQLException { LOG.finestTrace("getString"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(String.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, String.class); } catch (BigQueryJdbcCoercionNotFoundException e) { throw createCoercionException(columnIndex, String.class, e); } @@ -342,7 +342,7 @@ public boolean getBoolean(int columnIndex) throws SQLException { try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(Boolean.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Boolean.class); } catch (BigQueryJdbcCoercionNotFoundException e) { throw createCoercionException(columnIndex, Boolean.class, e); } @@ -353,7 +353,7 @@ public byte getByte(int columnIndex) throws SQLException { LOG.finestTrace("getByte"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(Byte.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Byte.class); } catch (BigQueryJdbcCoercionNotFoundException | BigQueryJdbcCoercionException e) { throw createCoercionException(columnIndex, Byte.class, e); } @@ -364,7 +364,7 @@ public short getShort(int columnIndex) throws SQLException { LOG.finestTrace("getShort"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(Short.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Short.class); } catch (BigQueryJdbcCoercionNotFoundException | BigQueryJdbcCoercionException e) { throw createCoercionException(columnIndex, Short.class, e); } @@ -375,7 +375,7 @@ public int getInt(int columnIndex) throws SQLException { LOG.finestTrace("getInt"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(Integer.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Integer.class); } catch (BigQueryJdbcCoercionNotFoundException | BigQueryJdbcCoercionException e) { throw createCoercionException(columnIndex, Integer.class, e); } @@ -386,7 +386,7 @@ public long getLong(int columnIndex) throws SQLException { LOG.finestTrace("getLong"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(Long.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Long.class); } catch (BigQueryJdbcCoercionNotFoundException | BigQueryJdbcCoercionException e) { throw createCoercionException(columnIndex, Long.class, e); } @@ -397,7 +397,7 @@ public float getFloat(int columnIndex) throws SQLException { LOG.finestTrace("getFloat"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(Float.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Float.class); } catch (BigQueryJdbcCoercionNotFoundException | BigQueryJdbcCoercionException e) { throw createCoercionException(columnIndex, Float.class, e); } @@ -408,7 +408,7 @@ public double getDouble(int columnIndex) throws SQLException { LOG.finestTrace("getDouble"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(Double.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Double.class); } catch (BigQueryJdbcCoercionNotFoundException | BigQueryJdbcCoercionException e) { throw createCoercionException(columnIndex, Double.class, e); } @@ -421,7 +421,7 @@ public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException LOG.finestTrace("getBigDecimal"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(BigDecimal.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, BigDecimal.class); } catch (BigQueryJdbcCoercionNotFoundException | BigQueryJdbcCoercionException e) { throw createCoercionException(columnIndex, BigDecimal.class, e); } @@ -432,7 +432,7 @@ public byte[] getBytes(int columnIndex) throws SQLException { LOG.finestTrace("getBytes"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(byte[].class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, byte[].class); } catch (BigQueryJdbcCoercionNotFoundException e) { throw createCoercionException(columnIndex, byte[].class, e); } @@ -443,7 +443,7 @@ public Date getDate(int columnIndex) throws SQLException { LOG.finestTrace("getDate"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(java.sql.Date.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, java.sql.Date.class); } catch (BigQueryJdbcCoercionNotFoundException e) { throw createCoercionException(columnIndex, java.sql.Date.class, e); } @@ -458,7 +458,7 @@ public Time getTime(int columnIndex) throws SQLException { } try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(java.sql.Time.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, java.sql.Time.class); } catch (BigQueryJdbcCoercionNotFoundException e) { throw createCoercionException(columnIndex, java.sql.Time.class, e); } @@ -473,7 +473,7 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { } try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(java.sql.Timestamp.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, java.sql.Timestamp.class); } catch (BigQueryJdbcCoercionNotFoundException e) { throw createCoercionException(columnIndex, java.sql.Timestamp.class, e); } @@ -484,7 +484,7 @@ public BigDecimal getBigDecimal(int columnIndex) throws SQLException { LOG.finestTrace("getBigDecimal"); try { Object value = getObject(columnIndex); - return this.bigQueryTypeCoercer.coerceTo(BigDecimal.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, BigDecimal.class); } catch (BigQueryJdbcCoercionNotFoundException | BigQueryJdbcCoercionException e) { throw createCoercionException(columnIndex, BigDecimal.class, e); } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArray.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArray.java index 280c34aa15d6..a85fc3cce440 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArray.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArray.java @@ -25,13 +25,13 @@ import com.google.cloud.bigquery.FieldValue; import com.google.cloud.bigquery.Schema; import java.sql.ResultSet; +import java.sql.SQLException; import java.util.List; /** An implementation of {@link BigQueryBaseArray} used to represent Array values from Json data. */ @InternalApi class BigQueryJsonArray extends BigQueryBaseArray { - private static final BigQueryTypeCoercer BIGQUERY_TYPE_COERCER = - BigQueryTypeCoercionUtility.INSTANCE; + private List values; BigQueryJsonArray(Field schema, FieldValue values) { @@ -44,7 +44,7 @@ class BigQueryJsonArray extends BigQueryBaseArray { } @Override - public Object getArray() { + public Object getArray() throws SQLException { ensureValid(); LOG.finestTrace("getArray"); if (this.values == null) { @@ -54,7 +54,7 @@ public Object getArray() { } @Override - public Object getArray(long index, int count) { + public Object getArray(long index, int count) throws SQLException { ensureValid(); LOG.finestTrace("getArray"); if (this.values == null) { @@ -98,11 +98,11 @@ public void free() { } @Override - Object getCoercedValue(int index) { + Object getCoercedValue(int index) throws SQLException { FieldValue fieldValue = this.values.get(index); return this.arrayOfStruct ? new BigQueryJsonStruct( this.schema.getSubFields(), fieldValue, this.LOG.getJsonStructLogger()) - : BIGQUERY_TYPE_COERCER.coerceTo(getTargetClass(), fieldValue, this.LOG); + : BigQueryTypeRegistry.convert(fieldValue, getTargetClass()); } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java index 0dbda843d1e1..a5bd1ded6107 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java @@ -225,7 +225,7 @@ public Object getObject(int columnIndex) throws SQLException { } if (this.isNested && columnIndex == 1) { - return this.bigQueryTypeCoercer.coerceTo(Integer.class, value, this.LOG); + return BigQueryTypeRegistry.convert(value, Integer.class); } if (this.isNested && columnIndex == 2) { @@ -237,7 +237,7 @@ public Object getObject(int columnIndex) throws SQLException { Class targetClass = BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( arrayField.getType().getStandardType()); - return this.bigQueryTypeCoercer.coerceTo(targetClass, value, this.LOG); + return BigQueryTypeRegistry.convert(value, targetClass); } int extraIndex = this.isNested ? 2 : 1; @@ -251,7 +251,7 @@ public Object getObject(int columnIndex) throws SQLException { Class targetClass = BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( fieldSchema.getType().getStandardType()); - return this.bigQueryTypeCoercer.coerceTo(targetClass, value, this.LOG); + return BigQueryTypeRegistry.convert(value, targetClass); } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java index ed39edbecf17..7943f229cb87 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java @@ -22,6 +22,7 @@ import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.FieldList; import com.google.cloud.bigquery.FieldValue; +import java.sql.SQLException; import java.lang.reflect.Array; import java.util.List; @@ -30,8 +31,7 @@ */ @InternalApi class BigQueryJsonStruct extends BigQueryBaseStruct { - private static final BigQueryTypeCoercer BIGQUERY_TYPE_COERCER = - BigQueryTypeCoercionUtility.INSTANCE; + private final FieldList schema; private final List values; @@ -52,7 +52,7 @@ FieldList getSchema() { } @Override - public Object[] getAttributes() { + public Object[] getAttributes() throws SQLException { LOG.finestTrace("getAttributes"); int size = schema.size(); Object[] attributes = (Object[]) Array.newInstance(Object.class, size); @@ -66,18 +66,18 @@ public Object[] getAttributes() { return attributes; } - private Object getValue(Field currentSchema, FieldValue currentValue) { + private Object getValue(Field currentSchema, Object currentValue) throws SQLException { LOG.finestTrace("getValue"); if (isArray(currentSchema)) { - return new BigQueryJsonArray(currentSchema, currentValue, this.LOG.getJsonArrayLogger()); + return new BigQueryJsonArray(currentSchema, (FieldValue) currentValue, this.LOG.getJsonArrayLogger()); } else if (isStruct(currentSchema)) { return new BigQueryJsonStruct( - currentSchema.getSubFields(), currentValue, this.LOG.getJsonStructLogger()); + currentSchema.getSubFields(), (FieldValue) currentValue, this.LOG.getJsonStructLogger()); } else { Class targetClass = BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( currentSchema.getType().getStandardType()); - return BIGQUERY_TYPE_COERCER.coerceTo(targetClass, currentValue, this.LOG); + return BigQueryTypeRegistry.convert(currentValue, targetClass); } } } From e26da00d6839e4889796e09e41e018ac6858e27d Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Wed, 12 Aug 2026 20:12:24 -0400 Subject: [PATCH 2/6] refactor(jdbc): migrate Read Path to BigQueryTypeRegistry --- .../google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java | 3 +-- .../com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java | 3 +-- .../com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java index 15f6976436aa..11d208f99cea 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java @@ -472,8 +472,7 @@ private String formatRangeElement(Object element, StandardSQLTypeName elementTyp return date.toString(); case DATETIME: // Arrow gives DATETIME as a LocalDateTime - Timestamp dtTs = - BigQueryTypeRegistry.convert((LocalDateTime) element, Timestamp.class); + Timestamp dtTs = BigQueryTypeRegistry.convert((LocalDateTime) element, Timestamp.class); return BigQueryTypeRegistry.convert(dtTs, String.class); case TIMESTAMP: // Arrow gives TIMESTAMP as a Long (microseconds since epoch) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java index d9f0160b3f5e..c55114461733 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java @@ -19,9 +19,9 @@ import static com.google.cloud.bigquery.jdbc.BigQueryBaseArray.isArray; import com.google.cloud.bigquery.Field; -import java.sql.SQLException; import com.google.cloud.bigquery.FieldList; import java.lang.reflect.Array; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.apache.arrow.vector.util.JsonStringArrayList; @@ -32,7 +32,6 @@ */ class BigQueryArrowStruct extends BigQueryBaseStruct { - private final FieldList schema; private final JsonStringHashMap values; diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java index 7943f229cb87..56f0046759be 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java @@ -22,8 +22,8 @@ import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.FieldList; import com.google.cloud.bigquery.FieldValue; -import java.sql.SQLException; import java.lang.reflect.Array; +import java.sql.SQLException; import java.util.List; /** @@ -32,7 +32,6 @@ @InternalApi class BigQueryJsonStruct extends BigQueryBaseStruct { - private final FieldList schema; private final List values; @@ -69,7 +68,8 @@ public Object[] getAttributes() throws SQLException { private Object getValue(Field currentSchema, Object currentValue) throws SQLException { LOG.finestTrace("getValue"); if (isArray(currentSchema)) { - return new BigQueryJsonArray(currentSchema, (FieldValue) currentValue, this.LOG.getJsonArrayLogger()); + return new BigQueryJsonArray( + currentSchema, (FieldValue) currentValue, this.LOG.getJsonArrayLogger()); } else if (isStruct(currentSchema)) { return new BigQueryJsonStruct( currentSchema.getSubFields(), (FieldValue) currentValue, this.LOG.getJsonStructLogger()); From 962b62b58dd38d58f07d8028053b3d5e2ad3a353 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Wed, 12 Aug 2026 21:14:46 -0400 Subject: [PATCH 3/6] Merge branch 'jdbc-phase4-registry-integration' into jdbc-phase4-registry-read-path --- .../cloud/bigquery/jdbc/BigQueryArrowResultSet.java | 10 ++-------- .../cloud/bigquery/jdbc/BigQueryArrowStruct.java | 6 ++---- .../google/cloud/bigquery/jdbc/BigQueryBaseArray.java | 6 ++---- .../cloud/bigquery/jdbc/BigQueryJsonResultSet.java | 10 ++-------- .../google/cloud/bigquery/jdbc/BigQueryJsonStruct.java | 6 ++---- .../cloud/bigquery/jdbc/BigQueryTypeRegistry.java | 2 ++ .../bigquery/jdbc/BigQueryDatabaseMetaDataTest.java | 2 +- 7 files changed, 13 insertions(+), 29 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java index 11d208f99cea..660f363a3317 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java @@ -368,10 +368,7 @@ public Object getObject(int columnIndex) throws SQLException { (JsonStringHashMap) value, this.LOG.getArrowStructLogger()); } - Class targetClass = - BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( - arrayField.getType().getStandardType()); - return BigQueryTypeRegistry.convert(value, targetClass); + return BigQueryTypeRegistry.convert(value, arrayField.getType().getStandardType(), null); } int fieldIndex = this.isNested ? 0 : columnIndex - 1; @@ -437,10 +434,7 @@ public Object getObject(int columnIndex) throws SQLException { // Strip trailing zeros to match JSON API and CLI output return ((BigDecimal) value).stripTrailingZeros(); } - Class targetClass = - BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( - fieldSchema.getType().getStandardType()); - return BigQueryTypeRegistry.convert(value, targetClass); + return BigQueryTypeRegistry.convert(value, fieldSchema.getType().getStandardType(), null); } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java index c55114461733..62a003a1fb28 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java @@ -83,10 +83,8 @@ private Object getValue(Field currentSchema, Object currentValue) throws SQLExce (JsonStringHashMap) currentValue, this.LOG.getArrowStructLogger()); } else { - Class targetClass = - BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( - currentSchema.getType().getStandardType()); - return BigQueryTypeRegistry.convert(currentValue, targetClass); + return BigQueryTypeRegistry.convert( + currentValue, currentSchema.getType().getStandardType(), null); } } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java index d58847ab4946..d677f3182e8e 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryBaseArray.java @@ -65,8 +65,7 @@ public final String getBaseTypeName() { public final int getBaseType() { LOG.finestTrace("getBaseType"); ensureValid(); - return BigQueryJdbcTypeMappings.standardSQLToJavaSqlTypesMapping.get( - schema.getType().getStandardType()); + return BigQueryTypeRegistry.toJdbcType(schema.getType().getStandardType()); } @Override @@ -145,8 +144,7 @@ protected Class getTargetClass() { LOG.finestTrace("getTargetClass"); return this.arrayOfStruct ? Struct.class - : BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( - this.schema.getType().getStandardType()); + : BigQueryTypeRegistry.toJavaClass(this.schema.getType().getStandardType()); } abstract Object getCoercedValue(int index) throws SQLException; diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java index a5bd1ded6107..e08b87751fbb 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonResultSet.java @@ -234,10 +234,7 @@ public Object getObject(int columnIndex) throws SQLException { return new BigQueryJsonStruct( arrayField.getSubFields(), value, this.LOG.getJsonStructLogger()); } - Class targetClass = - BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( - arrayField.getType().getStandardType()); - return BigQueryTypeRegistry.convert(value, targetClass); + return BigQueryTypeRegistry.convert(value, arrayField.getType().getStandardType(), null); } int extraIndex = this.isNested ? 2 : 1; @@ -248,10 +245,7 @@ public Object getObject(int columnIndex) throws SQLException { return new BigQueryJsonStruct( fieldSchema.getSubFields(), value, this.LOG.getJsonStructLogger()); } else { - Class targetClass = - BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( - fieldSchema.getType().getStandardType()); - return BigQueryTypeRegistry.convert(value, targetClass); + return BigQueryTypeRegistry.convert(value, fieldSchema.getType().getStandardType(), null); } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java index 56f0046759be..c463419f6f1b 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java @@ -74,10 +74,8 @@ private Object getValue(Field currentSchema, Object currentValue) throws SQLExce return new BigQueryJsonStruct( currentSchema.getSubFields(), (FieldValue) currentValue, this.LOG.getJsonStructLogger()); } else { - Class targetClass = - BigQueryJdbcTypeMappings.standardSQLToJavaTypeMapping.get( - currentSchema.getType().getStandardType()); - return BigQueryTypeRegistry.convert(currentValue, targetClass); + return BigQueryTypeRegistry.convert( + currentValue, currentSchema.getType().getStandardType(), null); } } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java index 6005a78ccce1..301be4856e2c 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java @@ -408,10 +408,12 @@ public static Class toJavaClass(StandardSQLTypeName bqType) { int ordinal = bqType.ordinal(); if (ordinal >= DESCRIPTORS_BY_ORDINAL.length || DESCRIPTORS_BY_ORDINAL[ordinal] == null) { return String.class; + } return DESCRIPTORS_BY_ORDINAL[ordinal].getDefaultJavaClass(); } + /** * Returns the standard Java Class equivalent for a given JDBC SQL type. * diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java index 77e3e08f1a70..5b58b80f2765 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryDatabaseMetaDataTest.java @@ -3280,7 +3280,7 @@ public void testMetadataAndResultSetMetadataTypeMappingConsistency(StandardSQLTy } ColumnTypeInfo metadataTypeInfo = dbMetadata.mapBigQueryTypeToJdbc(field); - Integer resultSetType = BigQueryJdbcTypeMappings.standardSQLToJavaSqlTypesMapping.get(type); + Integer resultSetType = BigQueryTypeRegistry.toJdbcType(type); assertNotNull(resultSetType, "ResultSet mapping should exist for " + type); assertEquals( From aefad3b31445fc7072c7c4db31212f4995cfa1d5 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Thu, 13 Aug 2026 12:42:50 -0400 Subject: [PATCH 4/6] address bugs --- .../bigquery/jdbc/BigQueryJsonStruct.java | 7 ++-- .../jdbc/BigQueryTemporalUtility.java | 39 ++++++++++++++++++- .../bigquery/jdbc/BigQueryTypeRegistry.java | 38 ++++++++++++++++-- .../BigQueryJsonArrayOfPrimitivesTest.java | 8 ++-- .../bigquery/jdbc/BigQueryJsonStructTest.java | 2 +- 5 files changed, 81 insertions(+), 13 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java index c463419f6f1b..c8129f41b88b 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStruct.java @@ -65,14 +65,13 @@ public Object[] getAttributes() throws SQLException { return attributes; } - private Object getValue(Field currentSchema, Object currentValue) throws SQLException { + private Object getValue(Field currentSchema, FieldValue currentValue) throws SQLException { LOG.finestTrace("getValue"); if (isArray(currentSchema)) { - return new BigQueryJsonArray( - currentSchema, (FieldValue) currentValue, this.LOG.getJsonArrayLogger()); + return new BigQueryJsonArray(currentSchema, currentValue, this.LOG.getJsonArrayLogger()); } else if (isStruct(currentSchema)) { return new BigQueryJsonStruct( - currentSchema.getSubFields(), (FieldValue) currentValue, this.LOG.getJsonStructLogger()); + currentSchema.getSubFields(), currentValue, this.LOG.getJsonStructLogger()); } else { return BigQueryTypeRegistry.convert( currentValue, currentSchema.getType().getStandardType(), null); diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTemporalUtility.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTemporalUtility.java index b26cf78bac0a..db7912ff4b6a 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTemporalUtility.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTemporalUtility.java @@ -16,6 +16,7 @@ package com.google.cloud.bigquery.jdbc; +import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; @@ -94,6 +95,21 @@ public static Time boxTime(String val, ZoneId zoneId) { * the Calendar timezone is explicitly ignored per JDBC 4.2 spec. */ public static Timestamp boxTimestamp(String val) { + // Check if the value is a numeric float string (e.g. "1680174859.8202269" from JSON API) + try { + if (val.indexOf('-') < 0 + || (val.startsWith("-") + && val.indexOf('-', 1) < 0)) { // Quick check to ensure it's not a date string + BigDecimal bd = new BigDecimal(val); + long secondsLong = bd.longValue(); + int nanos = bd.remainder(BigDecimal.ONE).multiply(new BigDecimal(1_000_000_000)).intValue(); + Timestamp ts = new Timestamp(secondsLong * 1000L); + ts.setNanos(nanos); + return ts; + } + } catch (NumberFormatException ignored) { + } + String iso = val; // Handle the " UTC" suffix format if (iso.endsWith(" UTC")) { @@ -104,12 +120,33 @@ public static Timestamp boxTimestamp(String val) { if (iso.length() > 10 && iso.charAt(10) == ' ') { iso = iso.substring(0, 10) + 'T' + iso.substring(11); } + // If it doesn't have a timezone designator, assume UTC 'Z' + if (!iso.endsWith("Z") && !iso.contains("+") && iso.lastIndexOf('-') <= 10) { + iso = iso + "Z"; + } try { return Timestamp.from(Instant.parse(iso)); } catch (java.time.format.DateTimeParseException e) { // Fallback for non-standard formats - return Timestamp.valueOf(val); + String fallback = val; + if (fallback.indexOf('T') > 0) { + fallback = fallback.replace('T', ' '); + } + return Timestamp.valueOf(fallback); } } + + /** + * Converts milliseconds of the day to a local epoch millis anchored to 1970-01-01 in the given + * timezone. + */ + public static long getLocalMillis(long millisOfDay, ZoneId zoneId) { + ZoneId targetZone = zoneId != null ? zoneId : ZoneId.systemDefault(); + return LocalTime.ofNanoOfDay(millisOfDay * 1_000_000L) + .atDate(LocalDate.of(1970, 1, 1)) + .atZone(targetZone) + .toInstant() + .toEpochMilli(); + } } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java index 933e1232f001..fe5899f9a1c0 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java @@ -16,6 +16,7 @@ package com.google.cloud.bigquery.jdbc; +import com.google.cloud.bigquery.FieldValue; import com.google.cloud.bigquery.StandardSQLTypeName; import com.google.cloud.bigquery.exception.BigQueryJdbcException; import com.google.cloud.bigquery.exception.BigQueryJdbcSqlFeatureNotSupportedException; @@ -35,6 +36,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Arrays; +import java.util.Base64; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -157,6 +159,11 @@ static TypeDescriptor createDateDescriptor() { else if (val instanceof java.util.Date) sqlDate = new Date(((java.util.Date) val).getTime()); else if (val instanceof LocalDate) sqlDate = Date.valueOf((LocalDate) val); + else if (val instanceof Integer) + sqlDate = Date.valueOf(LocalDate.ofEpochDay(((Integer) val).longValue())); + else if (val instanceof Long) sqlDate = Date.valueOf(LocalDate.ofEpochDay((Long) val)); + else if (val instanceof LocalDateTime) + sqlDate = Date.valueOf(((LocalDateTime) val).toLocalDate()); else if (val instanceof String) sqlDate = BigQueryTemporalUtility.boxDate((String) val, zone); else throw new BigQueryJdbcException("Cannot convert to DATE: " + val); @@ -205,6 +212,12 @@ else if (val instanceof OffsetDateTime) ts = Timestamp.from(((OffsetDateTime) val).toInstant()); else if (val instanceof ZonedDateTime) ts = Timestamp.from(((ZonedDateTime) val).toInstant()); + else if (val instanceof LocalDateTime) + ts = Timestamp.from(((LocalDateTime) val).toInstant(java.time.ZoneOffset.UTC)); + else if (val instanceof Long) + ts = + Timestamp.from( + Instant.EPOCH.plus((Long) val, java.time.temporal.ChronoUnit.MICROS)); else if (val instanceof String) ts = BigQueryTemporalUtility.boxTimestamp((String) val); else throw new BigQueryJdbcException("Cannot convert to TIMESTAMP: " + val); @@ -234,7 +247,14 @@ static TypeDescriptor createTimeDescriptor() { else if (val instanceof java.util.Date) sqlTime = new Time(((java.util.Date) val).getTime()); else if (val instanceof LocalTime) sqlTime = Time.valueOf((LocalTime) val); - else if (val instanceof String) + else if (val instanceof LocalDateTime) { + long millisOfDay = ((LocalDateTime) val).toLocalTime().toNanoOfDay() / 1_000_000; + sqlTime = new Time(BigQueryTemporalUtility.getLocalMillis(millisOfDay, zone)); + } else if (val instanceof Long) { + long millisOfDay = (Long) val / 1000; + // Align with civil time anchoring + sqlTime = new Time(BigQueryTemporalUtility.getLocalMillis(millisOfDay, zone)); + } else if (val instanceof String) sqlTime = BigQueryTemporalUtility.boxTime((String) val, zone); else throw new BigQueryJdbcException("Cannot convert to TIME: " + val); @@ -256,6 +276,7 @@ static TypeDescriptor createBytesDescriptor() { Arrays.asList(byte[].class), (val, targetClass, zone) -> { if (val instanceof byte[]) return val; + else if (val instanceof String) return Base64.getDecoder().decode((String) val); throw new BigQueryJdbcException("Cannot convert to BYTES: " + val); }); } @@ -418,12 +439,10 @@ public static Class toJavaClass(StandardSQLTypeName bqType) { int ordinal = bqType.ordinal(); if (ordinal >= DESCRIPTORS_BY_ORDINAL.length || DESCRIPTORS_BY_ORDINAL[ordinal] == null) { return String.class; - } return DESCRIPTORS_BY_ORDINAL[ordinal].getDefaultJavaClass(); } - /** * Returns the standard Java Class equivalent for a given JDBC SQL type. * @@ -449,6 +468,14 @@ public static T convert(Object input, Class targetClass) throws BigQueryJ if (input == null) { return null; } + if (input instanceof FieldValue) { + FieldValue fv = (FieldValue) input; + if (fv.isNull()) return null; + input = fv.getValue(); + } + if (targetClass.isInstance(input)) { + return (T) input; + } TypeDescriptor descriptor = getDescriptorForClass(targetClass); if (descriptor == null) { throw new BigQueryJdbcException("Unsupported target class: " + targetClass.getName()); @@ -468,6 +495,11 @@ public static T convert(Object input, Class targetClass) throws BigQueryJ public static Object convert(Object input, StandardSQLTypeName bqType, ZoneId zoneId) throws BigQueryJdbcException { if (input == null) return null; + if (input instanceof FieldValue) { + FieldValue fv = (FieldValue) input; + if (fv.isNull()) return null; + input = fv.getValue(); + } int ordinal = bqType.ordinal(); if (ordinal >= DESCRIPTORS_BY_ORDINAL.length || DESCRIPTORS_BY_ORDINAL[ordinal] == null) { throw new BigQueryJdbcException("No type descriptor registered for BigQuery type: " + bqType); diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArrayOfPrimitivesTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArrayOfPrimitivesTest.java index 537e20b60fea..30a66a2bf5fd 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArrayOfPrimitivesTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonArrayOfPrimitivesTest.java @@ -127,10 +127,10 @@ public static Collection data() { TIMESTAMP, arraySchemaAndValue( TIMESTAMP, - "1680174859.8202269", - "1680261259.8202269", - "1680347659.8202269", - "1680434059.8202269"), + "1680174859.820227", + "1680261259.820227", + "1680347659.820227", + "1680434059.820227"), new Timestamp[] { Timestamp.valueOf(aTimeStamp), // 2023-03-30 16:44:19.82 Timestamp.valueOf(aTimeStamp.plusDays(1)), diff --git a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStructTest.java b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStructTest.java index ae074fa19e84..5c8deeab85e3 100644 --- a/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStructTest.java +++ b/java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryJsonStructTest.java @@ -241,7 +241,7 @@ public void structOfStructs() throws SQLException { public void structWithNullValue() throws SQLException { assertThat(structWithNullValue.getAttributes()) .isEqualTo( - Arrays.asList(0L, false, 0.0, null, null, null, null, null, null, null, null, null) + Arrays.asList(null, null, null, null, null, null, null, null, null, null, null, null) .toArray()); } From c5253821d969f74c3083ecf2d18bb50aa7d46a77 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Thu, 13 Aug 2026 14:03:09 -0400 Subject: [PATCH 5/6] fix date --- .../bigquery/jdbc/BigQueryArrowArray.java | 6 ++ .../bigquery/jdbc/BigQueryArrowResultSet.java | 9 ++ .../bigquery/jdbc/BigQueryArrowStruct.java | 6 ++ .../bigquery/jdbc/BigQueryTypeRegistry.java | 91 +++++++++++++++++-- 4 files changed, 103 insertions(+), 9 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java index 81ef40d04d0e..2a8336768470 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowArray.java @@ -19,8 +19,10 @@ import com.google.cloud.Tuple; import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.Schema; +import com.google.cloud.bigquery.StandardSQLTypeName; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.LocalDate; import org.apache.arrow.vector.util.JsonStringArrayList; import org.apache.arrow.vector.util.JsonStringHashMap; @@ -100,6 +102,10 @@ public void free() { Object getCoercedValue(int index) throws SQLException { LOG.finestTrace("getCoercedValue"); Object value = this.values.get(index); + if (value instanceof Integer + && schema.getType().getStandardType() == StandardSQLTypeName.DATE) { + value = LocalDate.ofEpochDay(((Integer) value).longValue()); + } return this.arrayOfStruct ? new BigQueryArrowStruct( schema.getSubFields(), (JsonStringHashMap) value, this.LOG.getArrowStructLogger()) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java index 660f363a3317..120500cc2007 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowResultSet.java @@ -35,6 +35,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -42,6 +43,7 @@ import java.util.concurrent.Future; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; +import org.apache.arrow.vector.DateDayVector; import org.apache.arrow.vector.FieldVector; import org.apache.arrow.vector.VectorLoader; import org.apache.arrow.vector.VectorSchemaRoot; @@ -340,6 +342,9 @@ private Object getObjectInternal(int columnIndex) throws SQLException { FieldVector currentColumn = this.vectorSchemaRoot.getVector(columnIndex - 1); // get the current row value = currentColumn.getObject(this.currentBatchRowIndex); + if (value instanceof Integer && currentColumn instanceof DateDayVector) { + value = LocalDate.ofEpochDay(((Integer) value).longValue()); + } } setWasNull(value); return value; @@ -368,6 +373,10 @@ public Object getObject(int columnIndex) throws SQLException { (JsonStringHashMap) value, this.LOG.getArrowStructLogger()); } + if (value instanceof Integer + && arrayField.getType().getStandardType() == StandardSQLTypeName.DATE) { + value = LocalDate.ofEpochDay(((Integer) value).longValue()); + } return BigQueryTypeRegistry.convert(value, arrayField.getType().getStandardType(), null); } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java index 62a003a1fb28..375c0619703a 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryArrowStruct.java @@ -20,8 +20,10 @@ import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.FieldList; +import com.google.cloud.bigquery.StandardSQLTypeName; import java.lang.reflect.Array; import java.sql.SQLException; +import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import org.apache.arrow.vector.util.JsonStringArrayList; @@ -83,6 +85,10 @@ private Object getValue(Field currentSchema, Object currentValue) throws SQLExce (JsonStringHashMap) currentValue, this.LOG.getArrowStructLogger()); } else { + if (currentValue instanceof Integer + && currentSchema.getType().getStandardType() == StandardSQLTypeName.DATE) { + currentValue = LocalDate.ofEpochDay(((Integer) currentValue).longValue()); + } return BigQueryTypeRegistry.convert( currentValue, currentSchema.getType().getStandardType(), null); } diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java index fe5899f9a1c0..bde49b32855c 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java @@ -17,6 +17,7 @@ package com.google.cloud.bigquery.jdbc; import com.google.cloud.bigquery.FieldValue; +import com.google.cloud.bigquery.Range; import com.google.cloud.bigquery.StandardSQLTypeName; import com.google.cloud.bigquery.exception.BigQueryJdbcException; import com.google.cloud.bigquery.exception.BigQueryJdbcSqlFeatureNotSupportedException; @@ -28,17 +29,20 @@ import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; +import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; +import java.time.Period; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Base64; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.arrow.vector.PeriodDuration; /** * A central, bidirectional engine for resolving and coercing types between JDBC, Java, and @@ -82,6 +86,7 @@ static TypeDescriptor createBoolDescriptor() { Arrays.asList(Boolean.class), (val, targetClass, zone) -> { if (val instanceof Boolean) return val; + if (val instanceof Number) return ((Number) val).longValue() != 0; if (val instanceof String) return Boolean.parseBoolean((String) val); throw new BigQueryJdbcException("Cannot convert to BOOL: " + val); }); @@ -93,7 +98,51 @@ static TypeDescriptor createStringDescriptor() { String.class, StandardSQLTypeName.STRING, Arrays.asList(String.class), - (val, targetClass, zone) -> String.valueOf(val)); + (val, targetClass, zone) -> { + if (val == null) return null; + if (val instanceof byte[]) return Base64.getEncoder().encodeToString((byte[]) val); + if (val instanceof Range) { + Range range = (Range) val; + String start = + range.getStart().isNull() ? "UNBOUNDED" : range.getStart().getStringValue(); + String end = range.getEnd().isNull() ? "UNBOUNDED" : range.getEnd().getStringValue(); + return String.format("[%s, %s)", start, end); + } + if (val instanceof PeriodDuration) { + PeriodDuration pd = (PeriodDuration) val; + Period period = pd.getPeriod().normalized(); + StringBuilder builder = new StringBuilder(); + builder + .append(period.getYears()) + .append("-") + .append(period.getMonths()) + .append(" ") + .append(period.getDays()) + .append(" "); + Duration duration = pd.getDuration(); + if (duration.isNegative()) { + builder.append("-"); + duration = duration.negated(); + } + long hours = duration.toHours(); + duration = duration.minusHours(hours); + long minutes = duration.toMinutes(); + duration = duration.minusMinutes(minutes); + long seconds = duration.getSeconds(); + duration = duration.minusSeconds(seconds); + long microseconds = duration.toNanos() / 1000; + builder + .append(hours) + .append(":") + .append(minutes) + .append(":") + .append(seconds) + .append(".") + .append(microseconds); + return builder.toString().replaceFirst("--", "-"); + } + return String.valueOf(val); + }); } static TypeDescriptor createInt64Descriptor() { @@ -104,13 +153,35 @@ static TypeDescriptor createInt64Descriptor() { Arrays.asList(Long.class, Integer.class, Short.class, Byte.class), (val, targetClass, zone) -> { long longVal; - if (val instanceof Number) longVal = ((Number) val).longValue(); - else if (val instanceof String) longVal = Long.parseLong((String) val); - else throw new BigQueryJdbcException("Cannot convert to INT64: " + val); + if (val instanceof Number) { + if (val instanceof BigDecimal) { + longVal = ((BigDecimal) val).longValueExact(); + } else { + longVal = ((Number) val).longValue(); + } + } else if (val instanceof String) { + longVal = Long.parseLong((String) val); + } else if (val instanceof Boolean) { + longVal = (Boolean) val ? 1L : 0L; + } else { + throw new BigQueryJdbcException("Cannot convert to INT64: " + val); + } - if (targetClass == Integer.class) return (int) longVal; - if (targetClass == Short.class) return (short) longVal; - if (targetClass == Byte.class) return (byte) longVal; + if (targetClass == Integer.class) { + if (longVal > Integer.MAX_VALUE || longVal < Integer.MIN_VALUE) + throw new BigQueryJdbcException("Value out of range for Integer: " + longVal); + return (int) longVal; + } + if (targetClass == Short.class) { + if (longVal > Short.MAX_VALUE || longVal < Short.MIN_VALUE) + throw new BigQueryJdbcException("Value out of range for Short: " + longVal); + return (short) longVal; + } + if (targetClass == Byte.class) { + if (longVal > Byte.MAX_VALUE || longVal < Byte.MIN_VALUE) + throw new BigQueryJdbcException("Value out of range for Byte: " + longVal); + return (byte) longVal; + } return longVal; }); } @@ -125,6 +196,7 @@ static TypeDescriptor createFloat64Descriptor() { double doubleVal; if (val instanceof Number) doubleVal = ((Number) val).doubleValue(); else if (val instanceof String) doubleVal = Double.parseDouble((String) val); + else if (val instanceof Boolean) doubleVal = (Boolean) val ? 1.0 : 0.0; else throw new BigQueryJdbcException("Cannot convert to FLOAT64: " + val); if (targetClass == Float.class) return (float) doubleVal; @@ -142,6 +214,9 @@ static TypeDescriptor createNumericDescriptor() { if (val instanceof BigDecimal) return val; if (val instanceof Number) return new BigDecimal(val.toString()); if (val instanceof String) return new BigDecimal((String) val); + if (val instanceof Boolean) { + return (Boolean) val ? BigDecimal.ONE : BigDecimal.ZERO; + } throw new BigQueryJdbcException("Cannot convert to NUMERIC: " + val); }); } @@ -159,8 +234,6 @@ static TypeDescriptor createDateDescriptor() { else if (val instanceof java.util.Date) sqlDate = new Date(((java.util.Date) val).getTime()); else if (val instanceof LocalDate) sqlDate = Date.valueOf((LocalDate) val); - else if (val instanceof Integer) - sqlDate = Date.valueOf(LocalDate.ofEpochDay(((Integer) val).longValue())); else if (val instanceof Long) sqlDate = Date.valueOf(LocalDate.ofEpochDay((Long) val)); else if (val instanceof LocalDateTime) sqlDate = Date.valueOf(((LocalDateTime) val).toLocalDate()); From ece2ee66e123e0632e82fa33d090395ef64f6532 Mon Sep 17 00:00:00 2001 From: Neenu1995 Date: Thu, 13 Aug 2026 14:40:36 -0400 Subject: [PATCH 6/6] fix date --- .../bigquery/jdbc/BigQueryTypeRegistry.java | 107 ++++++++++-------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java index bde49b32855c..b3cb54977fe1 100644 --- a/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java +++ b/java-bigquery-jdbc/src/main/java/com/google/cloud/bigquery/jdbc/BigQueryTypeRegistry.java @@ -101,45 +101,13 @@ static TypeDescriptor createStringDescriptor() { (val, targetClass, zone) -> { if (val == null) return null; if (val instanceof byte[]) return Base64.getEncoder().encodeToString((byte[]) val); - if (val instanceof Range) { - Range range = (Range) val; - String start = - range.getStart().isNull() ? "UNBOUNDED" : range.getStart().getStringValue(); - String end = range.getEnd().isNull() ? "UNBOUNDED" : range.getEnd().getStringValue(); - return String.format("[%s, %s)", start, end); + if (val instanceof Timestamp) { + return java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS") + .format(((Timestamp) val).toLocalDateTime()); } - if (val instanceof PeriodDuration) { - PeriodDuration pd = (PeriodDuration) val; - Period period = pd.getPeriod().normalized(); - StringBuilder builder = new StringBuilder(); - builder - .append(period.getYears()) - .append("-") - .append(period.getMonths()) - .append(" ") - .append(period.getDays()) - .append(" "); - Duration duration = pd.getDuration(); - if (duration.isNegative()) { - builder.append("-"); - duration = duration.negated(); - } - long hours = duration.toHours(); - duration = duration.minusHours(hours); - long minutes = duration.toMinutes(); - duration = duration.minusMinutes(minutes); - long seconds = duration.getSeconds(); - duration = duration.minusSeconds(seconds); - long microseconds = duration.toNanos() / 1000; - builder - .append(hours) - .append(":") - .append(minutes) - .append(":") - .append(seconds) - .append(".") - .append(microseconds); - return builder.toString().replaceFirst("--", "-"); + if (val instanceof Time) { + return java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss.SSS") + .format(((Time) val).toLocalTime()); } return String.valueOf(val); }); @@ -154,11 +122,7 @@ static TypeDescriptor createInt64Descriptor() { (val, targetClass, zone) -> { long longVal; if (val instanceof Number) { - if (val instanceof BigDecimal) { - longVal = ((BigDecimal) val).longValueExact(); - } else { - longVal = ((Number) val).longValue(); - } + longVal = ((Number) val).longValue(); } else if (val instanceof String) { longVal = Long.parseLong((String) val); } else if (val instanceof Boolean) { @@ -234,7 +198,6 @@ static TypeDescriptor createDateDescriptor() { else if (val instanceof java.util.Date) sqlDate = new Date(((java.util.Date) val).getTime()); else if (val instanceof LocalDate) sqlDate = Date.valueOf((LocalDate) val); - else if (val instanceof Long) sqlDate = Date.valueOf(LocalDate.ofEpochDay((Long) val)); else if (val instanceof LocalDateTime) sqlDate = Date.valueOf(((LocalDateTime) val).toLocalDate()); else if (val instanceof String) @@ -317,7 +280,11 @@ static TypeDescriptor createTimeDescriptor() { Time sqlTime; if (val instanceof Time) sqlTime = (Time) val; - else if (val instanceof java.util.Date) + else if (val instanceof Timestamp) { + sqlTime = + Time.valueOf( + ((Timestamp) val).toInstant().atOffset(java.time.ZoneOffset.UTC).toLocalTime()); + } else if (val instanceof java.util.Date) sqlTime = new Time(((java.util.Date) val).getTime()); else if (val instanceof LocalTime) sqlTime = Time.valueOf((LocalTime) val); else if (val instanceof LocalDateTime) { @@ -416,7 +383,43 @@ static TypeDescriptor createIntervalDescriptor() { String.class, StandardSQLTypeName.INTERVAL, Arrays.asList(String.class), - (val, targetClass, zone) -> String.valueOf(val)); + (val, targetClass, zone) -> { + if (val == null) return null; + if (val instanceof PeriodDuration) { + PeriodDuration pd = (PeriodDuration) val; + Period period = pd.getPeriod().normalized(); + StringBuilder builder = new StringBuilder(); + builder + .append(period.getYears()) + .append("-") + .append(period.getMonths()) + .append(" ") + .append(period.getDays()) + .append(" "); + Duration duration = pd.getDuration(); + if (duration.isNegative()) { + builder.append("-"); + duration = duration.negated(); + } + long hours = duration.toHours(); + duration = duration.minusHours(hours); + long minutes = duration.toMinutes(); + duration = duration.minusMinutes(minutes); + long seconds = duration.getSeconds(); + duration = duration.minusSeconds(seconds); + long microseconds = duration.toNanos() / 1000; + builder + .append(hours) + .append(":") + .append(minutes) + .append(":") + .append(seconds) + .append(".") + .append(microseconds); + return builder.toString().replaceFirst("--", "-"); + } + return String.valueOf(val); + }); } static TypeDescriptor createRangeDescriptor() { @@ -425,7 +428,17 @@ static TypeDescriptor createRangeDescriptor() { String.class, StandardSQLTypeName.RANGE, Arrays.asList(String.class), - (val, targetClass, zone) -> String.valueOf(val)); + (val, targetClass, zone) -> { + if (val == null) return null; + if (val instanceof Range) { + Range range = (Range) val; + String start = + range.getStart().isNull() ? "UNBOUNDED" : range.getStart().getStringValue(); + String end = range.getEnd().isNull() ? "UNBOUNDED" : range.getEnd().getStringValue(); + return String.format("[%s, %s)", start, end); + } + return String.valueOf(val); + }); } private static void register(TypeDescriptor descriptor) {