This is an automated email from the ASF dual-hosted git repository.

yufei pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git


The following commit(s) were added to refs/heads/main by this push:
     new 0c48970b1 [JDBC] Part3: Plumb JDBC module to Quarkus (#1371)
0c48970b1 is described below

commit 0c48970b1fb620b404c72f68ddb3cda9100e51b4
Author: Prashant Singh <[email protected]>
AuthorDate: Fri Apr 25 20:08:07 2025 -0700

    [JDBC] Part3: Plumb JDBC module to Quarkus (#1371)
---
 bom/build.gradle.kts                               |   1 +
 .../persistence/relational-jdbc/build.gradle.kts   |   5 +-
 .../persistence/relational/jdbc/DatabaseType.java  |  49 +++++++
 .../relational/jdbc/DatasourceOperations.java      |   8 --
 .../relational/jdbc/JdbcBasePersistenceImpl.java   | 156 +++++++++------------
 .../jdbc/JdbcMetaStoreManagerFactory.java          |  23 +--
 .../relational/jdbc/models/ModelEntity.java        |   7 +-
 .../h2/{schema-v1-h2.sql => schema-v1.sql}         |   0
 .../src/main/resources/postgres/schema-v1.sql      |  15 +-
 ...toreManagerWithJdbcBasePersistenceImplTest.java |   9 +-
 gradle/projects.main.properties                    |   1 +
 .../it/test/PolarisRestCatalogIntegrationTest.java |   6 +
 .../PolarisRestCatalogViewIntegrationBase.java     |   8 +-
 quarkus/admin/build.gradle.kts                     |   3 +
 .../jdbc/RelationalJdbcAdminProfile.java           |  32 +++--
 .../jdbc/RelationalJdbcBootstrapCommandTest.java   |  22 +--
 .../jdbc/RelationalJdbcPurgeCommandTest.java       |  32 ++---
 quarkus/server/build.gradle.kts                    |   2 +
 quarkus/service/build.gradle.kts                   |  18 ++-
 .../relational/jdbc/JdbcQuarkusApplicationIT.java  |  25 ++--
 .../jdbc/JdbcQuarkusManagementServiceIT.java       |  25 ++--
 .../it/relational/jdbc/JdbcQuarkusViewFileIT.java  |  25 ++--
 .../it/relational/jdbc/JdbcRestCatalogIT.java      |  25 ++--
 .../quarkus/catalog/IcebergCatalogTest.java        |   5 +
 .../quarkus/catalog/IcebergCatalogViewTest.java    |   5 +
 .../test-commons}/build.gradle.kts                 |  33 +++--
 .../PostgresRelationalJdbcLifeCycleManagement.java |  86 ++++++++++++
 .../test/commons/RelationalJdbcProfile.java        |  30 ++--
 28 files changed, 383 insertions(+), 273 deletions(-)

diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts
index 11859b32c..4fc33d96c 100644
--- a/bom/build.gradle.kts
+++ b/bom/build.gradle.kts
@@ -46,6 +46,7 @@ dependencies {
     api(project(":polaris-jpa-model"))
 
     api(project(":polaris-quarkus-admin"))
+    api(project(":polaris-quarkus-test-commons"))
     api(project(":polaris-quarkus-defaults"))
     api(project(":polaris-quarkus-server"))
     api(project(":polaris-quarkus-service"))
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/extension/persistence/relational-jdbc/build.gradle.kts
index 0895d21ec..82f67c8a5 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ b/extension/persistence/relational-jdbc/build.gradle.kts
@@ -17,7 +17,10 @@
  * under the License.
  */
 
-plugins { id("polaris-server") }
+plugins {
+  id("polaris-server")
+  alias(libs.plugins.jandex)
+}
 
 dependencies {
   implementation(project(":polaris-core"))
diff --git 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/DatabaseType.java
 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/DatabaseType.java
new file mode 100644
index 000000000..cdf6ddc43
--- /dev/null
+++ 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/DatabaseType.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.polaris.extension.persistence.relational.jdbc;
+
+import java.util.Locale;
+
+public enum DatabaseType {
+  POSTGRES("postgres"),
+  H2("h2");
+
+  private final String displayName; // Store the user-friendly name
+
+  DatabaseType(String displayName) {
+    this.displayName = displayName;
+  }
+
+  // Method to get the user-friendly display name
+  public String getDisplayName() {
+    return displayName;
+  }
+
+  public static DatabaseType fromDisplayName(String displayName) {
+    return switch (displayName.toLowerCase(Locale.ROOT)) {
+      case "h2" -> DatabaseType.H2;
+      case "postgresql" -> DatabaseType.POSTGRES;
+      default -> throw new IllegalStateException("Unsupported DatabaseType: '" 
+ displayName + "'");
+    };
+  }
+
+  public String getInitScriptResource() {
+    return String.format("%s/schema-v1.sql", this.getDisplayName());
+  }
+}
diff --git 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/DatasourceOperations.java
 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/DatasourceOperations.java
index 4884dde4e..6fec8e67f 100644
--- 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/DatasourceOperations.java
+++ 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/DatasourceOperations.java
@@ -35,13 +35,9 @@ import java.util.function.Function;
 import java.util.function.Predicate;
 import javax.sql.DataSource;
 import 
org.apache.polaris.extension.persistence.relational.jdbc.models.Converter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class DatasourceOperations {
-  private static final Logger LOGGER = 
LoggerFactory.getLogger(DatasourceOperations.class);
 
-  private static final String ALREADY_EXISTS_STATE_POSTGRES = "42P07";
   private static final String CONSTRAINT_VIOLATION_SQL_CODE = "23505";
 
   private final DataSource datasource;
@@ -189,10 +185,6 @@ public class DatasourceOperations {
     return CONSTRAINT_VIOLATION_SQL_CODE.equals(e.getSQLState());
   }
 
-  public boolean isAlreadyExistsException(SQLException e) {
-    return ALREADY_EXISTS_STATE_POSTGRES.equals(e.getSQLState());
-  }
-
   private Connection borrowConnection() throws SQLException {
     return datasource.getConnection();
   }
diff --git 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcBasePersistenceImpl.java
 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcBasePersistenceImpl.java
index 24bb92702..ea99ed98d 100644
--- 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcBasePersistenceImpl.java
+++ 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcBasePersistenceImpl.java
@@ -23,6 +23,7 @@ import static 
org.apache.polaris.extension.persistence.relational.jdbc.QueryGene
 import jakarta.annotation.Nonnull;
 import jakarta.annotation.Nullable;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -86,44 +87,14 @@ public class JdbcBasePersistenceImpl implements 
BasePersistence, IntegrationPers
       @Nonnull PolarisBaseEntity entity,
       boolean nameOrParentChanged,
       PolarisBaseEntity originalEntity) {
-    ModelEntity modelEntity = ModelEntity.fromEntity(entity);
-    String query;
-    if (originalEntity == null) {
-      try {
-        query = generateInsertQuery(modelEntity, realmId);
-        datasourceOperations.executeUpdate(query);
-      } catch (SQLException e) {
-        if ((datasourceOperations.isConstraintViolation(e)
-            || datasourceOperations.isAlreadyExistsException(e))) {
-          throw new EntityAlreadyExistsException(entity, e);
-        } else {
-          throw new RuntimeException(
-              String.format("Failed to write entity due to %s", 
e.getMessage()), e);
-        }
-      }
-    } else {
-      Map<String, Object> params =
-          Map.of(
-              "id",
-              originalEntity.getId(),
-              "catalog_id",
-              originalEntity.getCatalogId(),
-              "entity_version",
-              originalEntity.getEntityVersion(),
-              "realm_id",
-              realmId);
-      query = generateUpdateQuery(modelEntity, params);
-      try {
-        int rowsUpdated = datasourceOperations.executeUpdate(query);
-        if (rowsUpdated == 0) {
-          throw new RetryOnConcurrencyException(
-              "Entity '%s' id '%s' concurrently modified; expected version %s",
-              entity.getName(), entity.getId(), 
originalEntity.getEntityVersion());
-        }
-      } catch (SQLException e) {
-        throw new RuntimeException(
-            String.format("Failed to write entity due to %s", e.getMessage()), 
e);
-      }
+    try {
+      datasourceOperations.runWithinTransaction(
+          statement -> {
+            persistEntity(callCtx, entity, originalEntity, statement);
+            return true;
+          });
+    } catch (SQLException e) {
+      throw new RuntimeException("Error persisting entity", e);
     }
   }
 
@@ -137,70 +108,21 @@ public class JdbcBasePersistenceImpl implements 
BasePersistence, IntegrationPers
           statement -> {
             for (int i = 0; i < entities.size(); i++) {
               PolarisBaseEntity entity = entities.get(i);
-              ModelEntity modelEntity = ModelEntity.fromEntity(entity);
+              PolarisBaseEntity originalEntity =
+                  originalEntities != null ? originalEntities.get(i) : null;
 
               // first, check if the entity has already been created, in which 
case we will simply
               // return it.
               PolarisBaseEntity entityFound =
                   lookupEntity(
                       callCtx, entity.getCatalogId(), entity.getId(), 
entity.getTypeCode());
-              if (entityFound != null) {
+              if (entityFound != null && originalEntity == null) {
                 // probably the client retried, simply return it
                 // TODO: Check correctness of returning entityFound vs entity 
here. It may have
                 // already been updated after the creation.
                 continue;
               }
-              // lookup by name
-              EntityNameLookupRecord exists =
-                  lookupEntityIdAndSubTypeByName(
-                      callCtx,
-                      entity.getCatalogId(),
-                      entity.getParentId(),
-                      entity.getTypeCode(),
-                      entity.getName());
-              if (exists != null) {
-                throw new EntityAlreadyExistsException(entity);
-              }
-              String query;
-              if (originalEntities == null || originalEntities.get(i) == null) 
{
-                try {
-                  query = generateInsertQuery(modelEntity, realmId);
-                  statement.executeUpdate(query);
-                } catch (SQLException e) {
-                  if ((datasourceOperations.isConstraintViolation(e)
-                      || datasourceOperations.isAlreadyExistsException(e))) {
-                    throw new EntityAlreadyExistsException(entity, e);
-                  } else {
-                    throw new RuntimeException(
-                        String.format("Failed to write entity due to %s", 
e.getMessage()), e);
-                  }
-                }
-              } else {
-                Map<String, Object> params =
-                    Map.of(
-                        "id",
-                        originalEntities.get(i).getId(),
-                        "catalog_id",
-                        originalEntities.get(i).getCatalogId(),
-                        "entity_version",
-                        originalEntities.get(i).getEntityVersion(),
-                        "realm_id",
-                        realmId);
-                query = generateUpdateQuery(modelEntity, params);
-                try {
-                  int rowsUpdated = statement.executeUpdate(query);
-                  if (rowsUpdated == 0) {
-                    throw new RetryOnConcurrencyException(
-                        "Entity '%s' id '%s' concurrently modified; expected 
version %s",
-                        entity.getName(),
-                        entity.getId(),
-                        originalEntities.get(i).getEntityVersion());
-                  }
-                } catch (SQLException e) {
-                  throw new RuntimeException(
-                      String.format("Failed to write entity due to %s", 
e.getMessage()), e);
-                }
-              }
+              persistEntity(callCtx, entity, originalEntity, statement);
             }
             return true;
           });
@@ -212,6 +134,56 @@ public class JdbcBasePersistenceImpl implements 
BasePersistence, IntegrationPers
     }
   }
 
+  private void persistEntity(
+      @Nonnull PolarisCallContext callCtx,
+      @Nonnull PolarisBaseEntity entity,
+      PolarisBaseEntity originalEntity,
+      Statement statement)
+      throws SQLException {
+    ModelEntity modelEntity = ModelEntity.fromEntity(entity);
+    if (originalEntity == null) {
+      try {
+        statement.executeUpdate(generateInsertQuery(modelEntity, realmId));
+      } catch (SQLException e) {
+        if (datasourceOperations.isConstraintViolation(e)) {
+          PolarisBaseEntity existingEntity =
+              lookupEntityByName(
+                  callCtx,
+                  entity.getCatalogId(),
+                  entity.getParentId(),
+                  entity.getTypeCode(),
+                  entity.getName());
+          throw new EntityAlreadyExistsException(existingEntity, e);
+        } else {
+          throw new RuntimeException(
+              String.format("Failed to write entity due to %s", 
e.getMessage()), e);
+        }
+      }
+    } else {
+      Map<String, Object> params =
+          Map.of(
+              "id",
+              originalEntity.getId(),
+              "catalog_id",
+              originalEntity.getCatalogId(),
+              "entity_version",
+              originalEntity.getEntityVersion(),
+              "realm_id",
+              realmId);
+      try {
+        int rowsUpdated = 
statement.executeUpdate(generateUpdateQuery(modelEntity, params));
+        if (rowsUpdated == 0) {
+          throw new RetryOnConcurrencyException(
+              "Entity '%s' id '%s' concurrently modified; expected version %s",
+              originalEntity.getName(), originalEntity.getId(), 
originalEntity.getEntityVersion());
+        }
+      } catch (SQLException e) {
+        throw new RuntimeException(
+            String.format("Failed to write entity due to %s", e.getMessage()), 
e);
+      }
+    }
+  }
+
   @Override
   public void writeToGrantRecords(
       @Nonnull PolarisCallContext callCtx, @Nonnull PolarisGrantRecord 
grantRec) {
@@ -492,6 +464,8 @@ public class JdbcBasePersistenceImpl implements 
BasePersistence, IntegrationPers
         throw new IllegalStateException(
             String.format(
                 "More than one grant record %s for a given Grant record", 
results.getFirst()));
+      } else if (results.isEmpty()) {
+        return null;
       }
       return results.getFirst();
     } catch (SQLException e) {
diff --git 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java
 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java
index 07ac7f972..0f981a99b 100644
--- 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java
+++ 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java
@@ -21,7 +21,9 @@ package 
org.apache.polaris.extension.persistence.relational.jdbc;
 import io.smallrye.common.annotation.Identifier;
 import jakarta.annotation.Nullable;
 import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Instance;
 import jakarta.inject.Inject;
+import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Map;
@@ -37,6 +39,7 @@ import org.apache.polaris.core.entity.PolarisEntityConstants;
 import org.apache.polaris.core.entity.PolarisEntitySubType;
 import org.apache.polaris.core.entity.PolarisEntityType;
 import org.apache.polaris.core.entity.PolarisPrincipalSecrets;
+import org.apache.polaris.core.persistence.AtomicOperationMetaStoreManager;
 import org.apache.polaris.core.persistence.BasePersistence;
 import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
 import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
@@ -46,7 +49,6 @@ import org.apache.polaris.core.persistence.cache.EntityCache;
 import org.apache.polaris.core.persistence.dao.entity.BaseResult;
 import org.apache.polaris.core.persistence.dao.entity.EntityResult;
 import org.apache.polaris.core.persistence.dao.entity.PrincipalSecretsResult;
-import 
org.apache.polaris.core.persistence.transactional.TransactionalMetaStoreManagerImpl;
 import org.apache.polaris.core.storage.PolarisStorageIntegrationProvider;
 import org.apache.polaris.core.storage.cache.StorageCredentialCache;
 import org.slf4j.Logger;
@@ -68,10 +70,9 @@ public class JdbcMetaStoreManagerFactory implements 
MetaStoreManagerFactory {
   final Map<String, EntityCache> entityCacheMap = new HashMap<>();
   final Map<String, Supplier<BasePersistence>> sessionSupplierMap = new 
HashMap<>();
   protected final PolarisDiagnostics diagServices = new 
PolarisDefaultDiagServiceImpl();
-  // TODO: Pending discussion of if we should have one Database per realm or 1 
schema per realm
-  // or realm should be a primary key on all the tables.
-  @Inject DataSource dataSource;
+
   @Inject PolarisStorageIntegrationProvider storageIntegrationProvider;
+  @Inject Instance<DataSource> dataSource;
 
   protected JdbcMetaStoreManagerFactory() {}
 
@@ -86,7 +87,7 @@ public class JdbcMetaStoreManagerFactory implements 
MetaStoreManagerFactory {
   }
 
   protected PolarisMetaStoreManager createNewMetaStoreManager() {
-    return new TransactionalMetaStoreManagerImpl();
+    return new AtomicOperationMetaStoreManager();
   }
 
   private void initializeForRealm(
@@ -106,12 +107,16 @@ public class JdbcMetaStoreManagerFactory implements 
MetaStoreManagerFactory {
   }
 
   private DatasourceOperations getDatasourceOperations(boolean isBootstrap) {
-    DatasourceOperations databaseOperations = new 
DatasourceOperations(dataSource);
+    DatasourceOperations databaseOperations = new 
DatasourceOperations(dataSource.get());
     if (isBootstrap) {
-      // TODO: see if we need to take script from Quarkus or can we just
-      // use the script committed in the repo.
       try {
-        
databaseOperations.executeScript("scripts/postgres/schema-v1-postgres.sql");
+        DatabaseType databaseType;
+        try (Connection connection = dataSource.get().getConnection()) {
+          String productName = 
connection.getMetaData().getDatabaseProductName();
+          databaseType = DatabaseType.fromDisplayName(productName);
+        }
+        databaseOperations.executeScript(
+            String.format("%s/schema-v1.sql", databaseType.getDisplayName()));
       } catch (SQLException e) {
         throw new RuntimeException(
             String.format("Error executing sql script: %s", e.getMessage()), 
e);
diff --git 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/models/ModelEntity.java
 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/models/ModelEntity.java
index 5ec7bbf95..948f9a869 100644
--- 
a/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/models/ModelEntity.java
+++ 
b/extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/models/ModelEntity.java
@@ -152,8 +152,11 @@ public class ModelEntity implements Converter<ModelEntity> 
{
         .purgeTimestamp(r.getObject("purge_timestamp", Long.class))
         .toPurgeTimestamp(r.getObject("to_purge_timestamp", Long.class))
         .lastUpdateTimestamp(r.getObject("last_update_timestamp", Long.class))
-        .properties(r.getObject("properties", String.class))
-        .internalProperties(r.getObject("internal_properties", String.class))
+        .properties(
+            r.getString("properties")) // required for extracting when the 
underlying type is JSONB
+        .internalProperties(
+            r.getString(
+                "internal_properties")) // required for extracting when the 
underlying type is JSONB
         .grantRecordsVersion(r.getObject("grant_records_version", 
Integer.class))
         .build();
   }
diff --git 
a/extension/persistence/relational-jdbc/src/main/resources/h2/schema-v1-h2.sql 
b/extension/persistence/relational-jdbc/src/main/resources/h2/schema-v1.sql
similarity index 100%
rename from 
extension/persistence/relational-jdbc/src/main/resources/h2/schema-v1-h2.sql
rename to 
extension/persistence/relational-jdbc/src/main/resources/h2/schema-v1.sql
diff --git a/scripts/postgres/schema-v1-postgresql.sql 
b/extension/persistence/relational-jdbc/src/main/resources/postgres/schema-v1.sql
similarity index 92%
rename from scripts/postgres/schema-v1-postgresql.sql
rename to 
extension/persistence/relational-jdbc/src/main/resources/postgres/schema-v1.sql
index 3e18d9e30..12da8f7a8 100644
--- a/scripts/postgres/schema-v1-postgresql.sql
+++ 
b/extension/persistence/relational-jdbc/src/main/resources/postgres/schema-v1.sql
@@ -15,16 +15,12 @@
 -- KIND, either express or implied.  See the License for the
 -- specific language governing permissions and limitations
 -- under the License.
---
 
--- Note: Database and schema creation is not included in this script. Please 
create the database and
--- schema before running this script. for example in psql:
--- CREATE DATABASE polaris_db;
--- \c polaris_db
--- CREATE SCHEMA polaris_schema;
--- set search_path to polaris_schema;
+CREATE SCHEMA IF NOT EXISTS POLARIS_SCHEMA;
+SET search_path TO POLARIS_SCHEMA;
 
 CREATE TABLE IF NOT EXISTS entities (
+    realm_id TEXT NOT NULL,
     catalog_id BIGINT NOT NULL,
     id BIGINT NOT NULL,
     parent_id BIGINT NOT NULL,
@@ -49,6 +45,7 @@ CREATE INDEX IF NOT EXISTS idx_entities ON entities 
(realm_id, catalog_id, id);
 
 COMMENT ON TABLE entities IS 'all the entities';
 
+COMMENT ON COLUMN entities.realm_id IS 'realm_id used for multi-tenancy';
 COMMENT ON COLUMN entities.catalog_id IS 'catalog id';
 COMMENT ON COLUMN entities.id IS 'entity id';
 COMMENT ON COLUMN entities.parent_id IS 'entity id of parent';
@@ -65,7 +62,7 @@ COMMENT ON COLUMN entities.internal_properties IS 'entities 
internal properties
 COMMENT ON COLUMN entities.grant_records_version IS 'the version of grant 
records change on the entity';
 
 CREATE TABLE IF NOT EXISTS grant_records (
-    realm_id INT NOT NULL,
+    realm_id TEXT NOT NULL,
     securable_catalog_id BIGINT NOT NULL,
     securable_id BIGINT NOT NULL,
     grantee_catalog_id BIGINT NOT NULL,
@@ -84,7 +81,7 @@ COMMENT ON COLUMN grant_records.privilege_code IS 'privilege 
code';
 
 
 CREATE TABLE IF NOT EXISTS principal_authentication_data (
-    realm_id INT NOT NULL,
+    realm_id TEXT NOT NULL,
     principal_id BIGINT NOT NULL,
     principal_client_id VARCHAR(255) NOT NULL,
     main_secret_hash VARCHAR(255) NOT NULL,
diff --git 
a/extension/persistence/relational-jdbc/src/test/java/org/apache/polaris/extension/persistence/impl/relational/jdbc/AtomicMetastoreManagerWithJdbcBasePersistenceImplTest.java
 
b/extension/persistence/relational-jdbc/src/test/java/org/apache/polaris/extension/persistence/impl/relational/jdbc/AtomicMetastoreManagerWithJdbcBasePersistenceImplTest.java
index cdc31685b..72ef15913 100644
--- 
a/extension/persistence/relational-jdbc/src/test/java/org/apache/polaris/extension/persistence/impl/relational/jdbc/AtomicMetastoreManagerWithJdbcBasePersistenceImplTest.java
+++ 
b/extension/persistence/relational-jdbc/src/test/java/org/apache/polaris/extension/persistence/impl/relational/jdbc/AtomicMetastoreManagerWithJdbcBasePersistenceImplTest.java
@@ -31,6 +31,7 @@ import 
org.apache.polaris.core.config.PolarisConfigurationStore;
 import org.apache.polaris.core.persistence.AtomicOperationMetaStoreManager;
 import org.apache.polaris.core.persistence.BasePolarisMetaStoreManagerTest;
 import org.apache.polaris.core.persistence.PolarisTestMetaStoreManager;
+import org.apache.polaris.extension.persistence.relational.jdbc.DatabaseType;
 import 
org.apache.polaris.extension.persistence.relational.jdbc.DatasourceOperations;
 import 
org.apache.polaris.extension.persistence.relational.jdbc.JdbcBasePersistenceImpl;
 import org.h2.jdbcx.JdbcConnectionPool;
@@ -49,9 +50,13 @@ public class 
AtomicMetastoreManagerWithJdbcBasePersistenceImplTest
     PolarisDiagnostics diagServices = new PolarisDefaultDiagServiceImpl();
     DatasourceOperations datasourceOperations = new 
DatasourceOperations(createH2DataSource());
     try {
-      datasourceOperations.executeScript("h2/schema-v1-h2.sql");
+      datasourceOperations.executeScript(
+          String.format("%s/schema-v1.sql", DatabaseType.H2.getDisplayName()));
     } catch (SQLException e) {
-      throw new RuntimeException(String.format("Error executing h2 script: 
%s", e.getMessage()), e);
+      throw new RuntimeException(
+          String.format(
+              "Error executing %s script: %s", 
DatabaseType.H2.getDisplayName(), e.getMessage()),
+          e);
     }
 
     JdbcBasePersistenceImpl basePersistence =
diff --git a/gradle/projects.main.properties b/gradle/projects.main.properties
index 8debf1055..6161eee47 100644
--- a/gradle/projects.main.properties
+++ b/gradle/projects.main.properties
@@ -30,6 +30,7 @@ polaris-quarkus-service=quarkus/service
 polaris-quarkus-server=quarkus/server
 polaris-quarkus-spark-tests=quarkus/spark-tests
 polaris-quarkus-admin=quarkus/admin
+polaris-quarkus-test-commons=quarkus/test-commons
 polaris-quarkus-run-script=quarkus/run-script
 polaris-eclipselink=extension/persistence/eclipselink
 polaris-jpa-model=extension/persistence/jpa-model
diff --git 
a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationTest.java
 
b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationTest.java
index 2ab3f9325..e5c8619e1 100644
--- 
a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationTest.java
+++ 
b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationTest.java
@@ -99,7 +99,9 @@ import org.apache.polaris.service.it.env.PolarisClient;
 import org.apache.polaris.service.it.ext.PolarisIntegrationTestExtension;
 import org.apache.polaris.service.types.GenericTable;
 import org.assertj.core.api.Assertions;
+import org.assertj.core.api.Assumptions;
 import org.assertj.core.api.InstanceOfAssertFactories;
+import org.assertj.core.configuration.PreferredAssumptionException;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
@@ -178,6 +180,10 @@ public class PolarisRestCatalogIntegrationTest extends 
CatalogTests<RESTCatalog>
     externalCatalogBase = testRootUri.resolve("external-catalog");
   }
 
+  static {
+    
Assumptions.setPreferredAssumptionException(PreferredAssumptionException.JUNIT5);
+  }
+
   @AfterAll
   static void close() throws Exception {
     client.close();
diff --git 
a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java
 
b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java
index 8dc48837b..c5df1c5d8 100644
--- 
a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java
+++ 
b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogViewIntegrationBase.java
@@ -37,9 +37,10 @@ import org.apache.polaris.service.it.env.ManagementApi;
 import org.apache.polaris.service.it.env.PolarisApiEndpoints;
 import org.apache.polaris.service.it.env.PolarisClient;
 import org.apache.polaris.service.it.ext.PolarisIntegrationTestExtension;
+import org.assertj.core.api.Assumptions;
+import org.assertj.core.configuration.PreferredAssumptionException;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assumptions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
@@ -57,6 +58,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
  */
 @ExtendWith(PolarisIntegrationTestExtension.class)
 public abstract class PolarisRestCatalogViewIntegrationBase extends 
ViewCatalogTests<RESTCatalog> {
+  static {
+    
Assumptions.setPreferredAssumptionException(PreferredAssumptionException.JUNIT5);
+  }
 
   private static ClientCredentials adminCredentials;
   private static PolarisApiEndpoints endpoints;
@@ -80,7 +84,7 @@ public abstract class PolarisRestCatalogViewIntegrationBase 
extends ViewCatalogT
 
   @BeforeEach
   public void before(TestInfo testInfo) {
-    Assumptions.assumeFalse(shouldSkip());
+    Assumptions.assumeThat(shouldSkip()).isFalse();
 
     String principalName = client.newEntityName("snowman-rest");
     String principalRoleName = client.newEntityName("rest-admin");
diff --git a/quarkus/admin/build.gradle.kts b/quarkus/admin/build.gradle.kts
index 357acfd94..7b19a132a 100644
--- a/quarkus/admin/build.gradle.kts
+++ b/quarkus/admin/build.gradle.kts
@@ -44,14 +44,17 @@ dependencies {
   implementation(project(":polaris-api-iceberg-service"))
 
   runtimeOnly(project(":polaris-eclipselink"))
+  runtimeOnly(project(":polaris-relational-jdbc"))
   runtimeOnly("org.postgresql:postgresql")
 
+  implementation("io.quarkus:quarkus-jdbc-postgresql")
   implementation(enforcedPlatform(libs.quarkus.bom))
   implementation("io.quarkus:quarkus-picocli")
   implementation("io.quarkus:quarkus-container-image-docker")
 
   implementation("org.jboss.slf4j:slf4j-jboss-logmanager")
 
+  testImplementation(project(":polaris-quarkus-test-commons"))
   testFixturesApi(project(":polaris-core"))
 
   testFixturesApi(enforcedPlatform(libs.quarkus.bom))
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcAdminProfile.java
similarity index 51%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to 
quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcAdminProfile.java
index 0895d21ec..a64002983 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ 
b/quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcAdminProfile.java
@@ -16,22 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.polaris.admintool.relational.jdbc;
 
-plugins { id("polaris-server") }
+import static 
org.apache.polaris.admintool.PostgresTestResourceLifecycleManager.INIT_SCRIPT;
 
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
+import java.util.List;
+import java.util.Map;
+import 
org.apache.polaris.test.commons.PostgresRelationalJdbcLifeCycleManagement;
+import org.apache.polaris.test.commons.RelationalJdbcProfile;
 
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
+public class RelationalJdbcAdminProfile extends RelationalJdbcProfile {
+  @Override
+  public Map<String, String> getConfigOverrides() {
+    return Map.of();
+  }
 
-  implementation(libs.smallrye.common.annotation) // @Identifier
-
-  testImplementation(libs.mockito.junit.jupiter)
-
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
+  @Override
+  public List<TestResourceEntry> testResources() {
+    return List.of(
+        new TestResourceEntry(
+            PostgresRelationalJdbcLifeCycleManagement.class,
+            Map.of(INIT_SCRIPT, "org/apache/polaris/admintool/init.sql")));
+  }
 }
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcBootstrapCommandTest.java
similarity index 62%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to 
quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcBootstrapCommandTest.java
index 0895d21ec..dcd5eb140 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ 
b/quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcBootstrapCommandTest.java
@@ -16,22 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.polaris.admintool.relational.jdbc;
 
-plugins { id("polaris-server") }
+import io.quarkus.test.junit.TestProfile;
+import org.apache.polaris.admintool.BootstrapCommandTestBase;
 
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
-
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
-
-  implementation(libs.smallrye.common.annotation) // @Identifier
-
-  testImplementation(libs.mockito.junit.jupiter)
-
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
-}
+@TestProfile(RelationalJdbcAdminProfile.class)
+public class RelationalJdbcBootstrapCommandTest extends 
BootstrapCommandTestBase {}
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcPurgeCommandTest.java
similarity index 54%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to 
quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcPurgeCommandTest.java
index 0895d21ec..43163f531 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ 
b/quarkus/admin/src/test/java/org/apache/polaris/admintool/relational/jdbc/RelationalJdbcPurgeCommandTest.java
@@ -16,22 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.polaris.admintool.relational.jdbc;
 
-plugins { id("polaris-server") }
+import io.quarkus.test.junit.TestProfile;
+import java.util.Map;
+import org.apache.polaris.admintool.PurgeCommandTestBase;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
 
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
-
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
-
-  implementation(libs.smallrye.common.annotation) // @Identifier
-
-  testImplementation(libs.mockito.junit.jupiter)
-
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
+@TestProfile(RelationalJdbcPurgeCommandTest.Profile.class)
+public class RelationalJdbcPurgeCommandTest extends PurgeCommandTestBase {
+  public static class Profile extends RelationalJdbcAdminProfile {
+    @Override
+    public Map<String, String> getConfigOverrides() {
+      return ImmutableMap.<String, String>builder()
+          .putAll(super.getConfigOverrides())
+          .put("pre-bootstrap", "true")
+          .build();
+    }
+  }
 }
diff --git a/quarkus/server/build.gradle.kts b/quarkus/server/build.gradle.kts
index 61c1b4666..70112fd06 100644
--- a/quarkus/server/build.gradle.kts
+++ b/quarkus/server/build.gradle.kts
@@ -50,6 +50,8 @@ dependencies {
 
   runtimeOnly(project(":polaris-eclipselink"))
   runtimeOnly("org.postgresql:postgresql")
+  runtimeOnly(project(":polaris-relational-jdbc"))
+  runtimeOnly("io.quarkus:quarkus-jdbc-postgresql")
 
   // enforce the Quarkus _platform_ here, to get a consistent and validated 
set of dependencies
   implementation(enforcedPlatform(libs.quarkus.bom))
diff --git a/quarkus/service/build.gradle.kts b/quarkus/service/build.gradle.kts
index eb729b020..97b3daa0d 100644
--- a/quarkus/service/build.gradle.kts
+++ b/quarkus/service/build.gradle.kts
@@ -23,11 +23,6 @@ plugins {
   id("polaris-quarkus")
 }
 
-configurations.all {
-  // exclude junit4 dependency for this module
-  exclude(group = "junit", module = "junit")
-}
-
 dependencies {
   implementation(project(":polaris-core"))
   implementation(project(":polaris-api-management-service"))
@@ -113,6 +108,12 @@ dependencies {
   testImplementation("software.amazon.awssdk:kms")
   testImplementation("software.amazon.awssdk:dynamodb")
 
+  runtimeOnly(project(":polaris-relational-jdbc"))
+  runtimeOnly("io.quarkus:quarkus-jdbc-postgresql") {
+    exclude(group = "org.antlr", module = "antlr4-runtime")
+    exclude(group = "org.scala-lang", module = "scala-library")
+    exclude(group = "org.scala-lang", module = "scala-reflect")
+  }
   testImplementation(platform(libs.quarkus.bom))
   testImplementation("io.quarkus:quarkus-junit5")
   testImplementation("io.quarkus:quarkus-junit5-mockito")
@@ -122,6 +123,13 @@ dependencies {
 
   testImplementation(libs.threeten.extra)
   testImplementation(libs.hawkular.agent.prometheus.scraper)
+
+  testImplementation(project(":polaris-quarkus-test-commons"))
+  testImplementation("io.quarkus:quarkus-junit5")
+  testImplementation(platform(libs.testcontainers.bom))
+  testImplementation("org.testcontainers:testcontainers")
+  testImplementation("org.testcontainers:postgresql")
+  testImplementation("org.postgresql:postgresql")
 }
 
 tasks.withType(Test::class.java).configureEach {
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusApplicationIT.java
similarity index 62%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to 
quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusApplicationIT.java
index 0895d21ec..dd718db73 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ 
b/quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusApplicationIT.java
@@ -16,22 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.polaris.service.quarkus.it.relational.jdbc;
 
-plugins { id("polaris-server") }
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import io.quarkus.test.junit.TestProfile;
+import org.apache.polaris.service.it.test.PolarisApplicationIntegrationTest;
+import org.apache.polaris.test.commons.RelationalJdbcProfile;
 
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
-
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
-
-  implementation(libs.smallrye.common.annotation) // @Identifier
-
-  testImplementation(libs.mockito.junit.jupiter)
-
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
-}
+@TestProfile(RelationalJdbcProfile.class)
+@QuarkusIntegrationTest
+public class JdbcQuarkusApplicationIT extends 
PolarisApplicationIntegrationTest {}
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusManagementServiceIT.java
similarity index 62%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to 
quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusManagementServiceIT.java
index 0895d21ec..0961889fd 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ 
b/quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusManagementServiceIT.java
@@ -16,22 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.polaris.service.quarkus.it.relational.jdbc;
 
-plugins { id("polaris-server") }
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import io.quarkus.test.junit.TestProfile;
+import 
org.apache.polaris.service.it.test.PolarisManagementServiceIntegrationTest;
+import org.apache.polaris.test.commons.RelationalJdbcProfile;
 
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
-
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
-
-  implementation(libs.smallrye.common.annotation) // @Identifier
-
-  testImplementation(libs.mockito.junit.jupiter)
-
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
-}
+@TestProfile(RelationalJdbcProfile.class)
+@QuarkusIntegrationTest
+public class JdbcQuarkusManagementServiceIT extends 
PolarisManagementServiceIntegrationTest {}
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusViewFileIT.java
similarity index 62%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to 
quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusViewFileIT.java
index 0895d21ec..7471032a5 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ 
b/quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcQuarkusViewFileIT.java
@@ -16,22 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.polaris.service.quarkus.it.relational.jdbc;
 
-plugins { id("polaris-server") }
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import io.quarkus.test.junit.TestProfile;
+import 
org.apache.polaris.service.it.test.PolarisRestCatalogViewFileIntegrationTest;
+import org.apache.polaris.test.commons.RelationalJdbcProfile;
 
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
-
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
-
-  implementation(libs.smallrye.common.annotation) // @Identifier
-
-  testImplementation(libs.mockito.junit.jupiter)
-
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
-}
+@TestProfile(RelationalJdbcProfile.class)
+@QuarkusIntegrationTest
+public class JdbcQuarkusViewFileIT extends 
PolarisRestCatalogViewFileIntegrationTest {}
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcRestCatalogIT.java
similarity index 62%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to 
quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcRestCatalogIT.java
index 0895d21ec..59b35fe91 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ 
b/quarkus/service/src/intTest/java/org/apache/polaris/service/quarkus/it/relational/jdbc/JdbcRestCatalogIT.java
@@ -16,22 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.polaris.service.quarkus.it.relational.jdbc;
 
-plugins { id("polaris-server") }
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import io.quarkus.test.junit.TestProfile;
+import org.apache.polaris.service.it.test.PolarisRestCatalogIntegrationTest;
+import org.apache.polaris.test.commons.RelationalJdbcProfile;
 
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
-
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
-
-  implementation(libs.smallrye.common.annotation) // @Identifier
-
-  testImplementation(libs.mockito.junit.jupiter)
-
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
-}
+@TestProfile(RelationalJdbcProfile.class)
+@QuarkusIntegrationTest
+public class JdbcRestCatalogIT extends PolarisRestCatalogIntegrationTest {}
diff --git 
a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogTest.java
 
b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogTest.java
index 210a792e2..832ac32bd 100644
--- 
a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogTest.java
+++ 
b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogTest.java
@@ -127,6 +127,7 @@ import org.apache.polaris.service.types.NotificationType;
 import org.apache.polaris.service.types.TableUpdateNotification;
 import org.assertj.core.api.Assertions;
 import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
+import org.assertj.core.configuration.PreferredAssumptionException;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assumptions;
 import org.junit.jupiter.api.BeforeAll;
@@ -146,6 +147,10 @@ import 
software.amazon.awssdk.services.sts.model.Credentials;
 
 @TestProfile(IcebergCatalogTest.Profile.class)
 public abstract class IcebergCatalogTest extends CatalogTests<IcebergCatalog> {
+  static {
+    org.assertj.core.api.Assumptions.setPreferredAssumptionException(
+        PreferredAssumptionException.JUNIT5);
+  }
 
   public static class Profile implements QuarkusTestProfile {
 
diff --git 
a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogViewTest.java
 
b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogViewTest.java
index 8d7708dd7..5123ea0e1 100644
--- 
a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogViewTest.java
+++ 
b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/IcebergCatalogViewTest.java
@@ -68,6 +68,8 @@ import 
org.apache.polaris.service.catalog.io.DefaultFileIOFactory;
 import org.apache.polaris.service.catalog.io.FileIOFactory;
 import org.apache.polaris.service.config.RealmEntityManagerFactory;
 import 
org.apache.polaris.service.storage.PolarisStorageIntegrationProviderImpl;
+import org.assertj.core.api.Assumptions;
+import org.assertj.core.configuration.PreferredAssumptionException;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
@@ -78,6 +80,9 @@ import org.mockito.Mockito;
 @QuarkusTest
 @TestProfile(IcebergCatalogViewTest.Profile.class)
 public class IcebergCatalogViewTest extends ViewCatalogTests<IcebergCatalog> {
+  static {
+    
Assumptions.setPreferredAssumptionException(PreferredAssumptionException.JUNIT5);
+  }
 
   public static class Profile implements QuarkusTestProfile {
 
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/test-commons/build.gradle.kts
similarity index 56%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to quarkus/test-commons/build.gradle.kts
index 0895d21ec..b3813c90b 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ b/quarkus/test-commons/build.gradle.kts
@@ -17,21 +17,26 @@
  * under the License.
  */
 
-plugins { id("polaris-server") }
-
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
-
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
+plugins {
+  alias(libs.plugins.jandex)
+  id("java-test-fixtures")
+}
 
-  implementation(libs.smallrye.common.annotation) // @Identifier
+configurations.all {
+  exclude(group = "org.antlr", module = "antlr4-runtime")
+  exclude(group = "org.scala-lang", module = "scala-library")
+  exclude(group = "org.scala-lang", module = "scala-reflect")
+}
 
-  testImplementation(libs.mockito.junit.jupiter)
+java {
+  sourceCompatibility = JavaVersion.VERSION_21
+  targetCompatibility = JavaVersion.VERSION_21
+}
 
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
+dependencies {
+  implementation(enforcedPlatform(libs.quarkus.bom))
+  implementation("io.quarkus:quarkus-junit5")
+  implementation(platform(libs.testcontainers.bom))
+  implementation("org.testcontainers:testcontainers")
+  implementation("org.testcontainers:postgresql")
 }
diff --git 
a/quarkus/test-commons/src/main/java/org/apache/polaris/test/commons/PostgresRelationalJdbcLifeCycleManagement.java
 
b/quarkus/test-commons/src/main/java/org/apache/polaris/test/commons/PostgresRelationalJdbcLifeCycleManagement.java
new file mode 100644
index 000000000..a85a70909
--- /dev/null
+++ 
b/quarkus/test-commons/src/main/java/org/apache/polaris/test/commons/PostgresRelationalJdbcLifeCycleManagement.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.polaris.test.commons;
+
+import io.quarkus.test.common.DevServicesContext;
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import java.util.Map;
+import org.testcontainers.containers.PostgreSQLContainer;
+import org.testcontainers.utility.DockerImageName;
+
+public class PostgresRelationalJdbcLifeCycleManagement
+    implements QuarkusTestResourceLifecycleManager, 
DevServicesContext.ContextAware {
+  public static final String INIT_SCRIPT = "init-script";
+
+  private PostgreSQLContainer<?> postgres;
+  private String initScript;
+  private DevServicesContext context;
+
+  @Override
+  public void init(Map<String, String> initArgs) {
+    initScript = initArgs.get(INIT_SCRIPT);
+  }
+
+  @Override
+  @SuppressWarnings("resource")
+  public Map<String, String> start() {
+    postgres =
+        new PostgreSQLContainer<>(DockerImageName.parse("postgres:17"))
+            .withDatabaseName("polaris_db")
+            .withUsername("polaris")
+            .withPassword("polaris");
+
+    if (initScript != null) {
+      postgres.withInitScript(initScript);
+    }
+
+    context.containerNetworkId().ifPresent(postgres::withNetworkMode);
+    postgres.start();
+    return Map.of(
+        "polaris.persistence.type",
+        "relational-jdbc",
+        "quarkus.datasource.db-kind",
+        "pgsql",
+        "quarkus.datasource.jdbc.url",
+        postgres.getJdbcUrl(),
+        "quarkus.datasource.username",
+        postgres.getUsername(),
+        "quarkus.datasource.password",
+        postgres.getPassword(),
+        "quarkus.datasource.jdbc.initial-size",
+        "10");
+  }
+
+  @Override
+  public void stop() {
+    if (postgres != null) {
+      try {
+        postgres.stop();
+      } finally {
+        postgres = null;
+      }
+    }
+  }
+
+  @Override
+  public void setIntegrationTestContext(DevServicesContext context) {
+    this.context = context;
+  }
+}
diff --git a/extension/persistence/relational-jdbc/build.gradle.kts 
b/quarkus/test-commons/src/main/java/org/apache/polaris/test/commons/RelationalJdbcProfile.java
similarity index 59%
copy from extension/persistence/relational-jdbc/build.gradle.kts
copy to 
quarkus/test-commons/src/main/java/org/apache/polaris/test/commons/RelationalJdbcProfile.java
index 0895d21ec..38f7bd61a 100644
--- a/extension/persistence/relational-jdbc/build.gradle.kts
+++ 
b/quarkus/test-commons/src/main/java/org/apache/polaris/test/commons/RelationalJdbcProfile.java
@@ -16,22 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+package org.apache.polaris.test.commons;
 
-plugins { id("polaris-server") }
+import io.quarkus.test.junit.QuarkusTestProfile;
+import java.util.List;
+import java.util.Map;
 
-dependencies {
-  implementation(project(":polaris-core"))
-  implementation(libs.slf4j.api)
-  implementation(libs.guava)
+public class RelationalJdbcProfile implements QuarkusTestProfile {
+  @Override
+  public Map<String, String> getConfigOverrides() {
+    return Map.of("polaris.persistence.auto-bootstrap-types", 
"relational-jdbc");
+  }
 
-  compileOnly(libs.jakarta.annotation.api)
-  compileOnly(libs.jakarta.enterprise.cdi.api)
-  compileOnly(libs.jakarta.inject.api)
-
-  implementation(libs.smallrye.common.annotation) // @Identifier
-
-  testImplementation(libs.mockito.junit.jupiter)
-
-  testImplementation(libs.h2)
-  testImplementation(testFixtures(project(":polaris-core")))
+  @Override
+  public List<TestResourceEntry> testResources() {
+    return List.of(
+        new QuarkusTestProfile.TestResourceEntry(
+            PostgresRelationalJdbcLifeCycleManagement.class, Map.of()));
+  }
 }

Reply via email to