Skip to content

feat(provider): add Google BigQuery provider - #1672

Merged
cevheri merged 3 commits into
libredb:mainfrom
Dariusch:feat/bigquery-provider
Oct 11, 2026
Merged

cevheri merged 3 commits into
libredb:mainfrom
Dariusch:feat/bigquery-provider

Conversation

@Dariusch

@Dariusch Dariusch commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds Google BigQuery as an engine (bigquery), with no Google SDK: every statement is a REST jobs.query call over the shared node transport, and jose (already a dependency) signs service-account tokens.
It covers the editor (results, schema tree, autocomplete, a live cost estimate), monitoring and seeding. Agent and MCP run_read_query execution, and clause-aware completion, follow in separate PRs.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test addition or update

Related Issue

Part of #424.

Changes Made

  • Provider (src/lib/db/providers/sql/bigquery/): connect, query with paging and polling, typed cell decoding (INT64 as string, TIMESTAMP, STRUCT, ARRAY), error mapping, best-effort jobs.cancel on abort, and a datasets → tables / views / materialized views object surface.
  • Credentials: a pasted service-account key or access token; with BIGQUERY_SERVER_CREDENTIALS=true and only for operator-seeded connections, the server's ADC file or the metadata server (GKE Workload Identity). Tokens are cached and single-flighted.
  • Job location: the connection's location field, else the default dataset's location, else the most common dataset location, so table-less statements do not fall back to US.
  • Cost estimate: EXPLAIN is a free dry run reporting bytes processed, and the editor shows a live "This query will process X when run" badge.
  • Schema and autocomplete: two region-level INFORMATION_SCHEMA reads per location, cached, with a REST fallback; on a 3,000-table project this took the schema load from about 78 s to under 10 s.
  • Monitoring: overview, performance, slow queries, active jobs, table stats and storage from INFORMATION_SCHEMA, cached for 60 s.
  • SQL grammar for BigQuery, every fact measured with dry runs: # comments, backslash escapes (including in backtick names), triple-quoted strings, flat block comments, […] subscripts.
  • Shared code: a tablesNeedContainer capability and a labelQualified table-item flag (completion for other engines is unchanged), a showSslPanel UI flag, triple-quoted strings and backslash-escaped backticks in the span reader (off for every other grammar), and the server forcing managed: false on inline connections.
  • Seeding and form: seed type: bigquery with database (project), schema (default dataset), password (key JSON or token) and location; the form hides host, port, SSL and SSH.
  • Docs: new docs/providers/bigquery.md; BigQuery added to docs/SEED_CONNECTIONS.md, docs/API_DOCS.md, the READMEs and listings (thirty external engines); BIGQUERY_SERVER_CREDENTIALS, GOOGLE_APPLICATION_CREDENTIALS and GCE_METADATA_HOST in .env.example.
  • Chart: bumped to 0.1.86 because an upstream test pins the chart description to the engine count; metadata only, no template or value changes.

Testing

  • I have tested this locally
  • I have added/updated tests
  • All existing tests pass

Live, against a real BigQuery project (data in europe-west3): connect, queries, schema tree and autocomplete, the dry-run estimate, monitoring, and every grammar fact via dry runs.

Automated: new unit tests for the provider, transport, auth and tokens, decoding, the grammar and qualified completion labels, plus tests/integration/db/bigquery-provider.test.ts, which runs the real provider, transport and auth against a fake REST server, including the shared object-surface conformance check.

bun run test passes apart from tests/unit/db/etcd/tls-handshake.test.ts, which also fails on main.
Format, lint, typecheck, knip, chart:check, channels:showcase:check, readme:check, security:check, build, build:lib, attw, lib:closure and the Windows launcher's gofmt/go vet/go test pass.

Test Environment

  • LibreDB Studio Version: 0.18.3 (+ this branch)
  • Browser: Google Chrome
  • OS: Linux (Debian 13)
  • Node.js/Bun Version: Bun 1.4.2
  • Database Type: Google BigQuery

Screenshots (if applicable)

image image

Checklist

  • My code follows the project's code style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have updated the documentation accordingly
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • The required CI test job passes the 100% line-coverage gate (bun run test:coverage and bun run coverage:check)
  • If I changed src/lib/db/providers/, I updated the matching docs/providers/ documentation and tests/integration/db/ tests in the same PR (provider triad)
  • Any dependent changes have been merged and published

Additional Notes

  • No live BigQuery in CI: BigQuery has no local emulator that implements jobs.query and INFORMATION_SCHEMA faithfully, so the integration test uses a fake REST server and live behaviour was verified by hand.
  • Known limits, documented: an abort while the first jobs.query POST is still in flight cannot cancel the job; the shared inventory limit (1,000 container/kind pairs) truncates autocomplete above about 333 datasets.
  • Reviewing: the last commit applies the first review: the MCP fence, the completion rewrite and agent/MCP execution are reverted to main, so review the full diff rather than commit by commit.
  • Chart bump: the chart version bump to 0.1.86 may be something maintainers prefer to own at release; happy to drop or adjust it.

@cevheri cevheri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, this is a solid provider: the triad is complete, there is no new dependency, and nothing reverts main. I'm requesting changes because three pieces change behaviour beyond BigQuery, plus a few small fixes. A new provider should not change how any other engine behaves.

  1. Keep the MCP statement fence on main's reading for every engine.
    Passing connection.type into checkReadOnlyStatement changes the verdict on PostgreSQL, DuckDB and SQL Server too: SELECT /* /* */ 1; DROP TABLE t; */ was MULTIPLE_STATEMENTS on main and is now admitted there, and SELECT [a--b] FROM t flips the other way. No test pins those engines' verdicts.
    Revert src/lib/mcp/guards/execution-fence.ts and the call in run-read-query.ts to main's form. BigQuery's own queryReadOnly already applies its grammar.
    Done when: checkReadOnlyStatement matches main.

  2. Move the clause-aware completion rewrite into its own PR.
    The sql-completions.ts change alters completion for every engine, for example tables are no longer offered in a SELECT list. That deserves its own review.
    Keep only what BigQuery needs here (tablesNeedContainer and the qualified label), the way Databend and S3 added their completion facts without touching the shared flow.
    Done when: completion for a non-BigQuery connection behaves exactly as on main.

  3. Split agent and MCP execution on BigQuery into a follow-up PR.
    On the engines that execute today, the boundary includes the identity: PostgreSQL refuses broad roles and SQL Server checks at open that the principal cannot write. Here a key that can write is accepted, and the grammar comment notes remote functions are not covered.
    Land the editor provider first. In the follow-up we can talk about an identity check (for example a testIamPermissions probe that refuses write permissions).
    Done when: AGENT_EXECUTION_ENGINES, the MCP engine list and their tests are as on main.

  4. Document the server-credential variables in .env.example.
    BIGQUERY_SERVER_CREDENTIALS, GOOGLE_APPLICATION_CREDENTIALS and GCE_METADATA_HOST are read in auth.ts but are not in .env.example, where every env var lives.
    Done when: all three are there with an example value and a one-line explanation.

  5. Fix two small text slips.
    In src/lib/sql/grammar.ts, BIGQUERY_GRAMMAR sits between Databend's docblock and DATABEND_GRAMMAR, so that docblock now belongs to the wrong constant. DOCKERHUB.md says "The thirty-one row", which should be "thirty-first".
    Done when: each grammar constant directly follows its own docblock and the ordinal is fixed.

Please also replace Closes #<issue number> in the description with a reference to #424. I'll approve the CI run once these are in.

Comment thread tests/unit/db/bigquery/transport.test.ts Fixed
…cost estimate

Adds the `bigquery` engine with no Google SDK: statements are REST jobs.query calls over the shared node transport, and service-account tokens are signed with jose.
It covers the editor (results, schema tree, autocomplete with dataset.table labels, a live dry-run cost badge and EXPLAIN as a dry run), monitoring from region INFORMATION_SCHEMA, seeding with job location detection, and server credentials (ADC or GKE Workload Identity) for operator-seeded connections behind BIGQUERY_SERVER_CREDENTIALS.
Agent and MCP run_read_query execution and clause-aware completion follow in separate PRs (libredb#424).
@codecov

codecov Bot commented Oct 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Dariusch
Dariusch force-pushed the feat/bigquery-provider branch from bdee6ba to 8159252 Compare October 11, 2026 18:43
@cevheri

cevheri commented Oct 11, 2026

Copy link
Copy Markdown
Member

One more ask, on top of the review above. A new provider is a big change, so next time please open an issue first so we can agree on the scope before the code. For this one, the integration test mocks the HTTP layer, so nothing here has run against an engine. docs/providers/bigquery.md says no emulator covers jobs.query and INFORMATION_SCHEMA, but goccy/bigquery-emulator implements jobs.query, getQueryResults with paging, dataset and table listing, and the SCHEMATA, TABLES and COLUMNS views. That covers connect, the schema tree, a query and pagination. Dry run and bytes processed are not covered, so those stay mocked.

  1. Add a live check against the emulator.
    Read a server-side BIGQUERY_EMULATOR_HOST (env only, never a connection field) in transport.ts, then add tests/live/bigquery-live-check.ts in the shape of tests/live/databend-live-check.ts, run against ghcr.io/goccy/bigquery-emulator.
    Done when: the script connects, lists the datasets and tables, reads columns, runs a query across two pages and exits non-zero on any failure, and the doc line about the emulator says what it covers and what it does not.

@Dariusch

Copy link
Copy Markdown
Contributor Author

Yeah, I will create an issue for sure next time. For us, this initially started as a PoC, but I made quick progress and did not want to keep it from the project.

We did not have a good experience with https://github.com/goccy/bigquery-emulator for our CI pipelines. This is likely due to our setup; I will look into adding it to this project/PR.

BIGQUERY_EMULATOR_HOST (server environment only) points the transport at the emulator, and tests/live/bigquery-live-check.ts runs the real provider against it: connect, datasets, tables and views, columns, and a query across two pages.
The emulator found three fixes: jobTimeoutMs is sent as the int64 string the REST schema declares, a dataset listed without a location is kept, and tables.get's legacy type names are renamed to the GoogleSQL ones INFORMATION_SCHEMA reports.
The fake transport in the unit test now routes on the URL path, not a host substring, which CodeQL flagged.
@cevheri

cevheri commented Oct 11, 2026

Copy link
Copy Markdown
Member

how was your contribution experience with libredb-studio?

@cevheri cevheri added security Supply-chain, auth, or hardening work database-provider bigquery labels Oct 11, 2026
@cevheri

cevheri commented Oct 11, 2026

Copy link
Copy Markdown
Member

thanks Dariusch this is a big PR, and last commit fixed the live check, merging shortly and will see next release

@Dariusch

Copy link
Copy Markdown
Contributor Author

Thank you.
Tomorrow, I will reply to the contribution ask and create the follow-up Pull Requests. It is getting late here in Germany

Resolve the conflicts the MQTT provider (libredb#1676) left. Every engine list
now names both BigQuery and MQTT, and every count both branches had
moved by one now moves by two: 31 external engines, 32 type-ids, 58
named engines, 30 provider modules. Lines both branches changed the
same way merged without a conflict and still carried the +1, so those
were corrected too.

The chart goes to 0.1.87, because 0.1.86 was released with MQTT. The
MQTT row in DOCKERHUB.md is shortened to stay under the 23,000-byte
cap, BigQuery's showcase rank moves to 31 behind the embedded store,
the operator bundle is regenerated, and the factory.ts line references
in the connection fingerprint test point at the merged file.
@cevheri
cevheri merged commit d28cd1e into libredb:main Oct 11, 2026
24 checks passed
@cevheri

cevheri commented Oct 11, 2026

Copy link
Copy Markdown
Member

Thanks @Dariusch, BigQuery is merged. Two follow-ups, in one PR when you have time:

  1. Send no Google credential to the emulator. apiOrigin (src/lib/db/providers/sql/bigquery/transport.ts:142) reads BIGQUERY_EMULATOR_HOST in the server. When it is set, every API request carries the connection's real access token to that host, over plain HTTP for a bare host:port, so a variable left set on a server sends live tokens in clear text. With the variable set, skip the token source and send no authorization header; the emulator checks none.
    Done when: a case in tests/unit/db/bigquery/transport.test.ts with the variable and a key JSON shows no request to oauth2.googleapis.com and no authorization header on the API request, and tests/live/bigquery-live-check.ts still passes.

  2. Replay recorded answers in the integration test. Its header says every answer is built from the documented REST shapes, while gate 4 in Tracking: database provider coverage - shipped, queued, and declined #424 asks for answers captured from a live instance, with the date in the test header. You have a real project, so record them there with scrubbed captures (the pattern is tests/live/databend-evidence.ts and tests/fixtures/databend/README.md) and replay them.
    Done when: every fake answer in tests/integration/db/bigquery-provider.test.ts comes from a committed capture, and its header names the capture date and region.

Dariusch added a commit to Dariusch/libredb-studio that referenced this pull request Oct 12, 2026
…illed-bytes cap

A dry run admits only a SELECT, maximumBytesBilled caps the real run, the agent guard refuses ML.*, AI.* and EXTERNAL_QUERY, and opening under the agent profile refuses an identity that Resource Manager's testIamPermissions shows can write on the project. Split out of libredb#1672; the MCP statement fence stays as on main.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bigquery database-provider security Supply-chain, auth, or hardening work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants