From 4ee46eacaa62a73c62f9b83a816d784700dc5424 Mon Sep 17 00:00:00 2001 From: yoni Date: Wed, 9 Oct 2024 15:20:08 +0300 Subject: [PATCH 01/12] Adding Mysql support - initial commit --- app/pom.xml | 9 + .../storage/impl/sql/MySQLSqlStatements.java | 81 +++++++ .../impl/sql/RegistryDatabaseKind.java | 2 +- .../impl/sql/SqlStatementsProducer.java | 3 + .../registry/storage/impl/sql/mysql.ddl | 211 ++++++++++++++++++ .../storage/impl/sql/MysqlStorageTest.java | 15 ++ .../storage/util/MysqlTestProfile.java | 25 +++ utils/tests/pom.xml | 5 + .../tests/MySqlEmbeddedTestResource.java | 78 +++++++ 9 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java create mode 100644 app/src/main/resources/io/apicurio/registry/storage/impl/sql/mysql.ddl create mode 100644 app/src/test/java/io/apicurio/registry/storage/impl/sql/MysqlStorageTest.java create mode 100644 app/src/test/java/io/apicurio/registry/storage/util/MysqlTestProfile.java create mode 100644 utils/tests/src/main/java/io/apicurio/registry/utils/tests/MySqlEmbeddedTestResource.java diff --git a/app/pom.xml b/app/pom.xml index 7605313160..85d75f40e2 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -151,6 +151,10 @@ io.quarkus quarkus-jdbc-postgresql + + org.mariadb.jdbc + mariadb-java-client + io.quarkus quarkus-jdbc-h2 @@ -310,6 +314,11 @@ mssqlserver test + + org.testcontainers + mysql + test + org.awaitility awaitility diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java new file mode 100644 index 0000000000..4dc659cc9c --- /dev/null +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java @@ -0,0 +1,81 @@ +package io.apicurio.registry.storage.impl.sql; + +/** + * MySQL implementation of the sql statements interface. Provides sql statements that are specific to MySQL, + * where applicable. + */ +public class MySQLSqlStatements extends CommonSqlStatements { + + /** + * Constructor. + */ + public MySQLSqlStatements() { + } + + /** + * @see io.apicurio.registry.storage.impl.sql.SqlStatements#dbType() + */ + @Override + public String dbType() { + return "mysql"; + } + + /** + * @see io.apicurio.registry.storage.impl.sql.SqlStatements#isPrimaryKeyViolation(java.lang.Exception) + */ + @Override + public boolean isPrimaryKeyViolation(Exception error) { + return error.getMessage().contains("Duplicate entry"); + } + + /** + * @see io.apicurio.registry.storage.impl.sql.SqlStatements#isForeignKeyViolation(java.lang.Exception) + */ + @Override + public boolean isForeignKeyViolation(Exception error) { + return error.getMessage().contains("foreign key constraint fails"); + } + + /** + * @see io.apicurio.registry.storage.impl.sql.SqlStatements#isDatabaseInitialized() + */ + @Override + public String isDatabaseInitialized() { + return "SELECT count(*) AS count FROM information_schema.tables WHERE table_name = 'artifacts'"; + } + + /** + * @see io.apicurio.registry.storage.impl.sql.SqlStatements#getNextSequenceValue() + */ + @Override + public String getNextSequenceValue() { + return "INSERT INTO sequences (seqName, seqValue) VALUES (?, 1) ON DUPLICATE KEY UPDATE seqValue = seqValue + 1"; + } + + /** + * @see io.apicurio.registry.storage.impl.sql.SqlStatements#resetSequenceValue() + */ + @Override + public String resetSequenceValue() { + return "INSERT INTO sequences (seqName, seqValue) VALUES (?, ?) ON DUPLICATE KEY UPDATE seqValue = ?"; + } + + @Override + public String upsertBranch() { + return """ + INSERT INTO branches (groupId, artifactId, branchId, description, systemDefined, owner, createdOn, modifiedBy, modifiedOn) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE groupId=VALUES(groupId) + """; + } + + @Override + public String createDataSnapshot() { + throw new IllegalStateException("Snapshot creation is not supported for MySQL storage"); + } + + @Override + public String restoreFromSnapshot() { + throw new IllegalStateException("Restoring from snapshot is not supported for MySQL storage"); + } +} \ No newline at end of file diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatabaseKind.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatabaseKind.java index c01620ec0d..eadcc234bf 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatabaseKind.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatabaseKind.java @@ -3,7 +3,7 @@ public enum RegistryDatabaseKind { postgresql("org.postgresql.Driver"), h2("org.h2.Driver"), mssql( - "com.microsoft.sqlserver.jdbc.SQLServerDriver"); + "com.microsoft.sqlserver.jdbc.SQLServerDriver"), mysql("org.mariadb.jdbc.Driver"); final String driverClassName; diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatementsProducer.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatementsProducer.java index f617990cc5..cdbe66e03a 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatementsProducer.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatementsProducer.java @@ -33,6 +33,9 @@ public SqlStatements createSqlStatements() { if ("postgresql".equals(databaseType)) { return new PostgreSQLSqlStatements(); } + if ("mysql".equals(databaseType)) { + return new MySQLSqlStatements(); + } throw new RuntimeException("Unsupported database type: " + databaseType); } diff --git a/app/src/main/resources/io/apicurio/registry/storage/impl/sql/mysql.ddl b/app/src/main/resources/io/apicurio/registry/storage/impl/sql/mysql.ddl new file mode 100644 index 0000000000..1da0f37f6e --- /dev/null +++ b/app/src/main/resources/io/apicurio/registry/storage/impl/sql/mysql.ddl @@ -0,0 +1,211 @@ +-- ********************************************************************* +-- DDL for the Apicurio Registry - Database: MySQL 5.7+ +-- ********************************************************************* + +CREATE TABLE apicurio ( + propName VARCHAR(255) NOT NULL, + propValue VARCHAR(255) +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE apicurio ADD PRIMARY KEY (propName); +INSERT INTO apicurio (propName, propValue) VALUES ('db_version', 100); + +CREATE TABLE sequences ( + seqName VARCHAR(32) NOT NULL, + seqValue BIGINT NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE sequences ADD PRIMARY KEY (seqName); + +CREATE TABLE config ( + propName VARCHAR(255) NOT NULL, + propValue VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + modifiedOn BIGINT NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE config ADD PRIMARY KEY (propName); +CREATE INDEX IDX_config_1 ON config (modifiedOn); + +CREATE TABLE acls ( + principalId VARCHAR(256) NOT NULL, + role VARCHAR(32) NOT NULL, + principalName VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE acls ADD PRIMARY KEY (principalId); + +CREATE TABLE downloads ( + downloadId VARCHAR(128) NOT NULL, + expires BIGINT NOT NULL, + context VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE downloads ADD PRIMARY KEY (downloadId); +CREATE INDEX IDX_down_1 ON downloads (expires); + +CREATE TABLE global_rules ( + type VARCHAR(32) NOT NULL, + configuration TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE global_rules ADD PRIMARY KEY (type); + +CREATE TABLE content ( + contentId BIGINT NOT NULL, + canonicalHash VARCHAR(64) NOT NULL, + contentHash VARCHAR(64) NOT NULL, + contentType VARCHAR(64) NOT NULL, + content BLOB NOT NULL, + refs TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE content ADD PRIMARY KEY (contentId); +ALTER TABLE content ADD CONSTRAINT UQ_content_1 UNIQUE (contentHash); +CREATE INDEX IDX_content_1 ON content (canonicalHash); +CREATE INDEX IDX_content_2 ON content (contentHash); + +CREATE TABLE content_references ( + contentId BIGINT NOT NULL, + groupId VARCHAR(512), + artifactId VARCHAR(512) NOT NULL, + version VARCHAR(256), + name VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE content_references ADD PRIMARY KEY (contentId, name); +ALTER TABLE content_references ADD CONSTRAINT FK_content_references_1 FOREIGN KEY (contentId) REFERENCES content (contentId) ON DELETE CASCADE; + +CREATE TABLE `groups` ( + groupId VARCHAR(512) NOT NULL, + description VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + artifactsType VARCHAR(32), + owner VARCHAR(256), + createdOn TIMESTAMP NOT NULL, + modifiedBy VARCHAR(256), + modifiedOn TIMESTAMP, + labels TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE `groups` ADD PRIMARY KEY (groupId); + +CREATE TABLE group_labels ( + groupId VARCHAR(512) NOT NULL, + labelKey VARCHAR(256) NOT NULL, + labelValue VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE group_labels ADD CONSTRAINT FK_glabels_1 FOREIGN KEY (groupId) REFERENCES `groups` (groupId) ON DELETE CASCADE; +CREATE INDEX IDX_glabels_1 ON group_labels (labelKey); +CREATE INDEX IDX_glabels_2 ON group_labels (labelValue); + +CREATE TABLE group_rules ( + groupId VARCHAR(512) NOT NULL, + type VARCHAR(32) NOT NULL, + configuration VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE group_rules ADD PRIMARY KEY (groupId, type); +ALTER TABLE group_rules ADD CONSTRAINT FK_grules_1 FOREIGN KEY (groupId) REFERENCES `groups` (groupId) ON DELETE CASCADE; + +CREATE TABLE artifacts ( + groupId VARCHAR(512) NOT NULL, + artifactId VARCHAR(512) NOT NULL, + type VARCHAR(32) NOT NULL, + owner VARCHAR(256), + createdOn TIMESTAMP NOT NULL, + modifiedBy VARCHAR(256), + modifiedOn TIMESTAMP, + name VARCHAR(512), + description VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + labels TEXT +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE artifacts ADD PRIMARY KEY (groupId, artifactId); +CREATE INDEX IDX_artifacts_0 ON artifacts (type); +CREATE INDEX IDX_artifacts_1 ON artifacts (owner); +CREATE INDEX IDX_artifacts_2 ON artifacts (createdOn); +CREATE INDEX IDX_artifacts_3 ON artifacts (name); +-- CREATE INDEX IDX_artifacts_4 ON artifacts(description); + +CREATE TABLE artifact_labels ( + groupId VARCHAR(512) NOT NULL, + artifactId VARCHAR(512) NOT NULL, + labelKey VARCHAR(256) NOT NULL, + labelValue VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE artifact_labels ADD CONSTRAINT FK_alabels_1 FOREIGN KEY (groupId, artifactId) REFERENCES artifacts (groupId, artifactId) ON DELETE CASCADE; +CREATE INDEX IDX_alabels_1 ON artifact_labels (labelKey); +CREATE INDEX IDX_alabels_2 ON artifact_labels (labelValue); + +CREATE TABLE artifact_rules ( + groupId VARCHAR(512) NOT NULL, + artifactId VARCHAR(512) NOT NULL, + type VARCHAR(32) NOT NULL, + configuration VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE artifact_rules ADD PRIMARY KEY (groupId, artifactId, type); + +CREATE TABLE versions ( + globalId BIGINT NOT NULL, + groupId VARCHAR(512) NOT NULL, + artifactId VARCHAR(512) NOT NULL, + version VARCHAR(256), + versionOrder INT NOT NULL, + state VARCHAR(64) NOT NULL, + name VARCHAR(512), + description VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + owner VARCHAR(256), + createdOn TIMESTAMP NOT NULL, + modifiedBy VARCHAR(256), + modifiedOn TIMESTAMP, + labels TEXT, + contentId BIGINT NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE versions ADD PRIMARY KEY (globalId); +ALTER TABLE versions ADD CONSTRAINT UQ_versions_1 UNIQUE (groupId, artifactId, version); +ALTER TABLE versions ADD CONSTRAINT UQ_versions_2 UNIQUE (globalId, versionOrder); +ALTER TABLE versions ADD CONSTRAINT FK_versions_1 FOREIGN KEY (groupId, artifactId) REFERENCES artifacts (groupId, artifactId) ON DELETE CASCADE; +ALTER TABLE versions ADD CONSTRAINT FK_versions_2 FOREIGN KEY (contentId) REFERENCES content (contentId); +CREATE INDEX IDX_versions_1 ON versions (version); +CREATE INDEX IDX_versions_2 ON versions (state); +CREATE INDEX IDX_versions_3 ON versions (name); +-- CREATE INDEX IDX_versions_4 ON versions(description); +CREATE INDEX IDX_versions_5 ON versions (owner); +CREATE INDEX IDX_versions_6 ON versions (createdOn); +CREATE INDEX IDX_versions_7 ON versions (contentId); + +CREATE TABLE version_labels ( + globalId BIGINT NOT NULL, + labelKey VARCHAR(256) NOT NULL, + labelValue VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE version_labels ADD CONSTRAINT FK_vlabels_1 FOREIGN KEY (globalId) REFERENCES versions (globalId) ON DELETE CASCADE; +CREATE INDEX IDX_vlabels_1 ON version_labels (labelKey); +CREATE INDEX IDX_vlabels_2 ON version_labels (labelValue); + +CREATE TABLE version_comments ( + commentId VARCHAR(128) NOT NULL, + globalId BIGINT NOT NULL, + owner VARCHAR(256), + createdOn TIMESTAMP NOT NULL, + cvalue VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE version_comments ADD PRIMARY KEY (commentId); +ALTER TABLE version_comments ADD CONSTRAINT FK_version_comments_1 FOREIGN KEY (globalId) REFERENCES versions (globalId) ON DELETE CASCADE; +CREATE INDEX IDX_version_comments_1 ON version_comments (owner); + +CREATE TABLE branches ( + groupId VARCHAR(512) NOT NULL, + artifactId VARCHAR(512) NOT NULL, + branchId VARCHAR(256) NOT NULL, + description VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + systemDefined BOOLEAN NOT NULL, + owner VARCHAR(256), + createdOn TIMESTAMP NOT NULL, + modifiedBy VARCHAR(256), + modifiedOn TIMESTAMP +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE branches ADD PRIMARY KEY (groupId, artifactId, branchId); +ALTER TABLE branches ADD CONSTRAINT FK_branches_1 FOREIGN KEY (groupId, artifactId) REFERENCES artifacts (groupId, artifactId) ON DELETE CASCADE; + +CREATE TABLE branch_versions ( + groupId VARCHAR(512) NOT NULL, + artifactId VARCHAR(512) NOT NULL, + branchId VARCHAR(256) NOT NULL, + branchOrder INT NOT NULL, + version VARCHAR(256) NOT NULL +) DEFAULT CHARACTER SET ascii COLLATE ascii_general_ci; +ALTER TABLE branch_versions ADD PRIMARY KEY (groupId, artifactId, branchId, version); +ALTER TABLE branch_versions ADD CONSTRAINT FK_branch_versions_1 FOREIGN KEY (groupId, artifactId, branchId) REFERENCES branches (groupId, artifactId, branchId) ON DELETE CASCADE; +ALTER TABLE branch_versions ADD CONSTRAINT FK_branch_versions_2 FOREIGN KEY (groupId, artifactId, version) REFERENCES versions (groupId, artifactId, version) ON DELETE CASCADE; +CREATE INDEX IDX_branch_versions_1 ON branch_versions (groupId, artifactId, branchId, branchOrder); +CREATE INDEX IDX_branch_versions_2 ON branch_versions (branchId); +CREATE INDEX IDX_branch_versions_3 ON branch_versions (branchOrder); \ No newline at end of file diff --git a/app/src/test/java/io/apicurio/registry/storage/impl/sql/MysqlStorageTest.java b/app/src/test/java/io/apicurio/registry/storage/impl/sql/MysqlStorageTest.java new file mode 100644 index 0000000000..e9ac03d481 --- /dev/null +++ b/app/src/test/java/io/apicurio/registry/storage/impl/sql/MysqlStorageTest.java @@ -0,0 +1,15 @@ +package io.apicurio.registry.storage.impl.sql; + +import io.apicurio.registry.storage.util.MysqlTestProfile; +import io.apicurio.registry.utils.tests.ApicurioTestTags; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; +import jakarta.enterprise.inject.Typed; +import org.junit.jupiter.api.Tag; + +@QuarkusTest +@Tag(ApicurioTestTags.SLOW) +@TestProfile(MysqlTestProfile.class) +@Typed(MysqlStorageTest.class) +public class MysqlStorageTest extends DefaultRegistryStorageTest { +} diff --git a/app/src/test/java/io/apicurio/registry/storage/util/MysqlTestProfile.java b/app/src/test/java/io/apicurio/registry/storage/util/MysqlTestProfile.java new file mode 100644 index 0000000000..5304d13c3e --- /dev/null +++ b/app/src/test/java/io/apicurio/registry/storage/util/MysqlTestProfile.java @@ -0,0 +1,25 @@ +package io.apicurio.registry.storage.util; + +import io.apicurio.registry.utils.tests.MySqlEmbeddedTestResource; +import io.quarkus.test.junit.QuarkusTestProfile; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class MysqlTestProfile implements QuarkusTestProfile { + + @Override + public Map getConfigOverrides() { + return Collections.singletonMap("apicurio.storage.sql.kind", "mysql"); + } + + @Override + public List testResources() { + if (!Boolean.parseBoolean(System.getProperty("cluster.tests"))) { + return List.of(new TestResourceEntry(MySqlEmbeddedTestResource.class)); + } else { + return Collections.emptyList(); + } + } +} diff --git a/utils/tests/pom.xml b/utils/tests/pom.xml index 750a4d4e92..af268aa4cd 100644 --- a/utils/tests/pom.xml +++ b/utils/tests/pom.xml @@ -91,6 +91,11 @@ mssqlserver + + org.testcontainers + mysql + + io.strimzi strimzi-test-container diff --git a/utils/tests/src/main/java/io/apicurio/registry/utils/tests/MySqlEmbeddedTestResource.java b/utils/tests/src/main/java/io/apicurio/registry/utils/tests/MySqlEmbeddedTestResource.java new file mode 100644 index 0000000000..5536863982 --- /dev/null +++ b/utils/tests/src/main/java/io/apicurio/registry/utils/tests/MySqlEmbeddedTestResource.java @@ -0,0 +1,78 @@ +package io.apicurio.registry.utils.tests; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; +import org.eclipse.microprofile.config.ConfigProvider; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.utility.DockerImageName; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class MySqlEmbeddedTestResource implements QuarkusTestResourceLifecycleManager { + + private static final String DB_PASSWORD = "P4ssw0rd!#"; + + private static final DockerImageName IMAGE = DockerImageName.parse("mysql:latest"); + private MySQLContainer database = new MySQLContainer<>(IMAGE).withPassword(DB_PASSWORD); + + /** + * Constructor. + */ + public MySqlEmbeddedTestResource() { + System.out.println("[MySqlEmbeddedTestResource] MySQL test resource constructed."); + } + + /** + * @see QuarkusTestResourceLifecycleManager#start() + */ + @Override + public Map start() { + if (!Boolean.parseBoolean(System.getProperty("cluster.tests")) && isMySqlStorage()) { + + System.out.println("[MySqlEmbeddedTestResource] MySQL test resource starting."); + + String currentEnv = System.getenv("CURRENT_ENV"); + + if ("mas".equals(currentEnv)) { + Map props = new HashMap<>(); + props.put("apicurio.storage.sql.kind", "mysql"); + props.put("apicurio.datasource.url", "jdbc:mysql://localhost:3306/test"); + props.put("apicurio.datasource.username", "test"); + props.put("apicurio.datasource.password", "test"); + return props; + } else { + return startMySql(); + } + } + return Collections.emptyMap(); + } + + private static boolean isMySqlStorage() { + return ConfigProvider.getConfig().getValue("apicurio.storage.sql.kind", String.class).equals("mysql"); + } + + private Map startMySql() { + database.start(); + + String datasourceUrl = database.getJdbcUrl(); + + Map props = new HashMap<>(); + props.put("apicurio.storage.sql.kind", "mysql"); + props.put("apicurio.datasource.url", datasourceUrl); + props.put("apicurio.datasource.username", "root"); + props.put("apicurio.datasource.password", DB_PASSWORD); + return props; + } + + /** + * @see QuarkusTestResourceLifecycleManager#stop() + */ + @Override + public void stop() { + if (database != null) { + database.close(); + } + System.out.println("[MySqlEmbeddedTestResource] MySQL test resource stopped."); + } +} \ No newline at end of file From 5893829a41419f0e8f11d2dfe15cc98a6714f49b Mon Sep 17 00:00:00 2001 From: yoni Date: Wed, 9 Oct 2024 15:21:18 +0300 Subject: [PATCH 02/12] Fix for NPE when state.handle is null --- .../registry/storage/impl/sql/AbstractHandleFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractHandleFactory.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractHandleFactory.java index fc20d8b35f..d593a5ceec 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractHandleFactory.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractHandleFactory.java @@ -45,7 +45,9 @@ public R withHandle(HandleCallback callback) thro return callback.withHandle(state.handle); } catch (SQLException e) { // If a SQL exception is thrown, set the handle to rollback. - state.handle.setRollback(true); + if (state.handle != null) { + state.handle.setRollback(true); + } // Wrap the SQL exception. throw new RegistryStorageException(e); } catch (Exception e) { From 65245c0fdc871c7a02fb4cb90a7a6ccf454873c0 Mon Sep 17 00:00:00 2001 From: yoni Date: Wed, 9 Oct 2024 15:22:26 +0300 Subject: [PATCH 03/12] Bump maven version in mvn-wrapper.properties to 3.9.8 --- .mvn/wrapper/maven-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index abd303b673..e096624109 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar From 035948108d4940795182408dd053e44b1f139e86 Mon Sep 17 00:00:00 2001 From: yoni Date: Sun, 13 Oct 2024 17:22:44 +0300 Subject: [PATCH 04/12] Fixing code related to MySQL support after IT failures --- app/pom.xml | 4 ++-- .../impl/sql/AbstractSqlRegistryStorage.java | 12 ++++++++++- .../storage/impl/sql/CommonSqlStatements.java | 20 +++++++++---------- .../impl/sql/RegistryDatabaseKind.java | 2 +- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 85d75f40e2..c8d089b2d0 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -152,8 +152,8 @@ quarkus-jdbc-postgresql - org.mariadb.jdbc - mariadb-java-client + io.quarkus + quarkus-jdbc-mysql io.quarkus diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java index 3f077b56c2..5f7d38e997 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java @@ -3044,7 +3044,13 @@ private long nextCommentIdRaw(Handle handle) { private long nextSequenceValueRaw(Handle handle, String sequenceName) { if (isH2()) { return sequenceCounters.get(sequenceName).incrementAndGet(); - } else { + } else if (isMysql()) { + handle.createUpdate(sqlStatements.getNextSequenceValue()).bind(0, sequenceName) + .execute(); + return handle.createQuery(sqlStatements.selectCurrentSequenceValue()) + .bind(0, sequenceName).mapTo(Long.class).one(); + } + else { return handle.createQuery(sqlStatements.getNextSequenceValue()).bind(0, sequenceName) .mapTo(Long.class).one(); // TODO Handle non-existing sequence (see resetSequence) } @@ -3541,4 +3547,8 @@ private boolean isPostgresql() { private boolean isH2() { return sqlStatements.dbType().equals("h2"); } + + private boolean isMysql() { + return sqlStatements.dbType().equals("mysql"); + } } diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java index be8af1630f..6fd9a0b4f6 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java @@ -138,7 +138,7 @@ public String insertVersion(boolean firstVersion) { } else { // NOTE: Duplicated value of versionOrder is prevented by UQ_versions_2 constraint. query = "INSERT INTO versions (globalId, groupId, artifactId, version, versionOrder, state, name, description, owner, createdOn, modifiedBy, modifiedOn, labels, contentId)" - + " VALUES (?, ?, ?, ?, (SELECT MAX(versionOrder) + 1 FROM versions WHERE groupId = ? AND artifactId = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + " VALUES (?, ?, ?, ?, (SELECT maxVer FROM (SELECT MAX(versionOrder) + 1 AS maxVer FROM versions WHERE groupId = ? AND artifactId = ?) temp), ?, ?, ?, ?, ?, ?, ?, ?, ?)"; } return query; } @@ -564,7 +564,7 @@ public String selectArtifactCountById() { */ @Override public String selectGroupCountById() { - return "SELECT COUNT(g.groupId) FROM groups g WHERE g.groupId = ?"; + return "SELECT COUNT(g.groupId) FROM `groups` g WHERE g.groupId = ?"; } /** @@ -650,7 +650,7 @@ public String insertContentReference() { */ @Override public String insertGroup() { - return "INSERT INTO groups (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + return "INSERT INTO `groups` (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; } /** @@ -658,7 +658,7 @@ public String insertGroup() { */ @Override public String updateGroup() { - return "UPDATE groups SET description = ? , modifiedBy = ? , modifiedOn = ? , labels = ? WHERE groupId = ?"; + return "UPDATE `groups` SET description = ? , modifiedBy = ? , modifiedOn = ? , labels = ? WHERE groupId = ?"; } /** @@ -666,7 +666,7 @@ public String updateGroup() { */ @Override public String deleteGroup() { - return "DELETE FROM groups WHERE groupId = ?"; + return "DELETE FROM `groups` WHERE groupId = ?"; } /** @@ -674,7 +674,7 @@ public String deleteGroup() { */ @Override public String deleteAllGroups() { - return "DELETE FROM groups "; + return "DELETE FROM `groups` "; } /** @@ -682,7 +682,7 @@ public String deleteAllGroups() { */ @Override public String selectGroups() { - return "SELECT g.* FROM groups g ORDER BY g.groupId ASC LIMIT ?"; + return "SELECT g.* FROM `groups` g ORDER BY g.groupId ASC LIMIT ?"; } /** @@ -690,7 +690,7 @@ public String selectGroups() { */ @Override public String selectGroupByGroupId() { - return "SELECT g.* FROM groups g WHERE g.groupId = ?"; + return "SELECT g.* FROM `groups` g WHERE g.groupId = ?"; } @Override @@ -748,7 +748,7 @@ public String exportGlobalRules() { */ @Override public String exportGroups() { - return "SELECT * FROM groups g "; + return "SELECT * FROM `groups` g "; } @Override @@ -799,7 +799,7 @@ public String importGlobalRule() { */ @Override public String importGroup() { - return "INSERT INTO groups (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) " + return "INSERT INTO `groups` (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; } diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatabaseKind.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatabaseKind.java index eadcc234bf..fbbadc22a4 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatabaseKind.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatabaseKind.java @@ -3,7 +3,7 @@ public enum RegistryDatabaseKind { postgresql("org.postgresql.Driver"), h2("org.h2.Driver"), mssql( - "com.microsoft.sqlserver.jdbc.SQLServerDriver"), mysql("org.mariadb.jdbc.Driver"); + "com.microsoft.sqlserver.jdbc.SQLServerDriver"), mysql("com.mysql.cj.jdbc.Driver"); final String driverClassName; From 36a30d31081b651749d54526a47c88d2db14c64b Mon Sep 17 00:00:00 2001 From: yoni Date: Mon, 14 Oct 2024 11:53:30 +0300 Subject: [PATCH 05/12] spotless:apply --- .../storage/impl/sql/AbstractSqlRegistryStorage.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java index 5f7d38e997..de7d00e601 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java @@ -3045,12 +3045,10 @@ private long nextSequenceValueRaw(Handle handle, String sequenceName) { if (isH2()) { return sequenceCounters.get(sequenceName).incrementAndGet(); } else if (isMysql()) { - handle.createUpdate(sqlStatements.getNextSequenceValue()).bind(0, sequenceName) - .execute(); - return handle.createQuery(sqlStatements.selectCurrentSequenceValue()) - .bind(0, sequenceName).mapTo(Long.class).one(); - } - else { + handle.createUpdate(sqlStatements.getNextSequenceValue()).bind(0, sequenceName).execute(); + return handle.createQuery(sqlStatements.selectCurrentSequenceValue()).bind(0, sequenceName) + .mapTo(Long.class).one(); + } else { return handle.createQuery(sqlStatements.getNextSequenceValue()).bind(0, sequenceName) .mapTo(Long.class).one(); // TODO Handle non-existing sequence (see resetSequence) } From 204c0e281498707021c80cb08e2c0bde64b0a750 Mon Sep 17 00:00:00 2001 From: yoni Date: Sun, 27 Oct 2024 12:37:02 +0200 Subject: [PATCH 06/12] adding 101 upgrade --- .../storage/impl/sql/AbstractSqlRegistryStorage.java | 2 +- .../storage/impl/sql/upgrades/101/mysql.upgrade.ddl | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 app/src/main/resources/io/apicurio/registry/storage/impl/sql/upgrades/101/mysql.upgrade.ddl diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java index 0329452ec4..ed0aa48a5b 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java @@ -2809,7 +2809,7 @@ public GroupSearchResultsDto searchGroups(Set filters, OrderBy ord StringBuilder limitOffset = new StringBuilder(); // Formulate the SELECT clause for the artifacts query - selectTemplate.append("SELECT {{selectColumns}} FROM groups g "); + selectTemplate.append("SELECT {{selectColumns}} FROM `groups` g "); // Formulate the WHERE clause for both queries where.append(" WHERE (1 = 1)"); diff --git a/app/src/main/resources/io/apicurio/registry/storage/impl/sql/upgrades/101/mysql.upgrade.ddl b/app/src/main/resources/io/apicurio/registry/storage/impl/sql/upgrades/101/mysql.upgrade.ddl new file mode 100644 index 0000000000..4611f150b4 --- /dev/null +++ b/app/src/main/resources/io/apicurio/registry/storage/impl/sql/upgrades/101/mysql.upgrade.ddl @@ -0,0 +1,9 @@ +-- ********************************************************************* +-- DDL for the Apicurio Registry - Database: postgresql +-- Upgrade Script from 100 to 101 +-- ********************************************************************* + +UPDATE apicurio SET propValue = 101 WHERE propName = 'db_version'; + +CREATE TABLE outbox (id VARCHAR(128) NOT NULL, aggregatetype VARCHAR(255) NOT NULL, aggregateid VARCHAR(255) NOT NULL, type VARCHAR(255) NOT NULL, payload JSON NOT NULL); +ALTER TABLE outbox ADD PRIMARY KEY (id); \ No newline at end of file From a0a84739ad499bc174a39279524037efc4fe49be Mon Sep 17 00:00:00 2001 From: yoni Date: Sun, 27 Oct 2024 18:24:32 +0200 Subject: [PATCH 07/12] Fixing `groups` keyword escape by backquotes --- .../impl/sql/AbstractSqlRegistryStorage.java | 63 +++++-------------- .../storage/impl/sql/CommonSqlStatements.java | 33 +++++++--- .../storage/impl/sql/MySQLSqlStatements.java | 15 +++++ .../impl/sql/SQLServerSqlStatements.java | 6 ++ .../storage/impl/sql/SqlStatements.java | 3 + 5 files changed, 64 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java index ed0aa48a5b..1d24657631 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java @@ -943,13 +943,8 @@ public ArtifactSearchResultsDto searchArtifacts(Set filters, Order return handles.withHandleNoException(handle -> { List binders = new LinkedList<>(); - StringBuilder selectTemplate = new StringBuilder(); StringBuilder where = new StringBuilder(); StringBuilder orderByQuery = new StringBuilder(); - StringBuilder limitOffset = new StringBuilder(); - - // Formulate the SELECT clause for the artifacts query - selectTemplate.append("SELECT {{selectColumns}} FROM artifacts a "); // Formulate the WHERE clause for both queries String op; @@ -1081,19 +1076,14 @@ public ArtifactSearchResultsDto searchArtifacts(Set filters, Order } orderByQuery.append(" ").append(orderDirection.name()); - // Add limit and offset to artifact query - if ("mssql".equals(sqlStatements.dbType())) { - limitOffset.append(" OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - } else { - limitOffset.append(" LIMIT ? OFFSET ?"); - } // Query for the artifacts - String artifactsQuerySql = new StringBuilder(selectTemplate).append(where).append(orderByQuery) - .append(limitOffset).toString().replace("{{selectColumns}}", "a.*"); + String artifactsQuerySql = sqlStatements.selectTableTemplate("a.*", "artifacts", "a", + where.toString(), orderByQuery.toString()); Query artifactsQuery = handle.createQuery(artifactsQuerySql); - String countQuerySql = new StringBuilder(selectTemplate).append(where).toString() - .replace("{{selectColumns}}", "count(a.artifactId)"); + + String countQuerySql = sqlStatements.selectCountTableTemplate("a.artifactId", "artifacts", "a", + where.toString()); Query countQuery = handle.createQuery(countQuerySql); // Bind all query parameters @@ -2803,13 +2793,9 @@ public GroupSearchResultsDto searchGroups(Set filters, OrderBy ord List binders = new LinkedList<>(); String op; - StringBuilder selectTemplate = new StringBuilder(); + StringBuilder where = new StringBuilder(); StringBuilder orderByQuery = new StringBuilder(); - StringBuilder limitOffset = new StringBuilder(); - - // Formulate the SELECT clause for the artifacts query - selectTemplate.append("SELECT {{selectColumns}} FROM `groups` g "); // Formulate the WHERE clause for both queries where.append(" WHERE (1 = 1)"); @@ -2872,20 +2858,14 @@ public GroupSearchResultsDto searchGroups(Set filters, OrderBy ord } orderByQuery.append(" ").append(orderDirection.name()); - // Add limit and offset to query - if ("mssql".equals(sqlStatements.dbType())) { - limitOffset.append(" OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - } else { - limitOffset.append(" LIMIT ? OFFSET ?"); - } // Query for the group - String groupsQuerySql = new StringBuilder(selectTemplate).append(where).append(orderByQuery) - .append(limitOffset).toString().replace("{{selectColumns}}", "*"); + String groupsQuerySql = sqlStatements.selectTableTemplate("*", "groups", "g", + where.toString(), orderByQuery.toString()); Query groupsQuery = handle.createQuery(groupsQuerySql); // Query for the total row count - String countQuerySql = new StringBuilder(selectTemplate).append(where).toString() - .replace("{{selectColumns}}", "count(g.groupId)"); + String countQuerySql = sqlStatements.selectCountTableTemplate("g.groupId", "groups", "g", + where.toString()); Query countQuery = handle.createQuery(countQuerySql); // Bind all query parameters @@ -3393,13 +3373,8 @@ public BranchSearchResultsDto getBranches(GA ga, int offset, int limit) { return handles.withHandleNoException(handle -> { List binders = new LinkedList<>(); - StringBuilder selectTemplate = new StringBuilder(); StringBuilder where = new StringBuilder(); StringBuilder orderByQuery = new StringBuilder(); - StringBuilder limitOffset = new StringBuilder(); - - // Formulate the SELECT clause for the artifacts query - selectTemplate.append("SELECT {{selectColumns}} FROM branches b "); // Formulate the WHERE clause for both queries where.append(" WHERE b.groupId = ? AND b.artifactId = ?"); @@ -3413,20 +3388,14 @@ public BranchSearchResultsDto getBranches(GA ga, int offset, int limit) { // Add order by to artifact query orderByQuery.append(" ORDER BY b.branchId ASC"); - // Add limit and offset to query - if ("mssql".equals(sqlStatements.dbType())) { - limitOffset.append(" OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"); - } else { - limitOffset.append(" LIMIT ? OFFSET ?"); - } - // Query for the branc - String branchesQuerySql = new StringBuilder(selectTemplate).append(where).append(orderByQuery) - .append(limitOffset).toString().replace("{{selectColumns}}", "*"); + // Query for the artifacts + String branchesQuerySql = sqlStatements.selectTableTemplate("*", "branches", "b", + where.toString(), orderByQuery.toString()); Query branchesQuery = handle.createQuery(branchesQuerySql); - // Query for the total row count - String countQuerySql = new StringBuilder(selectTemplate).append(where).toString() - .replace("{{selectColumns}}", "count(b.branchId)"); + + String countQuerySql = sqlStatements.selectCountTableTemplate("b.branchId", "branches", "b", + where.toString()); Query countQuery = handle.createQuery(countQuerySql); // Bind all query parameters diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java index ff28484cf5..c03f1289b5 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java @@ -581,7 +581,7 @@ public String selectArtifactCountById() { */ @Override public String selectGroupCountById() { - return "SELECT COUNT(g.groupId) FROM `groups` g WHERE g.groupId = ?"; + return "SELECT COUNT(g.groupId) FROM " + groupsTable() + " g WHERE g.groupId = ?"; } /** @@ -669,7 +669,7 @@ public String insertContentReference() { */ @Override public String insertGroup() { - return "INSERT INTO `groups` (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + return "INSERT INTO " + groupsTable() + " (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; } /** @@ -677,7 +677,7 @@ public String insertGroup() { */ @Override public String updateGroup() { - return "UPDATE `groups` SET description = ? , modifiedBy = ? , modifiedOn = ? , labels = ? WHERE groupId = ?"; + return "UPDATE " + groupsTable() + " SET description = ? , modifiedBy = ? , modifiedOn = ? , labels = ? WHERE groupId = ?"; } /** @@ -685,7 +685,7 @@ public String updateGroup() { */ @Override public String deleteGroup() { - return "DELETE FROM `groups` WHERE groupId = ?"; + return "DELETE FROM " + groupsTable() + " WHERE groupId = ?"; } /** @@ -693,7 +693,7 @@ public String deleteGroup() { */ @Override public String deleteAllGroups() { - return "DELETE FROM `groups` "; + return "DELETE FROM " + groupsTable() + " "; } /** @@ -701,7 +701,7 @@ public String deleteAllGroups() { */ @Override public String selectGroups() { - return "SELECT g.* FROM `groups` g ORDER BY g.groupId ASC LIMIT ?"; + return "SELECT g.* FROM " + groupsTable() + " g ORDER BY g.groupId ASC LIMIT ?"; } /** @@ -709,7 +709,7 @@ public String selectGroups() { */ @Override public String selectGroupByGroupId() { - return "SELECT g.* FROM `groups` g WHERE g.groupId = ?"; + return "SELECT g.* FROM " + groupsTable() + " g WHERE g.groupId = ?"; } @Override @@ -767,7 +767,7 @@ public String exportGlobalRules() { */ @Override public String exportGroups() { - return "SELECT * FROM `groups` g "; + return "SELECT * FROM " + groupsTable() + " g "; } @Override @@ -818,7 +818,7 @@ public String importGlobalRule() { */ @Override public String importGroup() { - return "INSERT INTO `groups` (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) " + return "INSERT INTO " + groupsTable() + " (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; } @@ -1210,4 +1210,19 @@ public String createOutboxEvent() { public String deleteOutboxEvent() { return "DELETE FROM outbox WHERE id = ?"; } + + @Override + public String selectCountTableTemplate(String countBy, String tableName, String alias, String whereClause) { + return "SELECT COUNT(%s) FROM %s %s %s".formatted(countBy, tableName, alias, whereClause); + } + + @Override + public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, String orderBy) { + return "SELECT %s FROM %s %s %s %s LIMIT ? OFFSET ?".formatted(columns, tableName, alias, whereClause, orderBy); + } + + + protected String groupsTable() { + return "groups"; + } } diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java index 4dc659cc9c..87170bbb6c 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java @@ -69,6 +69,21 @@ INSERT INTO branches (groupId, artifactId, branchId, description, systemDefined, """; } + @Override + public String selectCountTableTemplate(String countBy, String tableName, String alias, String whereClause) { + return super.selectCountTableTemplate(countBy, "`" + tableName + "`", alias, whereClause); + } + + @Override + public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, String orderBy) { + return super.selectTableTemplate(columns, "`" + tableName + "`", alias, whereClause, orderBy); + } + + @Override + public String groupsTable() { + return "`groups`"; + } + @Override public String createDataSnapshot() { throw new IllegalStateException("Snapshot creation is not supported for MySQL storage"); diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SQLServerSqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SQLServerSqlStatements.java index d1e39213cb..4985233d67 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SQLServerSqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SQLServerSqlStatements.java @@ -125,6 +125,12 @@ public String selectBranchTipFilteredByState() { + "ORDER BY bv.branchOrder DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"; } + @Override + public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, String orderBy) { + return "SELECT %s FROM %s %s %s %s OFFSET ? ROWS FETCH NEXT ? ROWS ONLY" + .formatted(columns, tableName, alias, whereClause, orderBy); + } + @Override public String deleteAllOrphanedContent() { return "DELETE FROM content WHERE NOT EXISTS (SELECT 1 FROM versions v WHERE v.contentId = contentId )"; diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatements.java index 74ac6c65ed..c8faf84e96 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatements.java @@ -652,4 +652,7 @@ public interface SqlStatements { public String deleteOutboxEvent(); + String selectCountTableTemplate(String countBy, String tableName, String alias, String whereClause); + + String selectTableTemplate(String columns, String tableName, String alias, String whereClause, String orderBy); } From a14343163494d328c4b1648ba2af1be69d6f612e Mon Sep 17 00:00:00 2001 From: Yoni Weisberg Date: Mon, 28 Oct 2024 16:38:19 +0200 Subject: [PATCH 08/12] applying spotless fixes --- .../impl/sql/AbstractSqlRegistryStorage.java | 10 +++------- .../storage/impl/sql/CommonSqlStatements.java | 19 ++++++++++++------- .../storage/impl/sql/MySQLSqlStatements.java | 6 ++++-- .../impl/sql/SQLServerSqlStatements.java | 7 ++++--- .../storage/impl/sql/SqlStatements.java | 3 ++- 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java index 1d24657631..6aa0a57f01 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/AbstractSqlRegistryStorage.java @@ -1076,7 +1076,6 @@ public ArtifactSearchResultsDto searchArtifacts(Set filters, Order } orderByQuery.append(" ").append(orderDirection.name()); - // Query for the artifacts String artifactsQuerySql = sqlStatements.selectTableTemplate("a.*", "artifacts", "a", where.toString(), orderByQuery.toString()); @@ -2793,7 +2792,6 @@ public GroupSearchResultsDto searchGroups(Set filters, OrderBy ord List binders = new LinkedList<>(); String op; - StringBuilder where = new StringBuilder(); StringBuilder orderByQuery = new StringBuilder(); @@ -2858,14 +2856,13 @@ public GroupSearchResultsDto searchGroups(Set filters, OrderBy ord } orderByQuery.append(" ").append(orderDirection.name()); - // Query for the group - String groupsQuerySql = sqlStatements.selectTableTemplate("*", "groups", "g", - where.toString(), orderByQuery.toString()); + String groupsQuerySql = sqlStatements.selectTableTemplate("*", "groups", "g", where.toString(), + orderByQuery.toString()); Query groupsQuery = handle.createQuery(groupsQuerySql); // Query for the total row count String countQuerySql = sqlStatements.selectCountTableTemplate("g.groupId", "groups", "g", - where.toString()); + where.toString()); Query countQuery = handle.createQuery(countQuerySql); // Bind all query parameters @@ -3388,7 +3385,6 @@ public BranchSearchResultsDto getBranches(GA ga, int offset, int limit) { // Add order by to artifact query orderByQuery.append(" ORDER BY b.branchId ASC"); - // Query for the artifacts String branchesQuerySql = sqlStatements.selectTableTemplate("*", "branches", "b", where.toString(), orderByQuery.toString()); diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java index c03f1289b5..fbc791c6ef 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/CommonSqlStatements.java @@ -669,7 +669,8 @@ public String insertContentReference() { */ @Override public String insertGroup() { - return "INSERT INTO " + groupsTable() + " (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + return "INSERT INTO " + groupsTable() + + " (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; } /** @@ -677,7 +678,8 @@ public String insertGroup() { */ @Override public String updateGroup() { - return "UPDATE " + groupsTable() + " SET description = ? , modifiedBy = ? , modifiedOn = ? , labels = ? WHERE groupId = ?"; + return "UPDATE " + groupsTable() + + " SET description = ? , modifiedBy = ? , modifiedOn = ? , labels = ? WHERE groupId = ?"; } /** @@ -818,7 +820,8 @@ public String importGlobalRule() { */ @Override public String importGroup() { - return "INSERT INTO " + groupsTable() + " (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) " + return "INSERT INTO " + groupsTable() + + " (groupId, description, artifactsType, owner, createdOn, modifiedBy, modifiedOn, labels) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; } @@ -1212,16 +1215,18 @@ public String deleteOutboxEvent() { } @Override - public String selectCountTableTemplate(String countBy, String tableName, String alias, String whereClause) { + public String selectCountTableTemplate(String countBy, String tableName, String alias, + String whereClause) { return "SELECT COUNT(%s) FROM %s %s %s".formatted(countBy, tableName, alias, whereClause); } @Override - public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, String orderBy) { - return "SELECT %s FROM %s %s %s %s LIMIT ? OFFSET ?".formatted(columns, tableName, alias, whereClause, orderBy); + public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, + String orderBy) { + return "SELECT %s FROM %s %s %s %s LIMIT ? OFFSET ?".formatted(columns, tableName, alias, whereClause, + orderBy); } - protected String groupsTable() { return "groups"; } diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java index 87170bbb6c..2ce9b9912e 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/MySQLSqlStatements.java @@ -70,12 +70,14 @@ INSERT INTO branches (groupId, artifactId, branchId, description, systemDefined, } @Override - public String selectCountTableTemplate(String countBy, String tableName, String alias, String whereClause) { + public String selectCountTableTemplate(String countBy, String tableName, String alias, + String whereClause) { return super.selectCountTableTemplate(countBy, "`" + tableName + "`", alias, whereClause); } @Override - public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, String orderBy) { + public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, + String orderBy) { return super.selectTableTemplate(columns, "`" + tableName + "`", alias, whereClause, orderBy); } diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SQLServerSqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SQLServerSqlStatements.java index 4985233d67..27c22c0a75 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SQLServerSqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SQLServerSqlStatements.java @@ -126,9 +126,10 @@ public String selectBranchTipFilteredByState() { } @Override - public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, String orderBy) { - return "SELECT %s FROM %s %s %s %s OFFSET ? ROWS FETCH NEXT ? ROWS ONLY" - .formatted(columns, tableName, alias, whereClause, orderBy); + public String selectTableTemplate(String columns, String tableName, String alias, String whereClause, + String orderBy) { + return "SELECT %s FROM %s %s %s %s OFFSET ? ROWS FETCH NEXT ? ROWS ONLY".formatted(columns, tableName, + alias, whereClause, orderBy); } @Override diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatements.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatements.java index c8faf84e96..f16cae7b84 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatements.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/SqlStatements.java @@ -654,5 +654,6 @@ public interface SqlStatements { String selectCountTableTemplate(String countBy, String tableName, String alias, String whereClause); - String selectTableTemplate(String columns, String tableName, String alias, String whereClause, String orderBy); + String selectTableTemplate(String columns, String tableName, String alias, String whereClause, + String orderBy); } From b250be0ed84b414893688ec213c824a05fb62aa1 Mon Sep 17 00:00:00 2001 From: yoni Date: Tue, 12 Nov 2024 16:05:11 +0200 Subject: [PATCH 09/12] Added maxLifeTime property to RegistryDatasourceProducer --- .../storage/impl/sql/RegistryDatasourceProducer.java | 7 +++++++ .../partials/getting-started/ref-registry-all-configs.adoc | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java index 99a5c8ac3a..0c26e3e9f8 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java @@ -49,6 +49,10 @@ public class RegistryDatasourceProducer { @Info(category = "storage", description = "Application datasource pool maximum size", availableSince = "3.0.0") String maxSize; + @ConfigProperty(name = "apicurio.datasource.jdbc.max-life-time", defaultValue = "0") + @Info(category = "storage", description = "Maximum lifetime of a connection in seconds", availableSince = "3.0.5") + String maxLifeTime; + @Produces @ApplicationScoped @Named("application") @@ -75,6 +79,9 @@ public AgroalDataSource produceDatasource() throws SQLException { props.put(AgroalPropertiesReader.TRANSACTION_ISOLATION, TransactionIsolation.READ_COMMITTED.name()); props.put(AgroalPropertiesReader.TRANSACTION_REQUIREMENT, TransactionRequirement.WARN.name()); props.put(AgroalPropertiesReader.FLUSH_ON_CLOSE, "true"); + props.put(AgroalPropertiesReader.MAX_LIFETIME_S, maxLifeTime); + + AgroalDataSource datasource = AgroalDataSource .from(new AgroalPropertiesReader().readProperties(props).get()); diff --git a/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc b/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc index 82679453f9..8e18478a61 100644 --- a/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc +++ b/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc @@ -684,6 +684,11 @@ The following {registry} configuration options are available for each component |`20` |`3.0.0` |Application datasource pool minimum size +|`apicurio.datasource.jdbc.max-life-time` +|`string` +|`0` +|`3.0.5` +|Maximum lifetime of a connection in seconds |`apicurio.datasource.password` |`string` |`sa` From e36376d08e9e54bad72c969d27693b1a2098dbf7 Mon Sep 17 00:00:00 2001 From: yoni Date: Tue, 12 Nov 2024 16:29:01 +0200 Subject: [PATCH 10/12] spotless:apply --- .../registry/storage/impl/sql/RegistryDatasourceProducer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java index 0c26e3e9f8..8156832ca1 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java @@ -81,8 +81,6 @@ public AgroalDataSource produceDatasource() throws SQLException { props.put(AgroalPropertiesReader.FLUSH_ON_CLOSE, "true"); props.put(AgroalPropertiesReader.MAX_LIFETIME_S, maxLifeTime); - - AgroalDataSource datasource = AgroalDataSource .from(new AgroalPropertiesReader().readProperties(props).get()); From b78a0ba4c186998d578e8b2c8290f8b9cd818202 Mon Sep 17 00:00:00 2001 From: yoni Date: Tue, 12 Nov 2024 16:57:57 +0200 Subject: [PATCH 11/12] fixing doc version of `apicurio.datasource.jdbc.max-life-time` --- .../ROOT/partials/getting-started/ref-registry-all-configs.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc b/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc index 8e18478a61..e9d646924a 100644 --- a/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc +++ b/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc @@ -687,7 +687,7 @@ The following {registry} configuration options are available for each component |`apicurio.datasource.jdbc.max-life-time` |`string` |`0` -|`3.0.5` +|`3.0.4` |Maximum lifetime of a connection in seconds |`apicurio.datasource.password` |`string` From b5495e26167d1c1b610b4a774e441d6c6b6edc7c Mon Sep 17 00:00:00 2001 From: yoni Date: Wed, 13 Nov 2024 12:18:35 +0200 Subject: [PATCH 12/12] adding .seconds to apicurio.datasource.jdbc.max-life-time property --- .../registry/storage/impl/sql/RegistryDatasourceProducer.java | 2 +- .../ROOT/partials/getting-started/ref-registry-all-configs.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java index 8156832ca1..51f03199f8 100644 --- a/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java +++ b/app/src/main/java/io/apicurio/registry/storage/impl/sql/RegistryDatasourceProducer.java @@ -49,7 +49,7 @@ public class RegistryDatasourceProducer { @Info(category = "storage", description = "Application datasource pool maximum size", availableSince = "3.0.0") String maxSize; - @ConfigProperty(name = "apicurio.datasource.jdbc.max-life-time", defaultValue = "0") + @ConfigProperty(name = "apicurio.datasource.jdbc.max-life-time.seconds", defaultValue = "0") @Info(category = "storage", description = "Maximum lifetime of a connection in seconds", availableSince = "3.0.5") String maxLifeTime; diff --git a/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc b/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc index e9d646924a..e19dfc62c5 100644 --- a/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc +++ b/docs/modules/ROOT/partials/getting-started/ref-registry-all-configs.adoc @@ -684,7 +684,7 @@ The following {registry} configuration options are available for each component |`20` |`3.0.0` |Application datasource pool minimum size -|`apicurio.datasource.jdbc.max-life-time` +|`apicurio.datasource.jdbc.max-life-time.seconds` |`string` |`0` |`3.0.4`