Skip to content

feat(bigquery): add createQueryArrowStream for inline jobs.query results - #9596

Merged
ohmayr merged 9 commits into
mainfrom
feat/bigquery-arrow-inline
Oct 16, 2026
Merged

ohmayr merged 9 commits into
mainfrom
feat/bigquery-arrow-inline

Conversation

@ohmayr

@ohmayr ohmayr commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Adds BigQuery#createQueryArrowStream(query) to stream query results as apache-arrow RecordBatch objects when all rows are returned inline by jobs.query (queryResultsFormat: 'ARROW').

  • Lazily loads apache-arrow and verifies that @google-cloud/bigquery-storage (>= 6.1.0) is installed.
  • Defaults jobCreationMode to JOB_CREATION_OPTIONAL unless configured on the request or client, and rejects writeDisposition: 'WRITE_APPEND' before sending the request.
  • Decodes inline arrowSchema and arrowRecordBatch IPC bytes into RecordBatch objects, emitting a 0-row RecordBatch preserving the schema (or a 0-column schema for statements without a result schema) when no rows are returned.
  • Marks createQueryArrowStream and ArrowRecordBatchStream as [Beta] (@beta), since the ARROW results format on jobs.query isn't generally available yet.

@ohmayr
ohmayr requested review from a team as code owners October 12, 2026 23:01
@product-auto-label product-auto-label Bot added the api: bigquery Issues related to the BigQuery API. label Oct 12, 2026
@ohmayr
ohmayr marked this pull request as draft October 12, 2026 23:04
@ohmayr
ohmayr added this pull request to stack #9597 October 12, 2026 23:04

@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 Apache Arrow support to the BigQuery client library, allowing users to stream query results as Arrow RecordBatch objects. It adds the necessary dependencies, implements the Arrow streaming pipeline, and exposes the createQueryArrowStream method on the BigQuery class. The feedback suggests addressing potential silent data loss by explicitly handling asynchronous query jobs and paginated results, as well as simplifying the dependency verification logic.

Comment thread handwritten/bigquery/src/arrow.ts Outdated
Comment thread handwritten/bigquery/src/arrow.ts Outdated
@ohmayr
ohmayr force-pushed the feat/bigquery-arrow-inline branch 7 times, most recently from 71dff93 to af4b542 Compare October 13, 2026 04:30
@ohmayr
ohmayr force-pushed the feat/bigquery-arrow-inline branch from af4b542 to 36ae146 Compare October 13, 2026 08:41
@ohmayr
ohmayr force-pushed the feat/bigquery-arrow-inline branch from 36ae146 to 2ec010b Compare October 13, 2026 09:33
Base automatically changed from feat/bigquery-arrow-storage to main October 13, 2026 10:27
@ohmayr
ohmayr force-pushed the feat/bigquery-arrow-inline branch 2 times, most recently from bed860e to a756517 Compare October 13, 2026 10:42
@ohmayr
ohmayr force-pushed the feat/bigquery-arrow-inline branch 3 times, most recently from a9ab952 to 9b3d5e9 Compare October 13, 2026 19:26
@ohmayr
ohmayr marked this pull request as ready for review October 13, 2026 20:19
Comment thread handwritten/bigquery/src/arrow.ts Outdated
Comment thread handwritten/bigquery/package.json
"@google-cloud/paginator": "^7.0.0",
"@google-cloud/precise-date": "^6.0.0",
"@google-cloud/promisify": "^6.0.0",
"apache-arrow": "^21.0.0",

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.

All arrow dependencies (apache-arrow and @google-cloud/bigquery-storage) should be under peerDependencies with peerDependenciesMeta marking them "optional": true (while keeping both in devDependencies for CI) so we bundle Arrow support as an opt-in "plugin" and don't install apache-arrow by default for non-Arrow users. (Note: peerDependencies without peerDependenciesMeta are auto-installed by npm v7+.)

