FlywaySqlException.java
/*-
* ========================LICENSE_START=================================
* flyway-core
* ========================================================================
* Copyright (C) 2010 - 2025 Red Gate Software Ltd
* ========================================================================
* 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.
* =========================LICENSE_END==================================
*/
package org.flywaydb.core.internal.exception;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import lombok.Getter;
import lombok.SneakyThrows;
import org.flywaydb.core.api.CoreErrorCode;
import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.internal.database.DatabaseTypeRegister;
import org.flywaydb.core.internal.exception.sqlExceptions.FlywaySqlErrorCode;
import org.flywaydb.core.internal.exception.sqlExceptions.FlywaySqlNoDriversForInteractiveAuthException;
import org.flywaydb.core.internal.exception.sqlExceptions.FlywaySqlNoIntegratedAuthException;
import org.flywaydb.core.internal.exception.sqlExceptions.FlywaySqlServerUntrustedCertificateSqlException;
import org.flywaydb.core.internal.jdbc.DriverDataSource;
import org.flywaydb.core.internal.util.ExceptionUtils;
import org.flywaydb.core.internal.util.StringUtils;
/**
* The specific exception thrown when Flyway encounters a problem in SQL.
*/
@Getter
public class FlywaySqlException extends FlywayException {
private final FlywaySqlErrorCode subErrorCode;
private final String sqlState;
private final int sqlErrorCode;
private final Throwable innerCause;
@SuppressWarnings("ClassReferencesSubclass")
private static List<Class<? extends FlywaySqlException>> getSpecificFlywaySqlExceptionClasses() {
return List.of(FlywaySqlServerUntrustedCertificateSqlException.class,
FlywaySqlNoIntegratedAuthException.class,
FlywaySqlNoDriversForInteractiveAuthException.class);
}
public FlywaySqlException(final String message, final SQLException sqlException) {
this(message, sqlException, null);
}
public FlywaySqlException(final String message,
final SQLException sqlException,
final FlywaySqlErrorCode subErrorCode) {
super(message, sqlException, CoreErrorCode.DB_CONNECTION);
this.subErrorCode = subErrorCode;
sqlState = sqlException.getSQLState();
sqlErrorCode = sqlException.getErrorCode();
innerCause = sqlException.getCause();
}
@Override
public String getMessage() {
final String title = super.getMessage();
final String underline = StringUtils.trimOrPad("", title.length(), '-');
return title + "\n" + underline + "\n" + ExceptionUtils.toMessage((SQLException) getCause());
}
@SneakyThrows
public static void throwFlywayExceptionIfPossible(final SQLException sqlException, final DataSource dataSource) {
for (final Class<? extends FlywaySqlException> x : getSpecificFlywaySqlExceptionClasses()) {
if ((boolean) x.getMethod("isFlywaySpecificVersionOf", SQLException.class).invoke(null, sqlException)) {
throw x.getDeclaredConstructor(SQLException.class, DataSource.class)
.newInstance(sqlException, dataSource);
}
}
}
protected static String getDataSourceInfo(final DataSource dataSource, final boolean suppressNullUserMessage) {
if (!(dataSource instanceof final DriverDataSource driverDataSource)) {
return "";
}
final String user = driverDataSource.getUser();
String info = " (" + DatabaseTypeRegister.redactJdbcUrl(driverDataSource.getUrl()) + ")";
if (user != null || !suppressNullUserMessage) {
info += " for user '" + user + "'";
}
return info;
}
}