Skip to content

Commit

Permalink
[#2227] improvement(jdbc-backend): Improve the judgment of exception …
Browse files Browse the repository at this point in the history
…information in JDBC backend (#2862)

### What changes were proposed in this pull request?

Determine exceptions more accurately based on SQL Exception error codes.

### Why are the changes needed?

Fix: #2227

### How was this patch tested?

Add the unit tests.

---------

Co-authored-by: xiaojiebao <[email protected]>
  • Loading branch information
xloya and xiaojiebao authored Apr 14, 2024
1 parent 3bccc65 commit 9128db0
Show file tree
Hide file tree
Showing 13 changed files with 619 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.datastrato.gravitino.meta.SchemaEntity;
import com.datastrato.gravitino.meta.TableEntity;
import com.datastrato.gravitino.meta.TopicEntity;
import com.datastrato.gravitino.storage.relational.converters.SQLExceptionConverterFactory;
import com.datastrato.gravitino.storage.relational.service.CatalogMetaService;
import com.datastrato.gravitino.storage.relational.service.FilesetMetaService;
import com.datastrato.gravitino.storage.relational.service.MetalakeMetaService;
Expand All @@ -44,6 +45,7 @@ public class JDBCBackend implements RelationalBackend {
@Override
public void initialize(Config config) {
SqlSessionFactoryHelper.getInstance().init(config);
SQLExceptionConverterFactory.initConverter(config);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.storage.relational.converters;

import com.datastrato.gravitino.Entity;
import com.datastrato.gravitino.exceptions.AlreadyExistsException;
import com.datastrato.gravitino.exceptions.GravitinoRuntimeException;
import java.sql.SQLException;

/**
* Exception converter to Gravitino exception for H2. The definition of error codes can be found in
* the document: <a href="https://h2database.com/javadoc/org/h2/api/ErrorCode.html"></a>
*/
public class H2ExceptionConverter implements SQLExceptionConverter {
/** It means found a duplicated primary key or unique key entry in H2. */
private static final int DUPLICATED_ENTRY_ERROR_CODE = 23505;

@SuppressWarnings("FormatStringAnnotation")
@Override
public GravitinoRuntimeException toGravitinoException(
SQLException se, Entity.EntityType type, String name) {
switch (se.getErrorCode()) {
case DUPLICATED_ENTRY_ERROR_CODE:
return new AlreadyExistsException(se, se.getMessage());
default:
return new GravitinoRuntimeException(se, se.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.storage.relational.converters;

import com.datastrato.gravitino.Entity;
import com.datastrato.gravitino.exceptions.AlreadyExistsException;
import com.datastrato.gravitino.exceptions.GravitinoRuntimeException;
import java.sql.SQLException;

/**
* Exception converter to Gravitino exception for MySQL. The definition of error codes can be found
* in the document: <a
* href="https://dev.mysql.com/doc/connector-j/en/connector-j-reference-error-sqlstates.html"></a>
*/
public class MySQLExceptionConverter implements SQLExceptionConverter {
/** It means found a duplicated primary key or unique key entry in MySQL. */
private static final int DUPLICATED_ENTRY_ERROR_CODE = 1062;

@SuppressWarnings("FormatStringAnnotation")
@Override
public GravitinoRuntimeException toGravitinoException(
SQLException se, Entity.EntityType type, String name) {
switch (se.getErrorCode()) {
case DUPLICATED_ENTRY_ERROR_CODE:
return new AlreadyExistsException(se, se.getMessage());
default:
return new GravitinoRuntimeException(se, se.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.storage.relational.converters;

import com.datastrato.gravitino.Entity;
import com.datastrato.gravitino.exceptions.GravitinoRuntimeException;
import java.sql.SQLException;

/** Interface for converter JDBC SQL exceptions to Gravitino exceptions. */
public interface SQLExceptionConverter {
/**
* Convert JDBC exception to GravitinoException.
*
* @param sqlException The sql exception to map
* @param type The type of the entity
* @param name The name of the entity
* @return A best attempt at a corresponding jdbc connector exception or generic with the
* SQLException as the cause
*/
GravitinoRuntimeException toGravitinoException(
SQLException sqlException, Entity.EntityType type, String name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.storage.relational.converters;

import com.datastrato.gravitino.Config;
import com.datastrato.gravitino.Configs;
import com.google.common.base.Preconditions;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SQLExceptionConverterFactory {
private static final Pattern TYPE_PATTERN = Pattern.compile("jdbc:(\\w+):");
private static SQLExceptionConverter converter;

private SQLExceptionConverterFactory() {}

public static synchronized void initConverter(Config config) {
if (converter == null) {
String jdbcUrl = config.get(Configs.ENTITY_RELATIONAL_JDBC_BACKEND_URL);
Matcher typeMatcher = TYPE_PATTERN.matcher(jdbcUrl);
if (typeMatcher.find()) {
String jdbcType = typeMatcher.group(1);
if (jdbcType.equalsIgnoreCase("mysql")) {
converter = new MySQLExceptionConverter();
} else if (jdbcType.equalsIgnoreCase("h2")) {
converter = new H2ExceptionConverter();
} else {
throw new IllegalArgumentException(String.format("Unsupported jdbc type: %s", jdbcType));
}
} else {
throw new IllegalArgumentException(
String.format("Cannot find jdbc type in jdbc url: %s", jdbcUrl));
}
}
}

public static SQLExceptionConverter getConverter() {
Preconditions.checkState(converter != null, "Exception converter is not initialized.");
return converter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public void insertCatalog(CatalogEntity catalogEntity, boolean overwrite) {
}
});
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.CATALOG, catalogEntity.nameIdentifier().toString());
throw re;
}
Expand Down Expand Up @@ -147,7 +147,7 @@ public <E extends Entity & HasIdentifier> CatalogEntity updateCatalog(
POConverters.updateCatalogPOWithVersion(oldCatalogPO, newEntity, metalakeId),
oldCatalogPO));
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.CATALOG, newEntity.nameIdentifier().toString());
throw re;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void insertFileset(FilesetEntity filesetEntity, boolean overwrite) {
}
}));
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.FILESET, filesetEntity.nameIdentifier().toString());
throw re;
}
Expand Down Expand Up @@ -177,7 +177,7 @@ public <E extends Entity & HasIdentifier> FilesetEntity updateFileset(
mapper -> mapper.updateFilesetMeta(newFilesetPO, oldFilesetPO));
}
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.FILESET, newEntity.nameIdentifier().toString());
throw re;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public void insertMetalake(BaseMetalake baseMetalake, boolean overwrite) {
}
});
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.METALAKE, baseMetalake.nameIdentifier().toString());
throw re;
}
Expand Down Expand Up @@ -125,7 +125,7 @@ public <E extends Entity & HasIdentifier> BaseMetalake updateMetalake(
MetalakeMetaMapper.class,
mapper -> mapper.updateMetalakeMeta(newMetalakePO, oldMetalakePO));
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.METALAKE, newMetalakeEntity.nameIdentifier().toString());
throw re;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public void insertSchema(SchemaEntity schemaEntity, boolean overwrite) {
}
});
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.SCHEMA, schemaEntity.nameIdentifier().toString());
throw re;
}
Expand Down Expand Up @@ -142,7 +142,7 @@ public <E extends Entity & HasIdentifier> SchemaEntity updateSchema(
mapper.updateSchemaMeta(
POConverters.updateSchemaPOWithVersion(oldSchemaPO, newEntity), oldSchemaPO));
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.SCHEMA, newEntity.nameIdentifier().toString());
throw re;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public void insertTable(TableEntity tableEntity, boolean overwrite) {
}
});
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.TABLE, tableEntity.nameIdentifier().toString());
throw re;
}
Expand Down Expand Up @@ -135,7 +135,7 @@ public <E extends Entity & HasIdentifier> TableEntity updateTable(
mapper.updateTableMeta(
POConverters.updateTablePOWithVersion(oldTablePO, newEntity), oldTablePO));
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.TABLE, newEntity.nameIdentifier().toString());
throw re;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void insertTopic(TopicEntity topicEntity, boolean overwrite) {
});
// TODO: insert topic dataLayout version after supporting it
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.TOPIC, topicEntity.nameIdentifier().toString());
throw re;
}
Expand Down Expand Up @@ -97,7 +97,7 @@ public <E extends Entity & HasIdentifier> TopicEntity updateTopic(
mapper.updateTopicMeta(
POConverters.updateTopicPOWithVersion(oldTopicPO, newEntity), oldTopicPO));
} catch (RuntimeException re) {
ExceptionUtils.checkSQLConstraintException(
ExceptionUtils.checkSQLException(
re, Entity.EntityType.TOPIC, newEntity.nameIdentifier().toString());
throw re;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,17 @@
package com.datastrato.gravitino.storage.relational.utils;

import com.datastrato.gravitino.Entity;
import com.datastrato.gravitino.exceptions.AlreadyExistsException;
import java.sql.SQLIntegrityConstraintViolationException;
import com.datastrato.gravitino.storage.relational.converters.SQLExceptionConverterFactory;
import java.sql.SQLException;

public class ExceptionUtils {
private ExceptionUtils() {}

public static void checkSQLConstraintException(
public static void checkSQLException(
RuntimeException re, Entity.EntityType type, String entityName) {
if (re.getCause() != null
&& re.getCause() instanceof SQLIntegrityConstraintViolationException) {
// TODO We should make more fine-grained exception judgments
// Usually throwing `SQLIntegrityConstraintViolationException` means that
// SQL violates the constraints of `primary key` and `unique key`.
// We simply think that the entity already exists at this time.
throw new AlreadyExistsException("%s entity: %s already exists", type.name(), entityName);
if (re.getCause() instanceof SQLException) {
throw SQLExceptionConverterFactory.getConverter()
.toGravitinoException((SQLException) re.getCause(), type, entityName);
}
}
}
Loading

0 comments on commit 9128db0

Please sign in to comment.