https://docs.npmjs.com/cli/v11/configuring-npm/package-json#peerdependenciesmeta

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 considered this approach, but apache-arrow can't be an optional peer as long as our public typings reference it. ArrowRecordBatchStream is ResourceStream<RecordBatch>, and bigquery.d.ts re-exports it. TypeScript can't make that import optional, so any TS user who doesn't install apache-arrow breaks, even if they never call the Arrow API:

build/src/arrow.d.ts(17,42): error TS2307: Cannot find module 'apache-arrow' or its corresponding type declarations.
build/src/arrow.d.ts(20,34): error TS2307: Cannot find module 'apache-arrow' or its corresponding type declarations.

With skipLibCheck it compiles, but RecordBatch becomes any. pack-n-play (system-test/install.ts) caught exactly this, which is why I moved apache-arrow to dependencies.

To make it optional, we'd need to replace RecordBatch in the public typings with our own minimal interface, so the .d.ts files no longer import apache-arrow. That's about 45 lines, and we'd have to keep it in sync with apache-arrow's API. The trade-off is roughly 8.4 MB on install (flatbuffers, json-with-bigint, tslib) against users getting the real RecordBatch types. I went with the dependency for simplicity, but I'm happy to switch if you'd rather keep the install lean.

For @google-cloud/bigquery-storage I agree, and I'll declare it as an optional peer (peerDependenciesMeta.optional) while keeping it in devDependencies for CI.

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.

Added @google-cloud/bigquery-storage as an optional peer (kept in devDependencies for CI) in c1fc109. apache-arrow stays a regular dependency for the TS2307 reason above.

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 (for future consideration): Keeping apache-arrow in dependencies (while lazy-loading it at runtime) and moving @google-cloud/bigquery-storage to optional peerDependencies makes sense for now so pack-n-play doesn't fail with TS2307.

