Skip to content

feat(bigquery-jdbc): implement TypeRegistry and TypeDescriptor - #13947

Merged
Neenu1995 merged 9 commits into
mainfrom
feat/type-registry-phase-1
Aug 7, 2026
Merged

feat(bigquery-jdbc): implement TypeRegistry and TypeDescriptor#13947
Neenu1995 merged 9 commits into
mainfrom
feat/type-registry-phase-1

Conversation

@Neenu1995

Copy link
Copy Markdown
Contributor

No description provided.

@Neenu1995
Neenu1995 requested review from a team as code owners July 29, 2026 16:44

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces BigQueryTypeRegistry and TypeDescriptor to handle bidirectional type mapping and coercion between JDBC, Java, and BigQuery. The review feedback highlights several key issues and improvements: a potential runtime exception in the TIMESTAMP coercer for Java 8 date/time types, precision loss when converting Number to BigDecimal using doubleValue(), and unused code involving DESCRIPTORS_BY_ORDINAL. Additionally, suggestions were made to expand conversion support for java.util.Date and Gson's JsonElement to make the registry more robust.

*/
final class BigQueryTypeRegistry {

private static final TypeDescriptor<?>[] DESCRIPTORS_BY_ORDINAL;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The DESCRIPTORS_BY_ORDINAL array is declared and populated but never read anywhere in the class. It should be removed to clean up unused code.

Comment on lines +49 to +55
int maxOrdinal = 0;
for (StandardSQLTypeName type : StandardSQLTypeName.values()) {
if (type.ordinal() > maxOrdinal) {
maxOrdinal = type.ordinal();
}
}
DESCRIPTORS_BY_ORDINAL = new TypeDescriptor<?>[maxOrdinal + 1];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The initialization of DESCRIPTORS_BY_ORDINAL is unused and can be removed.

Comment on lines +261 to +263
if (DESCRIPTORS_BY_ORDINAL[descriptor.getBqType().ordinal()] == null) {
DESCRIPTORS_BY_ORDINAL[descriptor.getBqType().ordinal()] = descriptor;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The registration of DESCRIPTORS_BY_ORDINAL is unused and can be removed.

@Neenu1995

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a type mapping and coercion system between JDBC, Java, and BigQuery types, consisting of BigQueryTimezoneUtility, BigQueryTypeRegistry, and TypeDescriptor. The feedback highlights several key areas for improvement: resolving a potential parsing bug in boxTimestamp for strings with timezone offsets, using legacy Calendar manipulation for java.sql.Time to handle DST correctly, preserving millisecond precision when converting to LocalTime, and removing the unused DESCRIPTORS_BY_ORDINAL array.


/**
* Returns the exact BigQuery StandardSQLTypeName for a given Java class. If no mapping is found,
* returns StandardSQLTypeName.STRING as a fallback.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need a fallback? Shouldn't we implement all possible conversions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We need the STRING fallback to preserve backward compatibility and prevent crashing legacy applications that rely on the driver's old behavior of silently stringifying unmapped objects so the BigQuery backend can parse them.

@Neenu1995
Neenu1995 requested a review from logachev July 30, 2026 16:59
@Neenu1995

Copy link
Copy Markdown
Contributor Author

/gcprun

* High-performance hotpath convert for ResultSets. Converts the input value using the default
* mapping for the given BigQuery type via O(1) array indexing.
*/
public static Object convert(Object input, StandardSQLTypeName bqType, ZoneId zoneId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: I'd add overrides without ZoneId. It is used only with date/time objects, but we have a lot of other conversions where zone is unused.


/**
* Converts a BigQuery civil DATE string into an absolute Date by anchoring it to midnight of the
* provided timezone (or JVM default if null).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So this is where I'm not 100% sure about the behavior.

We have 4 time-related types: timestamp, time, date, datetime.

Time, Date, Datetime are not timezone specific, so we should be reading them as-is. If it is stored as "12:30:00", it should remain "12:30:00" regardless of the JVM timezone or provided timezone.

Timestamp is the only value that needs to be adjusted to the timezone.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  1. java.sql.Date stores epoch millis under the hood, not civil year/month/day fields.
  2. getDate(col, cal) contract: Per JDBC spec, the returned java.sql.Date must represent midnight (00:00:00) in the provided Calendar's timezone.
  3. Prevents date shifting: Without anchoring to midnight in the target timezone, formatting or reading the date in that timezone would shift it to the wrong day (e.g., UTC midnight 2026-07-17 displays as 2026-07-16 20:00 in New York time).

(Note: Modern LocalDate via getObject(col, LocalDate.class) is already timezone-agnostic and read as-is without any conversion).

Returned Class / Method Underlying Representation Timezone Handling
getObject(col, LocalDate.class) java.time.LocalDate As-is (completely timezone-agnostic)
getObject(col, LocalTime.class) java.time.LocalTime As-is (completely timezone-agnostic)
getDate(col) java.sql.Date Anchored to 00:00:00 in JVM default timezone
getDate(col, Calendar cal) java.sql.Date Anchored to 00:00:00 in target Calendar timezone
getTimestamp(col, Calendar cal) java.sql.Timestamp Absolute instant adjusted to target Calendar timezone

@Neenu1995
Neenu1995 merged commit 0557e69 into main Aug 7, 2026
206 checks passed
@Neenu1995
Neenu1995 deleted the feat/type-registry-phase-1 branch August 7, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants