Skip to content

Commit 9128db0

Browse files
xloyaxiaojiebao
and
xiaojiebao
authored
[#2227] improvement(jdbc-backend): Improve the judgment of exception 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 <xiaojiebao@xiaomi.com>
1 parent 3bccc65 commit 9128db0

13 files changed

+619
-22
lines changed

core/src/main/java/com/datastrato/gravitino/storage/relational/JDBCBackend.java

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.datastrato.gravitino.meta.SchemaEntity;
2222
import com.datastrato.gravitino.meta.TableEntity;
2323
import com.datastrato.gravitino.meta.TopicEntity;
24+
import com.datastrato.gravitino.storage.relational.converters.SQLExceptionConverterFactory;
2425
import com.datastrato.gravitino.storage.relational.service.CatalogMetaService;
2526
import com.datastrato.gravitino.storage.relational.service.FilesetMetaService;
2627
import com.datastrato.gravitino.storage.relational.service.MetalakeMetaService;
@@ -44,6 +45,7 @@ public class JDBCBackend implements RelationalBackend {
4445
@Override
4546
public void initialize(Config config) {
4647
SqlSessionFactoryHelper.getInstance().init(config);
48+
SQLExceptionConverterFactory.initConverter(config);
4749
}
4850

4951
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2024 Datastrato Pvt Ltd.
3+
* This software is licensed under the Apache License version 2.
4+
*/
5+
package com.datastrato.gravitino.storage.relational.converters;
6+
7+
import com.datastrato.gravitino.Entity;
8+
import com.datastrato.gravitino.exceptions.AlreadyExistsException;
9+
import com.datastrato.gravitino.exceptions.GravitinoRuntimeException;
10+
import java.sql.SQLException;
11+
12+
/**
13+
* Exception converter to Gravitino exception for H2. The definition of error codes can be found in
14+
* the document: <a href="https://h2database.com/javadoc/org/h2/api/ErrorCode.html"></a>
15+
*/
16+
public class H2ExceptionConverter implements SQLExceptionConverter {
17+
/** It means found a duplicated primary key or unique key entry in H2. */
18+
private static final int DUPLICATED_ENTRY_ERROR_CODE = 23505;
19+
20+
@SuppressWarnings("FormatStringAnnotation")
21+
@Override
22+
public GravitinoRuntimeException toGravitinoException(
23+
SQLException se, Entity.EntityType type, String name) {
24+
switch (se.getErrorCode()) {
25+
case DUPLICATED_ENTRY_ERROR_CODE:
26+
return new AlreadyExistsException(se, se.getMessage());
27+
default:
28+
return new GravitinoRuntimeException(se, se.getMessage());
29+
}
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2024 Datastrato Pvt Ltd.
3+
* This software is licensed under the Apache License version 2.
4+
*/
5+
package com.datastrato.gravitino.storage.relational.converters;
6+
7+
import com.datastrato.gravitino.Entity;
8+
import com.datastrato.gravitino.exceptions.AlreadyExistsException;
9+
import com.datastrato.gravitino.exceptions.GravitinoRuntimeException;
10+
import java.sql.SQLException;
11+
12+
/**
13+
* Exception converter to Gravitino exception for MySQL. The definition of error codes can be found
14+
* in the document: <a
15+
* href="https://dev.mysql.com/doc/connector-j/en/connector-j-reference-error-sqlstates.html"></a>
16+
*/
17+
public class MySQLExceptionConverter implements SQLExceptionConverter {
18+
/** It means found a duplicated primary key or unique key entry in MySQL. */
19+
private static final int DUPLICATED_ENTRY_ERROR_CODE = 1062;
20+
21+
@SuppressWarnings("FormatStringAnnotation")
22+
@Override
23+
public GravitinoRuntimeException toGravitinoException(
24+
SQLException se, Entity.EntityType type, String name) {
25+
switch (se.getErrorCode()) {
26+
case DUPLICATED_ENTRY_ERROR_CODE:
27+
return new AlreadyExistsException(se, se.getMessage());
28+
default:
29+
return new GravitinoRuntimeException(se, se.getMessage());
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2024 Datastrato Pvt Ltd.
3+
* This software is licensed under the Apache License version 2.
4+
*/
5+
package com.datastrato.gravitino.storage.relational.converters;
6+
7+
import com.datastrato.gravitino.Entity;
8+
import com.datastrato.gravitino.exceptions.GravitinoRuntimeException;
9+
import java.sql.SQLException;
10+
11+
/** Interface for converter JDBC SQL exceptions to Gravitino exceptions. */
12+
public interface SQLExceptionConverter {
13+
/**
14+
* Convert JDBC exception to GravitinoException.
15+
*
16+
* @param sqlException The sql exception to map
17+
* @param type The type of the entity
18+
* @param name The name of the entity
19+
* @return A best attempt at a corresponding jdbc connector exception or generic with the
20+
* SQLException as the cause
21+
*/
22+
GravitinoRuntimeException toGravitinoException(
23+
SQLException sqlException, Entity.EntityType type, String name);
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2024 Datastrato Pvt Ltd.
3+
* This software is licensed under the Apache License version 2.
4+
*/
5+
package com.datastrato.gravitino.storage.relational.converters;
6+
7+
import com.datastrato.gravitino.Config;
8+
import com.datastrato.gravitino.Configs;
9+
import com.google.common.base.Preconditions;
10+
import java.util.regex.Matcher;
11+
import java.util.regex.Pattern;
12+
13+
public class SQLExceptionConverterFactory {
14+
private static final Pattern TYPE_PATTERN = Pattern.compile("jdbc:(\\w+):");
15+
private static SQLExceptionConverter converter;
16+
17+
private SQLExceptionConverterFactory() {}
18+
19+
public static synchronized void initConverter(Config config) {
20+
if (converter == null) {
21+
String jdbcUrl = config.get(Configs.ENTITY_RELATIONAL_JDBC_BACKEND_URL);
22+
Matcher typeMatcher = TYPE_PATTERN.matcher(jdbcUrl);
23+
if (typeMatcher.find()) {
24+
String jdbcType = typeMatcher.group(1);
25+
if (jdbcType.equalsIgnoreCase("mysql")) {
26+
converter = new MySQLExceptionConverter();
27+
} else if (jdbcType.equalsIgnoreCase("h2")) {
28+
converter = new H2ExceptionConverter();
29+
} else {
30+
throw new IllegalArgumentException(String.format("Unsupported jdbc type: %s", jdbcType));
31+
}
32+
} else {
33+
throw new IllegalArgumentException(
34+
String.format("Cannot find jdbc type in jdbc url: %s", jdbcUrl));
35+
}
36+
}
37+
}
38+
39+
public static SQLExceptionConverter getConverter() {
40+
Preconditions.checkState(converter != null, "Exception converter is not initialized.");
41+
return converter;
42+
}
43+
}

core/src/main/java/com/datastrato/gravitino/storage/relational/service/CatalogMetaService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public void insertCatalog(CatalogEntity catalogEntity, boolean overwrite) {
112112
}
113113
});
114114
} catch (RuntimeException re) {
115-
ExceptionUtils.checkSQLConstraintException(
115+
ExceptionUtils.checkSQLException(
116116
re, Entity.EntityType.CATALOG, catalogEntity.nameIdentifier().toString());
117117
throw re;
118118
}
@@ -147,7 +147,7 @@ public <E extends Entity & HasIdentifier> CatalogEntity updateCatalog(
147147
POConverters.updateCatalogPOWithVersion(oldCatalogPO, newEntity, metalakeId),
148148
oldCatalogPO));
149149
} catch (RuntimeException re) {
150-
ExceptionUtils.checkSQLConstraintException(
150+
ExceptionUtils.checkSQLException(
151151
re, Entity.EntityType.CATALOG, newEntity.nameIdentifier().toString());
152152
throw re;
153153
}

core/src/main/java/com/datastrato/gravitino/storage/relational/service/FilesetMetaService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public void insertFileset(FilesetEntity filesetEntity, boolean overwrite) {
122122
}
123123
}));
124124
} catch (RuntimeException re) {
125-
ExceptionUtils.checkSQLConstraintException(
125+
ExceptionUtils.checkSQLException(
126126
re, Entity.EntityType.FILESET, filesetEntity.nameIdentifier().toString());
127127
throw re;
128128
}
@@ -177,7 +177,7 @@ public <E extends Entity & HasIdentifier> FilesetEntity updateFileset(
177177
mapper -> mapper.updateFilesetMeta(newFilesetPO, oldFilesetPO));
178178
}
179179
} catch (RuntimeException re) {
180-
ExceptionUtils.checkSQLConstraintException(
180+
ExceptionUtils.checkSQLException(
181181
re, Entity.EntityType.FILESET, newEntity.nameIdentifier().toString());
182182
throw re;
183183
}

core/src/main/java/com/datastrato/gravitino/storage/relational/service/MetalakeMetaService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public void insertMetalake(BaseMetalake baseMetalake, boolean overwrite) {
9090
}
9191
});
9292
} catch (RuntimeException re) {
93-
ExceptionUtils.checkSQLConstraintException(
93+
ExceptionUtils.checkSQLException(
9494
re, Entity.EntityType.METALAKE, baseMetalake.nameIdentifier().toString());
9595
throw re;
9696
}
@@ -125,7 +125,7 @@ public <E extends Entity & HasIdentifier> BaseMetalake updateMetalake(
125125
MetalakeMetaMapper.class,
126126
mapper -> mapper.updateMetalakeMeta(newMetalakePO, oldMetalakePO));
127127
} catch (RuntimeException re) {
128-
ExceptionUtils.checkSQLConstraintException(
128+
ExceptionUtils.checkSQLException(
129129
re, Entity.EntityType.METALAKE, newMetalakeEntity.nameIdentifier().toString());
130130
throw re;
131131
}

core/src/main/java/com/datastrato/gravitino/storage/relational/service/SchemaMetaService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public void insertSchema(SchemaEntity schemaEntity, boolean overwrite) {
109109
}
110110
});
111111
} catch (RuntimeException re) {
112-
ExceptionUtils.checkSQLConstraintException(
112+
ExceptionUtils.checkSQLException(
113113
re, Entity.EntityType.SCHEMA, schemaEntity.nameIdentifier().toString());
114114
throw re;
115115
}
@@ -142,7 +142,7 @@ public <E extends Entity & HasIdentifier> SchemaEntity updateSchema(
142142
mapper.updateSchemaMeta(
143143
POConverters.updateSchemaPOWithVersion(oldSchemaPO, newEntity), oldSchemaPO));
144144
} catch (RuntimeException re) {
145-
ExceptionUtils.checkSQLConstraintException(
145+
ExceptionUtils.checkSQLException(
146146
re, Entity.EntityType.SCHEMA, newEntity.nameIdentifier().toString());
147147
throw re;
148148
}

core/src/main/java/com/datastrato/gravitino/storage/relational/service/TableMetaService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public void insertTable(TableEntity tableEntity, boolean overwrite) {
102102
}
103103
});
104104
} catch (RuntimeException re) {
105-
ExceptionUtils.checkSQLConstraintException(
105+
ExceptionUtils.checkSQLException(
106106
re, Entity.EntityType.TABLE, tableEntity.nameIdentifier().toString());
107107
throw re;
108108
}
@@ -135,7 +135,7 @@ public <E extends Entity & HasIdentifier> TableEntity updateTable(
135135
mapper.updateTableMeta(
136136
POConverters.updateTablePOWithVersion(oldTablePO, newEntity), oldTablePO));
137137
} catch (RuntimeException re) {
138-
ExceptionUtils.checkSQLConstraintException(
138+
ExceptionUtils.checkSQLException(
139139
re, Entity.EntityType.TABLE, newEntity.nameIdentifier().toString());
140140
throw re;
141141
}

core/src/main/java/com/datastrato/gravitino/storage/relational/service/TopicMetaService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public void insertTopic(TopicEntity topicEntity, boolean overwrite) {
5353
});
5454
// TODO: insert topic dataLayout version after supporting it
5555
} catch (RuntimeException re) {
56-
ExceptionUtils.checkSQLConstraintException(
56+
ExceptionUtils.checkSQLException(
5757
re, Entity.EntityType.TOPIC, topicEntity.nameIdentifier().toString());
5858
throw re;
5959
}
@@ -97,7 +97,7 @@ public <E extends Entity & HasIdentifier> TopicEntity updateTopic(
9797
mapper.updateTopicMeta(
9898
POConverters.updateTopicPOWithVersion(oldTopicPO, newEntity), oldTopicPO));
9999
} catch (RuntimeException re) {
100-
ExceptionUtils.checkSQLConstraintException(
100+
ExceptionUtils.checkSQLException(
101101
re, Entity.EntityType.TOPIC, newEntity.nameIdentifier().toString());
102102
throw re;
103103
}

core/src/main/java/com/datastrato/gravitino/storage/relational/utils/ExceptionUtils.java

+6-10
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,17 @@
55
package com.datastrato.gravitino.storage.relational.utils;
66

77
import com.datastrato.gravitino.Entity;
8-
import com.datastrato.gravitino.exceptions.AlreadyExistsException;
9-
import java.sql.SQLIntegrityConstraintViolationException;
8+
import com.datastrato.gravitino.storage.relational.converters.SQLExceptionConverterFactory;
9+
import java.sql.SQLException;
1010

1111
public class ExceptionUtils {
1212
private ExceptionUtils() {}
1313

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

0 commit comments

Comments
 (0)