Skip to content

Commit 749677c

Browse files
Support for MySQL storage (#5411)
* Adding Mysql support - initial commit * Fix for NPE when state.handle is null * Bump maven version in mvn-wrapper.properties to 3.9.8 * Fixing code related to MySQL support after IT failures * spotless:apply * adding 101 upgrade * Fixing `groups` keyword escape by backquotes * applying spotless fixes
1 parent bf7acea commit 749677c

File tree

15 files changed

+520
-63
lines changed

15 files changed

+520
-63
lines changed

.mvn/wrapper/maven-wrapper.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip
1+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip
22
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

app/pom.xml

+9
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@
151151
<groupId>io.quarkus</groupId>
152152
<artifactId>quarkus-jdbc-postgresql</artifactId>
153153
</dependency>
154+
<dependency>
155+
<groupId>io.quarkus</groupId>
156+
<artifactId>quarkus-jdbc-mysql</artifactId>
157+
</dependency>
154158
<dependency>
155159
<groupId>io.quarkus</groupId>
156160
<artifactId>quarkus-jdbc-h2</artifactId>
@@ -310,6 +314,11 @@
310314
<artifactId>mssqlserver</artifactId>
311315
<scope>test</scope>
312316
</dependency>
317+
<dependency>
318+
<groupId>org.testcontainers</groupId>
319+
<artifactId>mysql</artifactId>
320+
<scope>test</scope>
321+
</dependency>
313322
<dependency>
314323
<groupId>org.awaitility</groupId>
315324
<artifactId>awaitility</artifactId>

app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java

+24-51
Original file line numberDiff line numberDiff line change
@@ -943,13 +943,8 @@ public ArtifactSearchResultsDto searchArtifacts(Set<SearchFilter> filters, Order
943943
return handles.withHandleNoException(handle -> {
944944
List<SqlStatementVariableBinder> binders = new LinkedList<>();
945945

946-
StringBuilder selectTemplate = new StringBuilder();
947946
StringBuilder where = new StringBuilder();
948947
StringBuilder orderByQuery = new StringBuilder();
949-
StringBuilder limitOffset = new StringBuilder();
950-
951-
// Formulate the SELECT clause for the artifacts query
952-
selectTemplate.append("SELECT {{selectColumns}} FROM artifacts a ");
953948

954949
// Formulate the WHERE clause for both queries
955950
String op;
@@ -1081,19 +1076,13 @@ public ArtifactSearchResultsDto searchArtifacts(Set<SearchFilter> filters, Order
10811076
}
10821077
orderByQuery.append(" ").append(orderDirection.name());
10831078

1084-
// Add limit and offset to artifact query
1085-
if ("mssql".equals(sqlStatements.dbType())) {
1086-
limitOffset.append(" OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");
1087-
} else {
1088-
limitOffset.append(" LIMIT ? OFFSET ?");
1089-
}
1090-
10911079
// Query for the artifacts
1092-
String artifactsQuerySql = new StringBuilder(selectTemplate).append(where).append(orderByQuery)
1093-
.append(limitOffset).toString().replace("{{selectColumns}}", "a.*");
1080+
String artifactsQuerySql = sqlStatements.selectTableTemplate("a.*", "artifacts", "a",
1081+
where.toString(), orderByQuery.toString());
10941082
Query artifactsQuery = handle.createQuery(artifactsQuerySql);
1095-
String countQuerySql = new StringBuilder(selectTemplate).append(where).toString()
1096-
.replace("{{selectColumns}}", "count(a.artifactId)");
1083+
1084+
String countQuerySql = sqlStatements.selectCountTableTemplate("a.artifactId", "artifacts", "a",
1085+
where.toString());
10971086
Query countQuery = handle.createQuery(countQuerySql);
10981087

10991088
// Bind all query parameters
@@ -2803,13 +2792,8 @@ public GroupSearchResultsDto searchGroups(Set<SearchFilter> filters, OrderBy ord
28032792
List<SqlStatementVariableBinder> binders = new LinkedList<>();
28042793
String op;
28052794

2806-
StringBuilder selectTemplate = new StringBuilder();
28072795
StringBuilder where = new StringBuilder();
28082796
StringBuilder orderByQuery = new StringBuilder();
2809-
StringBuilder limitOffset = new StringBuilder();
2810-
2811-
// Formulate the SELECT clause for the artifacts query
2812-
selectTemplate.append("SELECT {{selectColumns}} FROM groups g ");
28132797

28142798
// Formulate the WHERE clause for both queries
28152799
where.append(" WHERE (1 = 1)");
@@ -2872,20 +2856,13 @@ public GroupSearchResultsDto searchGroups(Set<SearchFilter> filters, OrderBy ord
28722856
}
28732857
orderByQuery.append(" ").append(orderDirection.name());
28742858

2875-
// Add limit and offset to query
2876-
if ("mssql".equals(sqlStatements.dbType())) {
2877-
limitOffset.append(" OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");
2878-
} else {
2879-
limitOffset.append(" LIMIT ? OFFSET ?");
2880-
}
2881-
28822859
// Query for the group
2883-
String groupsQuerySql = new StringBuilder(selectTemplate).append(where).append(orderByQuery)
2884-
.append(limitOffset).toString().replace("{{selectColumns}}", "*");
2860+
String groupsQuerySql = sqlStatements.selectTableTemplate("*", "groups", "g", where.toString(),
2861+
orderByQuery.toString());
28852862
Query groupsQuery = handle.createQuery(groupsQuerySql);
28862863
// Query for the total row count
2887-
String countQuerySql = new StringBuilder(selectTemplate).append(where).toString()
2888-
.replace("{{selectColumns}}", "count(g.groupId)");
2864+
String countQuerySql = sqlStatements.selectCountTableTemplate("g.groupId", "groups", "g",
2865+
where.toString());
28892866
Query countQuery = handle.createQuery(countQuerySql);
28902867

28912868
// Bind all query parameters
@@ -3254,6 +3231,10 @@ private long nextCommentIdRaw(Handle handle) {
32543231
private long nextSequenceValueRaw(Handle handle, String sequenceName) {
32553232
if (isH2()) {
32563233
return sequenceCounters.get(sequenceName).incrementAndGet();
3234+
} else if (isMysql()) {
3235+
handle.createUpdate(sqlStatements.getNextSequenceValue()).bind(0, sequenceName).execute();
3236+
return handle.createQuery(sqlStatements.selectCurrentSequenceValue()).bind(0, sequenceName)
3237+
.mapTo(Long.class).one();
32573238
} else {
32583239
return handle.createQuery(sqlStatements.getNextSequenceValue()).bind(0, sequenceName)
32593240
.mapTo(Long.class).one(); // TODO Handle non-existing sequence (see resetSequence)
@@ -3389,13 +3370,8 @@ public BranchSearchResultsDto getBranches(GA ga, int offset, int limit) {
33893370
return handles.withHandleNoException(handle -> {
33903371
List<SqlStatementVariableBinder> binders = new LinkedList<>();
33913372

3392-
StringBuilder selectTemplate = new StringBuilder();
33933373
StringBuilder where = new StringBuilder();
33943374
StringBuilder orderByQuery = new StringBuilder();
3395-
StringBuilder limitOffset = new StringBuilder();
3396-
3397-
// Formulate the SELECT clause for the artifacts query
3398-
selectTemplate.append("SELECT {{selectColumns}} FROM branches b ");
33993375

34003376
// Formulate the WHERE clause for both queries
34013377
where.append(" WHERE b.groupId = ? AND b.artifactId = ?");
@@ -3409,20 +3385,13 @@ public BranchSearchResultsDto getBranches(GA ga, int offset, int limit) {
34093385
// Add order by to artifact query
34103386
orderByQuery.append(" ORDER BY b.branchId ASC");
34113387

3412-
// Add limit and offset to query
3413-
if ("mssql".equals(sqlStatements.dbType())) {
3414-
limitOffset.append(" OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");
3415-
} else {
3416-
limitOffset.append(" LIMIT ? OFFSET ?");
3417-
}
3418-
3419-
// Query for the branc
3420-
String branchesQuerySql = new StringBuilder(selectTemplate).append(where).append(orderByQuery)
3421-
.append(limitOffset).toString().replace("{{selectColumns}}", "*");
3388+
// Query for the artifacts
3389+
String branchesQuerySql = sqlStatements.selectTableTemplate("*", "branches", "b",
3390+
where.toString(), orderByQuery.toString());
34223391
Query branchesQuery = handle.createQuery(branchesQuerySql);
3423-
// Query for the total row count
3424-
String countQuerySql = new StringBuilder(selectTemplate).append(where).toString()
3425-
.replace("{{selectColumns}}", "count(b.branchId)");
3392+
3393+
String countQuerySql = sqlStatements.selectCountTableTemplate("b.branchId", "branches", "b",
3394+
where.toString());
34263395
Query countQuery = handle.createQuery(countQuerySql);
34273396

34283397
// Bind all query parameters
@@ -3646,7 +3615,7 @@ private void createOrUpdateBranchRaw(Handle handle, GAV gav, BranchId branchId,
36463615

36473616
/**
36483617
* Removes a version from the given branch.
3649-
*
3618+
*
36503619
* @param handle
36513620
* @param gav
36523621
* @param branchId
@@ -3791,6 +3760,10 @@ private boolean isH2() {
37913760
return sqlStatements.dbType().equals("h2");
37923761
}
37933762

3763+
private boolean isMysql() {
3764+
return sqlStatements.dbType().equals("mysql");
3765+
}
3766+
37943767
/*
37953768
* Ensures that only a reasonable number/size of labels for each item in the list are returned. This is to
37963769
* guard against an unexpectedly enormous response size to a REST API search operation.

app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java

+30-10
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public String insertVersion(boolean firstVersion) {
138138
} else {
139139
// NOTE: Duplicated value of versionOrder is prevented by UQ_versions_2 constraint.
140140
query = "INSERT INTO versions (globalId, groupId, artifactId, version, versionOrder, state, name, description, owner, createdOn, modifiedBy, modifiedOn, labels, contentId)"
141-
+ " VALUES (?, ?, ?, ?, (SELECT MAX(versionOrder) + 1 FROM versions WHERE groupId = ? AND artifactId = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?)";
141+
+ " VALUES (?, ?, ?, ?, (SELECT maxVer FROM (SELECT MAX(versionOrder) + 1 AS maxVer FROM versions WHERE groupId = ? AND artifactId = ?) temp), ?, ?, ?, ?, ?, ?, ?, ?, ?)";
142142
}
143143
return query;
144144
}
@@ -581,7 +581,7 @@ public String selectArtifactCountById() {
581581
*/
582582
@Override
583583
public String selectGroupCountById() {
584-
return "SELECT COUNT(g.groupId) FROM groups g WHERE g.groupId = ?";
584+
return "SELECT COUNT(g.groupId) FROM " + groupsTable() + " g WHERE g.groupId = ?";
585585
}
586586

587587
/**
@@ -669,47 +669,49 @@ public String insertContentReference() {
669669
*/
670670
@Override
671671
public String insertGroup() {
672-
return "INSERT INTO groups (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
672+
return "INSERT INTO " + groupsTable()
673+
+ " (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
673674
}
674675

675676
/**
676677
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#updateGroup()
677678
*/
678679
@Override
679680
public String updateGroup() {
680-
return "UPDATE groups SET description = ? , modifiedBy = ? , modifiedOn = ? , labels = ? WHERE groupId = ?";
681+
return "UPDATE " + groupsTable()
682+
+ " SET description = ? , modifiedBy = ? , modifiedOn = ? , labels = ? WHERE groupId = ?";
681683
}
682684

683685
/**
684686
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#deleteGroup()
685687
*/
686688
@Override
687689
public String deleteGroup() {
688-
return "DELETE FROM groups WHERE groupId = ?";
690+
return "DELETE FROM " + groupsTable() + " WHERE groupId = ?";
689691
}
690692

691693
/**
692694
* @see SqlStatements#deleteAllGroups()
693695
*/
694696
@Override
695697
public String deleteAllGroups() {
696-
return "DELETE FROM groups ";
698+
return "DELETE FROM " + groupsTable() + " ";
697699
}
698700

699701
/**
700702
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#selectGroups()
701703
*/
702704
@Override
703705
public String selectGroups() {
704-
return "SELECT g.* FROM groups g ORDER BY g.groupId ASC LIMIT ?";
706+
return "SELECT g.* FROM " + groupsTable() + " g ORDER BY g.groupId ASC LIMIT ?";
705707
}
706708

707709
/**
708710
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#selectGroupByGroupId()
709711
*/
710712
@Override
711713
public String selectGroupByGroupId() {
712-
return "SELECT g.* FROM groups g WHERE g.groupId = ?";
714+
return "SELECT g.* FROM " + groupsTable() + " g WHERE g.groupId = ?";
713715
}
714716

715717
@Override
@@ -767,7 +769,7 @@ public String exportGlobalRules() {
767769
*/
768770
@Override
769771
public String exportGroups() {
770-
return "SELECT * FROM groups g ";
772+
return "SELECT * FROM " + groupsTable() + " g ";
771773
}
772774

773775
@Override
@@ -818,7 +820,8 @@ public String importGlobalRule() {
818820
*/
819821
@Override
820822
public String importGroup() {
821-
return "INSERT INTO groups (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) "
823+
return "INSERT INTO " + groupsTable()
824+
+ " (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) "
822825
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
823826
}
824827

@@ -1210,4 +1213,21 @@ public String createOutboxEvent() {
12101213
public String deleteOutboxEvent() {
12111214
return "DELETE FROM outbox WHERE id = ?";
12121215
}
1216+
1217+
@Override
1218+
public String selectCountTableTemplate(String countBy, String tableName, String alias,
1219+
String whereClause) {
1220+
return "SELECT COUNT(%s) FROM %s %s %s".formatted(countBy, tableName, alias, whereClause);
1221+
}
1222+
1223+
@Override
1224+
public String selectTableTemplate(String columns, String tableName, String alias, String whereClause,
1225+
String orderBy) {
1226+
return "SELECT %s FROM %s %s %s %s LIMIT ? OFFSET ?".formatted(columns, tableName, alias, whereClause,
1227+
orderBy);
1228+
}
1229+
1230+
protected String groupsTable() {
1231+
return "groups";
1232+
}
12131233
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package io.apicurio.registry.storage.impl.sql;
2+
3+
/**
4+
* MySQL implementation of the sql statements interface. Provides sql statements that are specific to MySQL,
5+
* where applicable.
6+
*/
7+
public class MySQLSqlStatements extends CommonSqlStatements {
8+
9+
/**
10+
* Constructor.
11+
*/
12+
public MySQLSqlStatements() {
13+
}
14+
15+
/**
16+
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#dbType()
17+
*/
18+
@Override
19+
public String dbType() {
20+
return "mysql";
21+
}
22+
23+
/**
24+
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#isPrimaryKeyViolation(java.lang.Exception)
25+
*/
26+
@Override
27+
public boolean isPrimaryKeyViolation(Exception error) {
28+
return error.getMessage().contains("Duplicate entry");
29+
}
30+
31+
/**
32+
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#isForeignKeyViolation(java.lang.Exception)
33+
*/
34+
@Override
35+
public boolean isForeignKeyViolation(Exception error) {
36+
return error.getMessage().contains("foreign key constraint fails");
37+
}
38+
39+
/**
40+
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#isDatabaseInitialized()
41+
*/
42+
@Override
43+
public String isDatabaseInitialized() {
44+
return "SELECT count(*) AS count FROM information_schema.tables WHERE table_name = 'artifacts'";
45+
}
46+
47+
/**
48+
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#getNextSequenceValue()
49+
*/
50+
@Override
51+
public String getNextSequenceValue() {
52+
return "INSERT INTO sequences (seqName, seqValue) VALUES (?, 1) ON DUPLICATE KEY UPDATE seqValue = seqValue + 1";
53+
}
54+
55+
/**
56+
* @see io.apicurio.registry.storage.impl.sql.SqlStatements#resetSequenceValue()
57+
*/
58+
@Override
59+
public String resetSequenceValue() {
60+
return "INSERT INTO sequences (seqName, seqValue) VALUES (?, ?) ON DUPLICATE KEY UPDATE seqValue = ?";
61+
}
62+
63+
@Override
64+
public String upsertBranch() {
65+
return """
66+
INSERT INTO branches (groupId, artifactId, branchId, description, systemDefined, owner, createdOn, modifiedBy, modifiedOn)
67+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
68+
ON DUPLICATE KEY UPDATE groupId=VALUES(groupId)
69+
""";
70+
}
71+
72+
@Override
73+
public String selectCountTableTemplate(String countBy, String tableName, String alias,
74+
String whereClause) {
75+
return super.selectCountTableTemplate(countBy, "`" + tableName + "`", alias, whereClause);
76+
}
77+
78+
@Override
79+
public String selectTableTemplate(String columns, String tableName, String alias, String whereClause,
80+
String orderBy) {
81+
return super.selectTableTemplate(columns, "`" + tableName + "`", alias, whereClause, orderBy);
82+
}
83+
84+
@Override
85+
public String groupsTable() {
86+
return "`groups`";
87+
}
88+
89+
@Override
90+
public String createDataSnapshot() {
91+
throw new IllegalStateException("Snapshot creation is not supported for MySQL storage");
92+
}
93+
94+
@Override
95+
public String restoreFromSnapshot() {
96+
throw new IllegalStateException("Restoring from snapshot is not supported for MySQL storage");
97+
}
98+
}

0 commit comments

Comments
 (0)