In the future, if we want to make apache-arrow a true optional peerDependency as well (so default installs don't download it at all), we could consider exposing Arrow via a subpath export in package.json:

  1. Add "exports" to package.json with "." (./build/src/index.js) and "./arrow" (./build/src/arrow.js).
  2. Stop re-exporting ./arrow from src/index.ts and src/bigquery.ts so the root .d.ts files never import type {RecordBatch} from 'apache-arrow'.
  3. Optionally use TypeScript module augmentation (declare module './bigquery' { interface BigQuery { ... } }) inside src/arrow.ts if we also want bq.queryAsArrowStream() typed when a user imports @google-cloud/bigquery/arrow.

That way, TypeScript only resolves apache-arrow types for users who actually import @google-cloud/bigquery/arrow, and pack-n-play stays green without apache-arrow installed. Totally fine to keep the current setup for this PR stack!

Comment thread handwritten/bigquery/src/arrow.ts Outdated
Comment on lines +64 to +139
function loadPackageJson(
requireFn: NodeRequire,
pkgName: string,
): {version?: string} {
try {
return requireFn(`${pkgName}/package.json`);
} catch {
const entryPath = (requireFn.resolve || require.resolve)(pkgName);
return requireFn(path.join(path.dirname(entryPath), 'package.json'));
}
}

/**
* Requires a dependency and verifies its minimum major.minor version.
*
* @private
*/
function requireWithMinVersion<T = unknown>(
requireFn: NodeRequire,
pkgName: string,
minMajor: number,
minMinor = 0,
): T {
const minVersion = `${minMajor}.${minMinor}.0`;
let mod: T;
let pkg: {version?: string};
try {
mod = requireFn(pkgName);
pkg = loadPackageJson(requireFn, pkgName);
} catch (err) {
throw new Error(
`Arrow support requires the "${pkgName}" package (>= ${minVersion}), ` +
`but it could not be loaded: ${(err as Error).message}. ` +
`Please install it using: npm install ${pkgName}`,
{cause: err},
);
}

const [majorStr, minorStr] = (pkg.version || '0.0.0').split('.');
const major = Number.parseInt(majorStr, 10);
const minor = Number.parseInt(minorStr, 10);
const versionOk =
!Number.isNaN(major) &&
(major > minMajor ||
(major === minMajor && !Number.isNaN(minor) && minor >= minMinor));

if (!versionOk) {
throw new Error(
`Arrow support requires "${pkgName}" >= ${minVersion} (found ${pkg.version || 'unknown'}). ` +
`Please upgrade using: npm install ${pkgName}@latest`,
);
}
return mod;
}

/**
* Verifies that `apache-arrow` (>= 21.0.0) and the optional
* `@google-cloud/bigquery-storage` (>= 6.1.0) package are installed and meet
* minimum version requirements.
*
* @private
*/
export function checkArrowDependencies(
requireFn: NodeRequire = require,
): ArrowModule {
const arrow = requireWithMinVersion<ArrowModule>(
requireFn,
'apache-arrow',
21,
);
// TODO: Bump minimum version (here and in package.json) once
// @google-cloud/bigquery-storage is released with
// @google-cloud/bigquery-storage-api >= 0.5.0.
requireWithMinVersion(requireFn, '@google-cloud/bigquery-storage', 6, 1);
return arrow;
}

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.

Saw your note that loadPackageJson will be removed once bigquery-storage is released.

Do we need requireWithMinVersion at all once 6.3.0 is published? Note that require('apache-arrow/package.json') also throws MODULE_NOT_FOUND because apache-arrow's exports map rewrites ./* to ./*.js. If both packages are declared in peerDependencies, the package manager handles version constraints at install time and we only need a simple lazy require() here.

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.

Good catch on the exports map. That also means my earlier note was wrong: loadPackageJson wasn't test-only, it ran for every user because of apache-arrow.

Since apache-arrow is a regular ^21 dependency, I dropped its version check, which removes loadPackageJson entirely (2d466a9). It's now a plain lazy require().

I kept the check for bigquery-storage, though. Package managers only warn when an installed version is outside an optional peer range (I checked npm 10 too), and an older version doesn't fail loudly: its protos silently drop newer request fields like arrowSerializationOptions. The check turns that into a clear error.

Comment thread handwritten/bigquery/src/arrow.ts Outdated
Comment on lines +146 to +201
function toBuffer(serialized: Uint8Array | string): Buffer {
if (typeof serialized === 'string') {
return Buffer.from(serialized, 'base64');
}
return Buffer.from(
serialized.buffer,
serialized.byteOffset,
serialized.byteLength,
);
}

/**
* Creates a 0-row `RecordBatch` that preserves the provided Arrow `Schema`.
*
* @private
*/
export function createEmptyRecordBatch(
arrow: ArrowModule,
schema: Schema,
): RecordBatch {
// Use makeBuilder so apache-arrow allocates valid 0-length typed array
// buffers for every column (including nested Struct and List fields).
const structData = arrow
.makeBuilder({type: new arrow.Struct(schema.fields)})
.finish()
.flush();
return new arrow.RecordBatch(schema, structData);
}

/**
* Decodes serialized Arrow IPC schema and record batch bytes into `RecordBatch`
* objects, emitting a single 0-row `RecordBatch` (preserving `schema` when
* present) if no record batches are present.
*
* @private
*/
export function decodeArrowPayload(
arrow: ArrowModule,
serializedSchema?: Uint8Array | string,
serializedRecordBatch?: Uint8Array | string,
): RecordBatch[] {
if (!serializedSchema) {
return [createEmptyRecordBatch(arrow, new arrow.Schema([]))];
}
// BigQuery returns the Arrow IPC Schema message and RecordBatch message as
// separate fields; concatenate them into a single IPC stream for the reader.
const schemaBuf = toBuffer(serializedSchema);
const ipcBuf = serializedRecordBatch
? Buffer.concat([schemaBuf, toBuffer(serializedRecordBatch)])
: schemaBuf;
const reader = arrow.RecordBatchReader.from(ipcBuf);
reader.open();
const schema = reader.schema;
const batches = [...reader];
return batches.length > 0 ? batches : [createEmptyRecordBatch(arrow, schema)];
}

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.

We can pass [schemaBuf, batchBuf] directly to arrow.RecordBatchReader.from to avoid copying buffers with Buffer.concat. Also, is createEmptyRecordBatch needed? RecordBatchReader.from([schemaBuf]).readAll() already returns a 0-row RecordBatch with the schema when no record batch is present, and new arrow.RecordBatch({}) handles the empty schema case:

Suggested change
function toBuffer(serialized: Uint8Array | string): Buffer {
if (typeof serialized === 'string') {
return Buffer.from(serialized, 'base64');
}
return Buffer.from(
serialized.buffer,
serialized.byteOffset,
serialized.byteLength,
);
}
/**
* Creates a 0-row `RecordBatch` that preserves the provided Arrow `Schema`.
*
* @private
*/
export function createEmptyRecordBatch(
arrow: ArrowModule,
schema: Schema,
): RecordBatch {
// Use makeBuilder so apache-arrow allocates valid 0-length typed array
// buffers for every column (including nested Struct and List fields).
const structData = arrow
.makeBuilder({type: new arrow.Struct(schema.fields)})
.finish()
.flush();
return new arrow.RecordBatch(schema, structData);
}
/**
* Decodes serialized Arrow IPC schema and record batch bytes into `RecordBatch`
* objects, emitting a single 0-row `RecordBatch` (preserving `schema` when
* present) if no record batches are present.
*
* @private
*/
export function decodeArrowPayload(
arrow: ArrowModule,
serializedSchema?: Uint8Array | string,
serializedRecordBatch?: Uint8Array | string,
): RecordBatch[] {
if (!serializedSchema) {
return [createEmptyRecordBatch(arrow, new arrow.Schema([]))];
}
// BigQuery returns the Arrow IPC Schema message and RecordBatch message as
// separate fields; concatenate them into a single IPC stream for the reader.
const schemaBuf = toBuffer(serializedSchema);
const ipcBuf = serializedRecordBatch
? Buffer.concat([schemaBuf, toBuffer(serializedRecordBatch)])
: schemaBuf;
const reader = arrow.RecordBatchReader.from(ipcBuf);
reader.open();
const schema = reader.schema;
const batches = [...reader];
return batches.length > 0 ? batches : [createEmptyRecordBatch(arrow, schema)];
}
function toUint8Array(serialized: Uint8Array | string): Uint8Array {
return typeof serialized === 'string'
? Buffer.from(serialized, 'base64')
: serialized;
}
/**
* Decodes serialized Arrow IPC schema and record batch bytes into `RecordBatch`
* objects, emitting a single 0-row `RecordBatch` (preserving `schema` when
* present) if no record batches are present.
*
* @private
*/
export function decodeArrowPayload(
arrow: ArrowModule,
serializedSchema?: Uint8Array | string,
serializedRecordBatch?: Uint8Array | string,
): RecordBatch[] {
if (!serializedSchema) {
return [new arrow.RecordBatch({})];
}
const chunks = serializedRecordBatch
? [toUint8Array(serializedSchema), toUint8Array(serializedRecordBatch)]
: [toUint8Array(serializedSchema)];
return arrow.RecordBatchReader.from(chunks).readAll();
}

@ohmayr ohmayr Oct 15, 2026 •

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.

Took the chunk array and new arrow.RecordBatch({}) (caec977). No more Buffer.concat copy per batch.

On dropping createEmptyRecordBatch, you're right that the reader already returns a 0-row batch with the schema, but that batch is incomplete for nested columns. For a STRUCT column it has no child fields, so on an empty result:

batch.getChild('address').getChild('city');
// reader's batch: TypeError: Vector constructor expects an Array of Data instances
// createEmptyRecordBatch: empty vector

LIST columns have the same problem (the child data is undefined). Looking into this showed we had the bug too: [...reader] already returned that placeholder, so our fallback never ran. Fixed in 6c36eb1, and the unit test now reads into a nested field so it can't regress quietly.

Comment thread handwritten/bigquery/src/arrow.ts Outdated
bq: BigQuery,
query?: Query | string,
): ArrowRecordBatchStream {
const bqInternal = bq as unknown as BigQueryInternal;

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.

Can we mark buildQueryRequest_ and runJobsQuery as @internal (non-private) on BigQuery instead of casting bq as unknown as BigQueryInternal?

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.

Done in 69bbe38: dropped private on both and removed the cast. I tagged them @private rather than @internal to match how the rest of BigQuery marks members shared across files (queryAsStream_, mergeSchemaWithRows_). Happy to switch to @internal if you prefer.

Comment thread handwritten/bigquery/src/arrow.ts Outdated
return;
}

