Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
public class BigQueryConversionException extends SQLException {

public BigQueryConversionException(String message, Throwable cause) {
super(BigQueryJdbcExceptionUtils.formatMessage(message, cause), cause);
super(
BigQueryJdbcExceptionUtils.formatMessage(message, cause),
BigQueryJdbcSqlStates.DATA_EXCEPTION,
cause);
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class BigQueryJdbcException extends SQLException {
* @param message The detail message.
*/
public BigQueryJdbcException(String message) {
super(message);
super(message, BigQueryJdbcSqlStates.GENERAL_ERROR);
}

/**
Expand All @@ -37,7 +37,7 @@ public BigQueryJdbcException(String message) {
* @param ex The InterruptedException to be thrown.
*/
public BigQueryJdbcException(InterruptedException ex) {
super(ex);
super(ex.getMessage(), BigQueryJdbcSqlStates.QUERY_CANCELED, ex);
}

/**
Expand All @@ -47,7 +47,10 @@ public BigQueryJdbcException(InterruptedException ex) {
* @param ex The BigQueryException to be thrown.
*/
public BigQueryJdbcException(String message, BigQueryException ex) {
super(BigQueryJdbcExceptionUtils.formatMessage(message, ex), ex);
super(
BigQueryJdbcExceptionUtils.formatMessage(message, ex),
BigQueryJdbcExceptionUtils.sqlStateForCause(ex),
ex);
this.bigQueryException = ex;
}

Expand All @@ -58,7 +61,10 @@ public BigQueryJdbcException(String message, BigQueryException ex) {
* @param cause Throwable that is being converted.
*/
public BigQueryJdbcException(String message, Throwable cause) {
super(BigQueryJdbcExceptionUtils.formatMessage(message, cause), cause);
super(
BigQueryJdbcExceptionUtils.formatMessage(message, cause),
BigQueryJdbcExceptionUtils.sqlStateForCause(cause),
cause);
}

/**
Expand All @@ -68,7 +74,10 @@ public BigQueryJdbcException(String message, Throwable cause) {
* @param cause Throwable that is being converted.
*/
public BigQueryJdbcException(Throwable cause) {
super(cause);
super(
cause == null ? null : cause.getMessage(),
BigQueryJdbcExceptionUtils.sqlStateForCause(cause),
cause);
}

public BigQueryException getBigQueryException() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.google.cloud.bigquery.exception;

import com.google.cloud.bigquery.BigQueryException;

/** Utility class for JDBC exceptions. */
final class BigQueryJdbcExceptionUtils {

Expand All @@ -37,4 +39,49 @@ public static String formatMessage(String message, Throwable cause) {
? "\n" + (cause.getMessage() != null ? cause.getMessage() : cause.toString())
: "");
}

/**
* Maps a cause to a standard SQL:2003 SQLState.
*
* <p>Returns {@code HY000} (general error) for anything unrecognised, so the result is always a
* valid 5-character state and never null.
*
* @param cause the underlying cause, may be null.
* @return a 5-character SQLState.
*/
static String sqlStateForCause(Throwable cause) {
if (!(cause instanceof BigQueryException)) {
return BigQueryJdbcSqlStates.GENERAL_ERROR;
}
String reason = ((BigQueryException) cause).getReason();
if (reason == null) {
return BigQueryJdbcSqlStates.GENERAL_ERROR;
}
switch (reason) {
Comment thread
logachev marked this conversation as resolved.
case "invalidQuery":
case "invalid":
case "badRequest":
return BigQueryJdbcSqlStates.SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION;
case "accessDenied":
return BigQueryJdbcSqlStates.INSUFFICIENT_PRIVILEGE;
case "invalidUser":
return BigQueryJdbcSqlStates.INVALID_AUTHORIZATION;
case "quotaExceeded":
case "rateLimitExceeded":
case "resourcesExceeded":
return BigQueryJdbcSqlStates.INSUFFICIENT_RESOURCES;
case "responseTooLarge":
return BigQueryJdbcSqlStates.PROGRAM_LIMIT_EXCEEDED;
case "stopped":
return BigQueryJdbcSqlStates.QUERY_CANCELED;
case "backendError":
case "internalError":
case "jobInternalError":
return BigQueryJdbcSqlStates.SYSTEM_ERROR;
case "notImplemented":
return BigQueryJdbcSqlStates.FEATURE_NOT_SUPPORTED;
default:
return BigQueryJdbcSqlStates.GENERAL_ERROR;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class BigQueryJdbcSqlFeatureNotSupportedException extends SQLFeatureNotSu
* @param message The detail message.
*/
public BigQueryJdbcSqlFeatureNotSupportedException(String message) {
super(message);
super(message, BigQueryJdbcSqlStates.FEATURE_NOT_SUPPORTED);
}

/**
Expand All @@ -36,6 +36,6 @@ public BigQueryJdbcSqlFeatureNotSupportedException(String message) {
* @param ex The BigQueryException to be thrown.
*/
public BigQueryJdbcSqlFeatureNotSupportedException(BigQueryException ex) {
super(ex);
super(ex.getMessage(), BigQueryJdbcSqlStates.FEATURE_NOT_SUPPORTED, ex);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.bigquery.exception;

/**
* Standard SQL:2003 SQLState codes used by the driver.
*
* <p>A SQLState is a 5-character code: a 2-character class followed by a 3-character subclass.
* These values are defined by the SQL standard and are portable across databases; nothing here is
* BigQuery-specific. {@code BigQueryDatabaseMetaData.getSQLStateType()} declares that the driver
* emits SQL:2003 states, so do not mix in X/Open or ODBC-only codes.
*/
final class BigQueryJdbcSqlStates {

/** 08 — connection exception. */
static final String CONNECTION_EXCEPTION = "08006";

/** 0A — feature not supported. */
static final String FEATURE_NOT_SUPPORTED = "0A000";

/** 22 — data exception (bad value, failed conversion). */
static final String DATA_EXCEPTION = "22000";

/** 28 — invalid authorization specification (authentication failed). */
static final String INVALID_AUTHORIZATION = "28000";

/** 42 — syntax error or access rule violation. */
static final String SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION = "42000";

static final String INSUFFICIENT_PRIVILEGE = "42501";

/** 53 — insufficient resources. */
static final String INSUFFICIENT_RESOURCES = "53000";

/** 54 — program limit exceeded. */
static final String PROGRAM_LIMIT_EXCEEDED = "54000";

/** 57 — operator intervention. */
static final String QUERY_CANCELED = "57014";

/** 58 — system error. */
static final String SYSTEM_ERROR = "58000";

/** HY — general error; the fallback when nothing more specific applies. */
static final String GENERAL_ERROR = "HY000";

private BigQueryJdbcSqlStates() {
// Utility class, prevent instantiation
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ public class BigQueryJdbcSqlSyntaxErrorException extends SQLSyntaxErrorException
* @param ex The BigQueryException to be thrown.
*/
public BigQueryJdbcSqlSyntaxErrorException(BigQueryException ex) {
super(ex.getMessage(), "Incorrect SQL syntax.");
super(ex.getMessage(), BigQueryJdbcSqlStates.SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION, ex);
}

public BigQueryJdbcSqlSyntaxErrorException(String message, BigQueryException ex) {
super(BigQueryJdbcExceptionUtils.formatMessage(message, ex), ex);
super(
BigQueryJdbcExceptionUtils.formatMessage(message, ex),
BigQueryJdbcSqlStates.SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION,
ex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.google.cloud.bigquery.jdbc;

import com.google.cloud.bigquery.jdbc.telemetry.v1.TelemetryManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -177,6 +178,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
LOG.severe("Exception occurred during " + methodName + ": " + errMsg, cause);
}

TelemetryManager.recordError(
TelemetryManager.extractErrorCode(cause),
TelemetryManager.extractSqlState(cause),
methodName);

throw cause;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldes
static final String USE_GLOBAL_OTEL_PROPERTY_NAME = "useGlobalOpenTelemetry";
static final boolean DEFAULT_USE_GLOBAL_OTEL_VALUE = false;
static final String ENABLE_DIAGNOSTIC_TELEMETRY_PROPERTY_NAME = "EnableDiagnosticTelemetry";
static final boolean DEFAULT_ENABLE_DIAGNOSTIC_TELEMETRY_VALUE = true;
private static final BigQueryJdbcCustomLogger LOG =
new BigQueryJdbcCustomLogger(BigQueryJdbcUrlUtility.class.getName());
static final String FILTER_TABLES_ON_DEFAULT_DATASET_PROPERTY_NAME =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,9 @@ void runQuery(String query, QueryJobConfiguration jobConfiguration)
throw new BigQueryJdbcSqlSyntaxErrorException("BigQueryException during runQuery", ex);
}
throw new BigQueryJdbcException("BigQueryException during runQuery", ex);
} catch (SQLException | RuntimeException ex) {
errorCode = TelemetryManager.extractErrorCode(ex);
throw ex;
} finally {
long durationMs = System.currentTimeMillis() - startTime;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.cloud.bigquery.exception.BigQueryJdbcException;
import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException;
import com.google.cloud.bigquery.jdbc.telemetry.v1.TelemetryPropertyUtility;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
Expand Down Expand Up @@ -1590,7 +1591,7 @@ public Boolean getEnableDiagnosticTelemetry() {
if (this.enableDiagnosticTelemetry != null) {
return this.enableDiagnosticTelemetry;
}
return BigQueryJdbcUrlUtility.DEFAULT_ENABLE_DIAGNOSTIC_TELEMETRY_VALUE;
return TelemetryPropertyUtility.DEFAULT_ENABLE_DIAGNOSTIC_TELEMETRY_VALUE;
}

public void setEnableDiagnosticTelemetry(Boolean enableDiagnosticTelemetry) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ TransportResult send(TelemetryPayload payload) {
long now = System.currentTimeMillis();
LogRequest logRequest =
LogRequest.newBuilder()
.setClientInfo(
ClientInfo.newBuilder()
.setClientType(TelemetryConfiguration.DEFAULT_CLIENT_TYPE)
.build())
.setLogSource(config.getLogSource())
.setRequestTimeMs(now)
.addLogEvents(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@
import java.util.logging.Logger;

/** Utility builder for constructing {@link DriverEnvironment} telemetry protos. */
final class DriverEnvironmentBuilder {
private static final Logger logger = Logger.getLogger(DriverEnvironmentBuilder.class.getName());
final class DriverEnvironmentDetector {
private static final Logger logger = Logger.getLogger(DriverEnvironmentDetector.class.getName());

static final String DRIVER_NAME = "google-bigquery-jdbc-driver";
static final String DRIVER_NAME = "Google-BigQuery-JDBC-Driver";
static final String CLIENT_LANGUAGE = "java";
static final String DEFAULT_TELEMETRY_TAG_DIR = ".bigquery-jdbc";
static final String DEFAULT_TELEMETRY_TAG_FILE = "telemetry-tag";
static final String UNKNOWN = "unknown";
static final String RESTRICTED = "restricted";

private DriverEnvironmentBuilder() {}
private DriverEnvironmentDetector() {}

static DriverEnvironment build() {
return build(null);
Expand Down Expand Up @@ -171,7 +171,7 @@ static String getOrCreateTelemetryTag(Path customFilePath) {
logger.log(Level.WARNING, "Failed to persist telemetry tag to file", e);
}
return newId;
} catch (SecurityException e) {
} catch (RuntimeException e) {
return UUID.randomUUID().toString();
}
}
Expand Down
Loading
Loading