|
| 1 | +# BigQuery JDBC Developer & Contributor Guide |
| 2 | + |
| 3 | +This guide details the architectural design, core abstractions, coding principles, and testing workflows for developers contributing to the `google-cloud-bigquery-jdbc` module. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## Table of Contents |
| 8 | +1. [Core Architecture & Component Map](#1-core-architecture--component-map) |
| 9 | +2. [Developer Guardrails & Rules of Engagement](#2-developer-guardrails--rules-of-engagement) |
| 10 | +3. [Build & Test Playbook](#3-build--test-playbook) |
| 11 | + - [Local Build Commands](#local-build-commands) |
| 12 | + - [Running Unit Tests](#running-unit-tests) |
| 13 | + - [Running Integration Tests](#running-integration-tests) |
| 14 | + - [Dockerized Execution](#dockerized-execution) |
| 15 | +4. [Logging Architecture & Developer Conventions](#4-logging-architecture--developer-conventions) |
| 16 | + - [Instantiating Loggers](#instantiating-loggers) |
| 17 | + - [Developer Logging Rules & Conventions](#developer-logging-rules--conventions) |
| 18 | +5. [Pre-PR Checklist](#5-pre-pr-checklist) |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## 1. Core Architecture & Component Map |
| 23 | + |
| 24 | +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. |
| 25 | + |
| 26 | +```mermaid |
| 27 | +graph TD |
| 28 | + Client[Client Application / BI Tool] -->|DriverManager.getConnection| Driver[BigQueryDriver] |
| 29 | + Driver -->|Parses URI & Options| UrlUtil[BigQueryJdbcUrlUtility] |
| 30 | + Driver -->|Configures Logging| RootLogger[BigQueryJdbcRootLogger] |
| 31 | + Driver -->|Creates| Conn[BigQueryConnection] |
| 32 | + Conn -->|Dynamic Context Proxy| Proxy[BigQueryJdbcContextProxy] |
| 33 | + Proxy -->|MDC Tracing| Mdc[BigQueryJdbcMdc] |
| 34 | + Proxy -->|Delegates Exec| DirectConn[Client Session] |
| 35 | + Conn -->|Type Mapping & Coercion| Coercion[BigQueryJdbcTypeMappings & BigQueryCoercion] |
| 36 | + Conn -->|REST / Storage API| BQSDK[google-cloud-bigquery] |
| 37 | +``` |
| 38 | + |
| 39 | +### Key Abstractions |
| 40 | + |
| 41 | +- **`BigQueryDriver`**: JDBC entry point registered with `java.sql.DriverManager`. Intercepts `jdbc:bigquery://` URLs, initializes early logger state, and instantiates `BigQueryConnection`. |
| 42 | +- **`BigQueryConnection`**: Represents an active BigQuery session, holding dataset defaults, connection configuration maps, and transaction/session state (`EnableSession=true`, `session_id`). |
| 43 | +- **`BigQueryJdbcUrlUtility`**: Parses and validates connection string parameters using a bounded LRU parse cache (`PARSE_CACHE`) to avoid heavy allocations during frequent connection creation. |
| 44 | +- **`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()`). |
| 45 | +- **`BigQueryJdbcTypeMappings` & `BigQueryCoercion`**: Centralized mapping logic handling standard JDBC-to-BigQuery SQL type mappings (`StandardSQLTypeName`) and object coercions (`Date`, `Timestamp`, `BigDecimal`, etc.). |
| 46 | +- **`BigQueryArrowResultSet`**: Custom result set implementation accelerating large query result retrieval via the BigQuery Storage Read API gRPC stream. |
| 47 | + |
| 48 | +--- |
| 49 | + |
| 50 | +## 2. Developer Guardrails & Rules of Engagement |
| 51 | + |
| 52 | +> [!IMPORTANT] |
| 53 | +> **Adhere strictly to the following guardrails when making code changes:** |
| 54 | +
|
| 55 | +1. **Visibility Principle**: Always default to the most restrictive access level (`private`, package-private, or `@InternalApi`). Do **NOT** expose classes or methods as `public` unless strictly required by standard JDBC interfaces. |
| 56 | +2. **Explicit Class Imports**: Always write explicit `import` statements. Do **NOT** use wildcard star imports or inline fully qualified class names (e.g., use `import java.math.BigDecimal;` instead of `java.math.BigDecimal` inline). |
| 57 | +3. **Logger Preference**: Always prefer `BigQueryJdbcCustomLogger` over `java.util.logging.Logger`. Format strings using `String.format(...)` before logging, as `BigQueryJdbcRootLogger` evaluates `record.getMessage()` directly. |
| 58 | +4. **Exception Handling**: Always throw exceptions from the `com.google.cloud.bigquery.exception` package (`BigQueryJdbcException`, `BigQueryJdbcSqlSyntaxErrorException`, `BigQueryConversionException`). |
| 59 | +5. **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. |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +## 3. Build & Test Playbook |
| 64 | + |
| 65 | +Builds and test tasks are managed via the module [Makefile](Makefile). |
| 66 | + |
| 67 | +### Local Build Commands |
| 68 | + |
| 69 | +```bash |
| 70 | +# Build & install module locally |
| 71 | +make install |
| 72 | + |
| 73 | +# Clean project target directory |
| 74 | +make clean |
| 75 | + |
| 76 | +# Format code and check linter compliance |
| 77 | +make lint |
| 78 | +``` |
| 79 | + |
| 80 | +### Running Unit Tests |
| 81 | + |
| 82 | +```bash |
| 83 | +# Run all unit tests |
| 84 | +make unittest |
| 85 | + |
| 86 | +# Run a specific unit test class |
| 87 | +make unittest test=BigQueryPreparedStatementTest |
| 88 | + |
| 89 | +# Run a specific unit test method |
| 90 | +make unittest test=BigQueryPreparedStatementTest#testSetObjectWithTemporalTypes |
| 91 | +``` |
| 92 | + |
| 93 | +### Running Integration Tests |
| 94 | + |
| 95 | +> [!WARNING] |
| 96 | +> Integration tests connect to real GCP BigQuery resources and require valid GCP credentials. |
| 97 | +
|
| 98 | +```bash |
| 99 | +# Set GCP service account credentials |
| 100 | +export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json |
| 101 | + |
| 102 | +# Run a specific integration test |
| 103 | +make integration-test test=ITBigQueryJDBCTest#testValidServiceAccountAuthenticationOAuthPvtKey |
| 104 | +``` |
| 105 | + |
| 106 | +### Dockerized Execution |
| 107 | + |
| 108 | +If local Java/Maven environments are not available, use the dockerized environment: |
| 109 | + |
| 110 | +```bash |
| 111 | +# Start an interactive shell session inside Docker container |
| 112 | +make docker-session |
| 113 | + |
| 114 | +# Run unit tests inside Docker |
| 115 | +make docker-unittest |
| 116 | +``` |
| 117 | + |
| 118 | +--- |
| 119 | + |
| 120 | +## 4. Logging Architecture & Developer Conventions |
| 121 | + |
| 122 | +The driver uses a custom logging subsystem built on top of `java.util.logging`: `BigQueryJdbcCustomLogger` and `BigQueryJdbcRootLogger`. |
| 123 | + |
| 124 | +### Instantiating Loggers |
| 125 | + |
| 126 | +- **For Instance Components** (`BigQueryConnection`, `BigQueryStatement`, `BigQueryDatabaseMetaData`): |
| 127 | + Use `this.toString()` to include instance identity in logger output: |
| 128 | + ```java |
| 129 | + private final BigQueryJdbcCustomLogger LOG = new BigQueryJdbcCustomLogger(this.toString()); |
| 130 | + ``` |
| 131 | +- **For Static / Utility Components** (`BigQueryJdbcUrlUtility`, `BigQueryJdbcTypeMappings`): |
| 132 | + Use the class name: |
| 133 | + ```java |
| 134 | + private static final BigQueryJdbcCustomLogger LOG = |
| 135 | + new BigQueryJdbcCustomLogger(BigQueryJdbcTypeMappings.class.getName()); |
| 136 | + ``` |
| 137 | + |
| 138 | +### Developer Logging Rules & Conventions |
| 139 | + |
| 140 | +1. **Method Entry / Exit Tracing**: |
| 141 | + Methods at `FINER` level must log entrance and exit points: |
| 142 | + ```java |
| 143 | + public ResultSet executeQuery(String sql) throws SQLException { |
| 144 | + LOG.finer("++enter++"); |
| 145 | + try { |
| 146 | + // ... execution logic ... |
| 147 | + return rs; |
| 148 | + } finally { |
| 149 | + LOG.finer("++exit++"); |
| 150 | + } |
| 151 | + } |
| 152 | + ``` |
| 153 | +2. **Format Placeholders (Zero Allocation)**: |
| 154 | + Avoid string concatenation in log calls. Use formatting placeholders or `Supplier<String>` lambdas to prevent unneeded string allocation when the log level is disabled: |
| 155 | + ```java |
| 156 | + // Recommended: Use printf-style formatting |
| 157 | + LOG.fine("Executing query on dataset: %s, table: %s", datasetId, tableId); |
| 158 | + |
| 159 | + // Recommended: Use supplier lambda for expensive calculations |
| 160 | + LOG.fine(() -> "Parsed properties: " + complexObject.toDebugString()); |
| 161 | + ``` |
| 162 | +3. **Caller Inference & MDC Propagation**: |
| 163 | + - `BigQueryJdbcCustomLogger` automatically wraps log records in `BigQueryJdbcLogRecord`, which inspects the stack trace to accurately infer caller class and method names. |
| 164 | + - `BigQueryJdbcMdc` maintains `connectionId` in a `ThreadLocal` context. When logging from proxy or worker threads, always ensure MDC context is preserved or propagated via `BigQueryJdbcContextProxy`. |
| 165 | + |
| 166 | +--- |
| 167 | + |
| 168 | +## 5. Pre-PR Checklist |
| 169 | + |
| 170 | +Before submitting a Pull Request: |
| 171 | + |
| 172 | +- [ ] All new classes and methods use the narrowest possible visibility scope (`private` or package-private). |
| 173 | +- [ ] No inline fully qualified names or wildcard star imports are present. |
| 174 | +- [ ] All logger instances use `BigQueryJdbcCustomLogger`. |
| 175 | +- [ ] Method entrance/exit logging (`++enter++` / `++exit++`) is included for complex internal routines. |
| 176 | +- [ ] All method changes and feature additions are covered by corresponding JUnit 5 tests. |
| 177 | +- [ ] Unit tests pass cleanly without Mockito `UnnecessaryStubbingException` warnings. |
| 178 | +- [ ] All `Statement`, `ResultSet`, or `DatabaseMetaData` objects returned by public entry points are properly wrapped via `BigQueryJdbcContextProxy.wrap()`. |
| 179 | +- [ ] Code formatting and linting pass via `make lint`. |
0 commit comments