Plan/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBOpException.java

175 lines
6.7 KiB
Java

/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.exceptions.database;
import com.djrapitops.plan.exceptions.ExceptionWithContext;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import java.sql.SQLException;
import java.util.Optional;
/**
* Runtime exception for wrapping database errors.
*
* @author AuroraLS3
*/
public class DBOpException extends IllegalStateException implements ExceptionWithContext {
public static final String CONSTRAINT_VIOLATION = "Constraint Violation";
public static final String DUPLICATE_KEY = "Duplicate key";
private final ErrorContext context;
public DBOpException(String message) {
super(message);
this.context = null;
}
public DBOpException(String message, Throwable cause) {
this(message, cause, null);
}
public DBOpException(String message, Throwable cause, ErrorContext context) {
super(message, cause);
this.context = context;
}
// Checkstyle.OFF: CyclomaticComplexity
public static DBOpException forCause(String sql, SQLException e) {
ErrorContext.Builder context = ErrorContext.builder();
int errorCode = e.getErrorCode();
context.related("Error code: " + errorCode)
.related(sql);
switch (errorCode) {
// SQLite Corrupt
case 10:
case 523:
context.related("SQL Corrupted")
.whatToDo("Your SQLite has corrupted. This can happen if .db-wal or .db-shm files get replaced mid operation. Restore database.db from backup.");
break;
// Syntax error codes
case 1054: // MySQL
case 1064:
case 1146:
context.related("SQL Grammar error")
.whatToDo("Report this, there is an SQL grammar error.");
break;
// Type mismatch
case 20: // SQLite
context.related("SQL Type mismatch")
.whatToDo("Report this, there is an SQL Type mismatch.");
break;
// Duplicate key
case 1062:
case 1022:
case 23001:
case 23505:
context.related(DUPLICATE_KEY)
.whatToDo("Report this, duplicate key exists in SQL.");
break;
// Constraint violation
case 19: // SQLite
case 275:
case 531:
case 787:
case 1043:
case 1555:
case 2579:
case 1811:
case 2067:
case 2323:
case 630: // MySQL
case 839:
case 840:
case 893:
case 1169:
case 1215:
case 1216:
case 1217:
case 1364:
case 1451:
case 1557:
context.related(CONSTRAINT_VIOLATION)
.whatToDo("Report this, there is an SQL Constraint Violation.");
break;
// Custom rules based on reported errors
case 11:
case 14:
context.related("SQLite file is corrupt.")
.whatToDo("SQLite database is corrupt, restore database.db, .db-shm & .db-wal files from a backup, or repair the database: See https://wordpress.semnaitik.com/repair-sqlite-database/.");
break;
case 13: // SQLite
case 1021: // MariaDB
context.related("Disk or temporary directory is full.")
.whatToDo("Disk or temporary directory is full, attempt to clear space in the temporary directory. See https://sqlite.org/rescode.html#full. If you use the Pterodactyl panel, increase the \"tmpfs_size\" config setting. See https://pterodactyl.io/wings/1.0/configuration.html#other-values");
break;
case 1104:
context.whatToDo("MySQL has too small query limits for the query. SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# (higher number)");
break;
case 1142:
context.related("Missing privilege")
.whatToDo("Grant the required privileges to your MySQL user (often 'REFERENCES' privilege is missing).");
break;
case 1213:
context.related("Deadlock");
break;
case 1267:
case 1366:
case 1115:
context.related("Incorrect character encoding in MySQL")
.whatToDo("Convert your MySQL database and tables to use utf8mb4: https://www.a2hosting.com/kb/developer-corner/mysql/convert-mysql-database-utf-8");
break;
case 1299: // SQLite
case 1048: // MySQL or MariaDB
case 1452:
case 1121:
case 1171:
case 1830:
case 1263:
context.related(CONSTRAINT_VIOLATION)
.whatToDo("Report this error. NOT NULL constraint violation occurred.");
break;
case 1071:
context.related("column byte length exceeded")
.whatToDo("Update your MySQL, column key size was exceeded (max key length is 767 bytes in 5.6) - MySQL 5.7 increases the limit.");
break;
default:
context.related("Unknown SQL Error code");
}
return new DBOpException("SQL Failure: " + e.getMessage(), e, context.build());
}
// Checkstyle.ON: CyclomaticComplexity
@Override
public Optional<ErrorContext> getContext() {
return Optional.ofNullable(context);
}
public boolean isUserIdConstraintViolation() {
return context != null
&& context.getRelated().contains(DBOpException.CONSTRAINT_VIOLATION)
&& getCause() != null
&& getCause().getMessage().contains("user_id");
}
public boolean isDuplicateKeyViolation() {
return context != null
&& context.getRelated().contains(DBOpException.CONSTRAINT_VIOLATION);
}
}