This guide details the architectural design, core abstractions, coding principles, and testing workflows for developers contributing to the google-cloud-bigquery-jdbc module.
- Core Architecture & Component Map
- Developer Guardrails & Rules of Engagement
- Build & Test Playbook
- Logging Architecture & Developer Conventions
- Pre-PR Checklist
The driver is structured to provide high performance, zero-allocation MDC log tracing, strict JDBC compliance, and seamless execution over the Google Cloud BigQuery REST and Storage APIs.
graph TD
Client[Client Application / BI Tool] -->|DriverManager.getConnection| Driver[BigQueryDriver]
Driver -->|Parses URI & Options| UrlUtil[BigQueryJdbcUrlUtility]
Driver -->|Configures Logging| RootLogger[BigQueryJdbcRootLogger]
Driver -->|Creates| Conn[BigQueryConnection]
Conn -->|Dynamic Context Proxy| Proxy[BigQueryJdbcContextProxy]
Proxy -->|MDC Tracing| Mdc[BigQueryJdbcMdc]
Proxy -->|Delegates Exec| DirectConn[Client Session]
Conn -->|Type Mapping & Coercion| Coercion[BigQueryJdbcTypeMappings & BigQueryCoercion]
Conn -->|REST / Storage API| BQSDK[google-cloud-bigquery]
BigQueryDriver: JDBC entry point registered withjava.sql.DriverManager. Interceptsjdbc:bigquery://URLs, initializes early logger state, and instantiatesBigQueryConnection.BigQueryConnection: Represents an active BigQuery session, holding dataset defaults, connection configuration maps, and transaction/session state (EnableSession=true,session_id).BigQueryJdbcUrlUtility: Parses and validates connection string parameters using a bounded LRU parse cache (PARSE_CACHE) to avoid heavy allocations during frequent connection creation.BigQueryJdbcContextProxy: A dynamic proxy layer (java.lang.reflect.Proxy) wrapping JDBC statements, connections, and metadata. Intercepts calls to propagate ThreadLocal MDC parameters (connectionId) across execution threads and enforce state validation (checkClosed()).BigQueryJdbcTypeMappings&BigQueryCoercion: Centralized mapping logic handling standard JDBC-to-BigQuery SQL type mappings (StandardSQLTypeName) and object coercions (Date,Timestamp,BigDecimal, etc.).BigQueryArrowResultSet: Custom result set implementation accelerating large query result retrieval via the BigQuery Storage Read API gRPC stream.
Important
Adhere strictly to the following guardrails when making code changes:
- Visibility Principle: Always default to the most restrictive access level (
private, package-private, or@InternalApi). Do NOT expose classes or methods aspublicunless strictly required by standard JDBC interfaces. - Explicit Class Imports: Always write explicit
importstatements. Do NOT use wildcard star imports or inline fully qualified class names (e.g., useimport java.math.BigDecimal;instead ofjava.math.BigDecimalinline). - Logger Preference: Always prefer
BigQueryJdbcCustomLoggeroverjava.util.logging.Logger. Format strings usingString.format(...)before logging, asBigQueryJdbcRootLoggerevaluatesrecord.getMessage()directly. - Exception Handling: Always throw exceptions from the
com.google.cloud.bigquery.exceptionpackage (BigQueryJdbcException,BigQueryJdbcSqlSyntaxErrorException,BigQueryConversionException). - No Mocking of Final JDK Classes: Do NOT mock final JDK types (
BigDecimal,LocalDate,Instant,UUID) with Mockito. Mocking final JDK classes is unstable and can cause JVM crashes under JDK 21+. Always construct real instances in unit tests.
Builds and test tasks are managed via the module Makefile.
# Build & install module locally
make install
# Clean project target directory
make clean
# Format code and check linter compliance
make lint# Run all unit tests
make unittest
# Run a specific unit test class
make unittest test=BigQueryPreparedStatementTest
# Run a specific unit test method
make unittest test=BigQueryPreparedStatementTest#testSetObjectWithTemporalTypesWarning
Integration tests connect to real GCP BigQuery resources and require valid GCP credentials.
# Set GCP service account credentials
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json
# Run a specific integration test
make integration-test test=ITBigQueryJDBCTest#testValidServiceAccountAuthenticationOAuthPvtKeyIf local Java/Maven environments are not available, use the dockerized environment:
# Start an interactive shell session inside Docker container
make docker-session
# Run unit tests inside Docker
make docker-unittestThe driver uses a custom logging subsystem built on top of java.util.logging: BigQueryJdbcCustomLogger and BigQueryJdbcRootLogger.
- For Instance Components (
BigQueryConnection,BigQueryStatement,BigQueryDatabaseMetaData): Usethis.toString()to include instance identity in logger output:private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString());
- For Static / Utility Components (
BigQueryJdbcUrlUtility,BigQueryJdbcTypeMappings): Use the class name:private static final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(BigQueryJdbcTypeMappings.class.getName());
- Method Entry / Exit Tracing:
Methods at
FINERlevel must log entrance and exit points:public ResultSet executeQuery(String sql) throws SQLException { LOG.finer("++enter++"); try { // ... execution logic ... return rs; } finally { LOG.finer("++exit++"); } }
- Format Placeholders (Zero Allocation):
Avoid string concatenation in log calls. Use formatting placeholders or
Supplier<String>lambdas to prevent unneeded string allocation when the log level is disabled:// Recommended: Use printf-style formatting LOG.fine("Executing query on dataset: %s, table: %s", datasetId, tableId); // Recommended: Use supplier lambda for expensive calculations LOG.fine(() -> "Parsed properties: " + complexObject.toDebugString());
- Caller Inference & MDC Propagation:
BigQueryJdbcCustomLoggerautomatically wraps log records inBigQueryJdbcLogRecord, which inspects the stack trace to accurately infer caller class and method names.BigQueryJdbcMdcmaintainsconnectionIdin aThreadLocalcontext. When logging from proxy or worker threads, always ensure MDC context is preserved or propagated viaBigQueryJdbcContextProxy.
Before submitting a Pull Request:
- All new classes and methods use the narrowest possible visibility scope (
privateor package-private). - No inline fully qualified names or wildcard star imports are present.
- All logger instances use
BigQueryJdbcCustomLogger. - Method entrance/exit logging (
++enter++/++exit++) is included for complex internal routines. - All method changes and feature additions are covered by corresponding JUnit 5 tests.
- Unit tests pass cleanly without Mockito
UnnecessaryStubbingExceptionwarnings. - All
Statement,ResultSet, orDatabaseMetaDataobjects returned by public entry points are properly wrapped viaBigQueryJdbcContextProxy.wrap(). - Code formatting and linting pass via
make lint.