const queryReq = bqInternal.buildQueryRequest_(queryObj, {});

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.

Wrap buildQueryRequest_ in a try / catch and route any error to stream.destroy(err as Error):

Suggested change
const queryReq = bqInternal.buildQueryRequest_(queryObj, {});
let queryReq: bigquery.IQueryRequest | undefined;
try {
queryReq = bqInternal.buildQueryRequest_(queryObj, {});
} catch (err) {
stream.destroy(err as Error);
return;
}

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.

Done in 60f6082, with a test for a null param without types.

Comment thread handwritten/bigquery/src/arrow.ts Outdated
Comment on lines +297 to +298
// pageRowCount and totalRows are returned as uint64 strings in REST JSON.
if (BigInt(res?.pageRowCount ?? 0) < BigInt(res?.totalRows ?? 0)) {

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.

Also check Boolean(res?.pageToken) here—on cached multi-page queries, jobs.query can return totalRows: "0" and omit pageRowCount while setting res.pageToken:

Suggested change
// pageRowCount and totalRows are returned as uint64 strings in REST JSON.
if (BigInt(res?.pageRowCount ?? 0) < BigInt(res?.totalRows ?? 0)) {
// pageRowCount and totalRows are returned as uint64 strings in REST JSON.
if (
Boolean(res?.pageToken) ||
BigInt(res?.pageRowCount ?? 0) < BigInt(res?.totalRows ?? 0)
) {

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.

Good catch, done in 27bed0b with a test for totalRows: '0' plus a pageToken. We've hit this too: on ARROW cache hits, jobs.query reports totalRows: "0" even when rows remain, so without this check the stream silently returned nothing.

@ohmayr

ohmayr commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

#9617 is a relevant PR for the system tests.

@quirogas quirogas 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.

LGTM! Thanks for addressing all the review comments.

@ohmayr

ohmayr commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

I added one docs commit after your approval: createQueryArrowStream and ArrowRecordBatchStream are now marked [Beta] with @beta. The ARROW results format on jobs.query isn't GA yet (the API reference still calls it not yet available), and the Java and Python clients label their Arrow query APIs as beta the same way. #9599 and #9603 label ArrowQuery and ArrowSerializationOptions too. Could you take another look?

@ohmayr ohmayr added the do not merge Indicates a pull request not ready for merge, due to either quality or timing. label Oct 15, 2026
@ohmayr

ohmayr commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Adding a Do Not Merge label until I get a signal from the backend team from what fields we should be relying on.

Googlers, see: b/573764383

Introduce bigquery.createQueryArrowStream(query) to execute SQL queries
with queryResultsFormat set to ARROW and stream decoded Apache Arrow
RecordBatch objects:
- Add apache-arrow (>= 21.0.0) to dependencies so published TypeScript
  declarations resolve RecordBatch, and verify apache-arrow (>= 21.0.0)
  and optional @google-cloud/bigquery-storage (>= 6.1.0) lazily at
  runtime.
- Default jobCreationMode to JOB_CREATION_OPTIONAL when not configured
  on the request or client.
- Reject writeDisposition: WRITE_APPEND before executing the query.
- Decode inline Arrow IPC schema and record batch payloads, emitting a
  0-row RecordBatch preserving the schema (or 0-column schema) when no
  rows are returned.
On query cache hits whose results don't fit inline, jobs.query with
queryResultsFormat ARROW returns totalRows "0" and omits pageRowCount
while still setting pageToken. Comparing pageRowCount with totalRows
alone saw 0 < 0, so createQueryArrowStream ended with no rows and no
error.

Also treat a non-empty pageToken as remaining rows, so these responses
fail with the "not returned inline" error instead of silently returning
an empty result.
List @google-cloud/bigquery-storage under peerDependencies with
peerDependenciesMeta optional, so the supported range is published and
package managers check it when the package is installed, without
installing it for users who don't use Arrow. Keep it in devDependencies
because optional peers aren't installed for tests.
createQueryArrowStream reached BigQuery's private buildQueryRequest_
and runJobsQuery through an `as unknown as BigQueryInternal` cast, the
only such cast in src/. A cast also hides signature changes from the
compiler.

Drop the TypeScript private modifier on both methods and tag them
@Private in JSDoc, matching how the package already shares internals
across files (Table.formatMetadata_, BigQuery.mergeSchemaWithRows_),
and call them directly from arrow.ts.
buildQueryRequest_ throws on invalid query parameters, for example a
null parameter without a type. The throw happened inside _read and only
reached the stream because Node's Readable catches it.

Catch it explicitly and destroy the stream with the error, matching the
checkArrowDependencies() handling, so the behavior doesn't depend on
stream internals.
decodeArrowPayload joined the IPC schema and record batch messages with
Buffer.concat before decoding, copying every batch. Pass them to
RecordBatchReader.from() as consecutive chunks instead, and use
new RecordBatch({}) when the response has no schema.
For a response with a schema but no record batch, RecordBatchReader
yields its own 0-row placeholder batch. decodeArrowPayload returned it
as is, so the createEmptyRecordBatch fallback never ran. The placeholder
has no Struct children and undefined List children, so reading a nested
column of a 0-row result threw "Vector constructor expects an Array of
Data instances".

Build the batch with createEmptyRecordBatch whenever the response has no
record batch, and assert in the unit test that nested children are
readable.
apache-arrow is a regular ^21.0.0 dependency, so the install already
guarantees its version. Its runtime check also needed loadPackageJson,
because apache-arrow's exports map blocks require('apache-arrow/
package.json'). Load it with a plain lazy require and remove
loadPackageJson.

Keep the @google-cloud/bigquery-storage check. Package managers only
warn when an installed version is outside an optional peer range, and
an older version's protos silently drop newer request fields such as
arrowSerializationOptions.
The ARROW query results format of the BigQuery API isn't generally
available yet; the API reference still describes it as not yet
available. Label createQueryArrowStream and ArrowRecordBatchStream as
[Beta] with @beta, as the Java and Python clients label their Arrow
query APIs, so callers know the method may change.
@ohmayr
ohmayr force-pushed the feat/bigquery-arrow-inline branch from 8f12a09 to 79608b0 Compare October 16, 2026 01:12
@ohmayr

ohmayr commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

The decision remains the same as what we have here. So no longer blocked.

@ohmayr ohmayr removed the do not merge Indicates a pull request not ready for merge, due to either quality or timing. label Oct 16, 2026
@ohmayr
ohmayr merged commit 69157ed into main Oct 16, 2026
47 checks passed
@ohmayr
ohmayr deleted the feat/bigquery-arrow-inline branch October 16, 2026 02:19
@release-please release-please Bot mentioned this pull request Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: bigquery Issues related to the BigQuery API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants