Skip to content

fix(bigquery-jdbc): prepare Arrow result set for job-stream data - #14941

Open
keshavdandeva wants to merge 3 commits into
jdbc/arrow-support-featurefrom
fix/jdbc-arrow-resultset-hardening
Open

keshavdandeva wants to merge 3 commits into
jdbc/arrow-support-featurefrom
fix/jdbc-arrow-resultset-hardening

Conversation

@keshavdandeva

@keshavdandeva keshavdandeva commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

b/582676539

Prepares the Arrow reader for the job stream (b/581544197).

  • Producer skips Read API responses without rows, fails fast when a response reports rows but has no record batch, and forwards a schema sent in the stream.
  • Result set skips zero-row batches and builds its decoder from the first schema it sees.
  • isBeforeFirst()/isLast() return false for an empty result.

…n row counts and lazy schema

- Producer skips Read API responses without rows and fails fast when a
  response reports rows but carries no record batch.
- Result set skips zero-row batches instead of returning a phantom row.
- Support an unknown total row count: iterate until the end marker;
  isLast()/isBeforeFirst() look ahead without skipping rows.
- Accept the Arrow schema from the first stream message.
- Fix isLast() when Statement.setMaxRows exceeds the result size.
- isBeforeFirst()/isLast() return false for an empty result (JDBC spec).
- Getters raise SQLException instead of IndexOutOfBoundsException or a
  stale value when the cursor is not on a row.
- Deserialize record batches without copying them to a heap byte array.

Bug: b/582676539
@keshavdandeva
keshavdandeva requested review from a team as code owners November 6, 2026 16:28

@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 refactors the BigQuery JDBC driver's Arrow result set processing to support streams with an unknown total row count, skip empty batches, and dynamically forward schemas. It also optimizes memory usage during Arrow batch deserialization by reading directly from the ByteString input stream instead of copying it to a heap byte array. Feedback is provided to wrap the newly introduced InputStream and ReadableByteChannel in a try-with-resources block to prevent potential static analysis warnings regarding unclosed resources.

ArrowRecordBatch currentBatch = response.getArrowRecordBatch();
arrowBatchWrapperBlockingQueue.put(
BigQueryArrowBatchWrapper.of(currentBatch));
enqueueReadRowsResponse(response, arrowBatchWrapperBlockingQueue);

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.

What is the impact of this change?

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.

For today's read-session path there's no observable change: every message we've seen has rows and a batch, and those are queued exactly as before. It only changes behaviour for messages we haven't seen yet. Messages without rows (allowed by ReadRowsResponse) are no longer queued, so the reader can't turn them into a phantom row. A message that reports rows but has no batch now fails instead of silently dropping those rows. It also passes on a schema sent within the stream, which the job stream needs.

/** This class acts as a facade layer and wraps Arrow's VectorSchemaRoot & JsonStringArrayList */
class BigQueryArrowBatchWrapper {
/** Row count used when the producer did not report one. */
static final long UNKNOWN_ROW_COUNT = -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.

What is a scenario for unknown # of rows?

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.

I added it originally because in early testing totalRows was missing from some jobs.query responses, and I assumed we might have to stream without it. Double-checking all the cases, it's only missing for DML/DDL (nothing to stream) and when the job hasn't finished yet, and in that case we wait first and the wait call always returns it. So there's no real scenario where we stream without a known row count. I've removed it, and isLast()/isBeforeFirst() are plain comparisons again.

@keshavdandeva keshavdandeva changed the title fix(bigquery-jdbc): harden Arrow result set for empty batches, unknown row counts and lazy schema fix(bigquery-jdbc): prepare Arrow result set for job-stream data Nov 9, 2026

This branch has not been deployed

No deployments
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