Skip to content

bigquery-jdbc: Storage Write batch throws NPE for setObject(index, null) #14066

Description

@cliffsun91

Issue Details

With Google BigQuery JDBC driver 1.2.0, a batch insert using the BigQuery Storage Write API throws a NullPointerException when a parameter is bound using PreparedStatement.setObject(index, null). The issue was reproduced using a nullable STRING column.

The expected behaviour is for the driver to insert SQL NULL.

The equivalent batch succeeds when EnableWriteAPI=false, indicating that null binding works through the standard SQL DML path and that the failure is specific to Storage Write serialization.

I searched the existing issues but did not find one describing this failure.

Environment

  • OS Type and Version: macOS (Darwin 25.6.0)
  • Java Version and JDK Vendor: Java 17; JDK vendor not captured
  • (If using GraalVM) GraalVM Version: N/A

GCP environment:

  • Project ID: N/A — omitted from this public report
  • Cloud Services: BigQuery and BigQuery Storage Write API
  • Deployment Environment: Local JVM process; not deployed on GCP

Dependencies

Relevant library versions:

  • Google BigQuery JDBC driver: com.google.cloud:google-cloud-bigquery-jdbc:1.2.0
  • Libraries-Bom: N/A
  • Gax: N/A — not directly declared
  • Auth: N/A — not directly declared
  • Other relevant Java SDK dependencies: N/A
  • Google-Http-Java-Client: N/A — not directly declared

Reproducer

Steps to reproduce

  1. Enable the BigQuery and BigQuery Storage Write APIs in a disposable GCP project.
  2. Create a table containing one nullable STRING column:
CREATE TABLE `<PROJECT_ID>.<DATASET_ID>.jdbc_null_repro` (
  value STRING
);
  1. Authenticate using a service-account JSON key file.
  2. Run the following program after replacing the placeholder project, dataset and credential-path values:
import com.google.cloud.bigquery.jdbc.BigQueryDriver;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Properties;

public final class BigQueryNullBatchReproducer {
  private static final String PROJECT_ID = "<PROJECT_ID>";
  private static final String DATASET_ID = "<DATASET_ID>";
  private static final String SERVICE_ACCOUNT_JSON =
      "<SERVICE_ACCOUNT_JSON_PATH>";

  public static void main(String[] args) throws Exception {
    String jdbcUrl =
        "jdbc:bigquery://https://bigquery.googleapis.com:443"
            + ";ProjectId=" + PROJECT_ID;

    Properties properties = new Properties();
    properties.setProperty("OAuthType", "0");
    properties.setProperty("OAuthPvtKeyPath", SERVICE_ACCOUNT_JSON);
    properties.setProperty("EnableWriteAPI", "true");
    properties.setProperty("SWA_ActivationRowCount", "1");

    String sql =
        "INSERT INTO `" + PROJECT_ID + "." + DATASET_ID
            + ".jdbc_null_repro` (value) VALUES (?)";

    BigQueryDriver driver = new BigQueryDriver();

    try (Connection connection = driver.connect(jdbcUrl, properties);
         PreparedStatement statement = connection.prepareStatement(sql)) {
      statement.setObject(1, null);
      statement.addBatch();
      statement.executeBatch();
    }
  }
}
  1. executeBatch() throws a NullPointerException.
  2. As a control, set EnableWriteAPI=false. The null value is inserted successfully.

Expected result: one row containing SQL NULL is inserted.

Actual result: the Storage Write API path throws a NullPointerException.

Logs and Stack Trace

Unrelated SLF4J and gRPC initialization messages have been omitted.

Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "Object.toString()" because the return value of
"com.google.cloud.bigquery.jdbc.BigQueryJdbcParameter.getValue()" is null
    at com.google.cloud.bigquery.jdbc.BigQueryPreparedStatement.bulkInsertWithWriteAPI(BigQueryPreparedStatement.java:396)
    at com.google.cloud.bigquery.jdbc.BigQueryPreparedStatement.executeBatch(BigQueryPreparedStatement.java:329)

Behavior

  • When did the issue begin? Is this related to a dependency upgrade?
    • It was found while evaluating migration to Google BigQuery JDBC 1.2.0. It was not caused by an upgrade in a production deployment.
  • Is the behaviour flaky?
    • No. It reproduces consistently.
  • Is the behaviour related to the volume of data?
    • No. A one-row batch triggers it when SWA_ActivationRowCount=1.
  • The equivalent null batch succeeds when the Storage Write API is disabled.
  • Authentication and connection establishment complete successfully before the failure.

The exception appears to originate from the STRING serialization branch in bulkInsertWithWriteAPI, which calls parameter.getValue().toString() without first checking whether the value is null.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions