Skip to content

Commit f5408af

Browse files
committed
[JDBC] Part3: Plumb JDBC module to quarkus
1 parent 4e186a8 commit f5408af

File tree

15 files changed

+273
-23
lines changed

15 files changed

+273
-23
lines changed

Diff for: extension/persistence/relational-jdbc/build.gradle.kts

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
* under the License.
1818
*/
1919

20-
plugins { id("polaris-server") }
20+
plugins {
21+
id("polaris-server")
22+
alias(libs.plugins.jandex)
23+
}
2124

2225
dependencies {
2326
implementation(project(":polaris-core"))
@@ -28,8 +31,6 @@ dependencies {
2831
compileOnly(libs.jakarta.inject.api)
2932

3033
implementation(libs.smallrye.common.annotation) // @Identifier
31-
32-
runtimeOnly(libs.postgresql)
3334
testImplementation(libs.mockito.junit.jupiter)
3435

3536
testImplementation(libs.h2)

Diff for: extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/DatasourceOperations.java

+29-5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.sql.SQLException;
2929
import java.sql.Statement;
3030
import java.util.List;
31+
import java.util.Locale;
3132
import java.util.Objects;
3233
import javax.sql.DataSource;
3334
import org.slf4j.Logger;
@@ -40,15 +41,21 @@ public class DatasourceOperations {
4041
private static final String CONSTRAINT_VIOLATION_SQL_CODE = "23505";
4142

4243
private final DataSource datasource;
44+
private final String realm;
4345

44-
public DatasourceOperations(DataSource datasource) {
46+
public DatasourceOperations(DataSource datasource, String realm) {
4547
this.datasource = datasource;
48+
this.realm = realm.toUpperCase(Locale.ROOT);
4649
}
4750

4851
public void executeScript(String scriptFilePath) throws SQLException {
4952
ClassLoader classLoader = DatasourceOperations.class.getClassLoader();
50-
try (Connection connection = borrowConnection();
53+
Statement realmSchemaStatement = null;
54+
try (Connection connection = datasource.getConnection();
5155
Statement statement = connection.createStatement()) {
56+
statement.execute(String.format("CREATE SCHEMA IF NOT EXISTS %s", realm));
57+
statement.close();
58+
realmSchemaStatement = borrowConnection().createStatement();
5259
BufferedReader reader =
5360
new BufferedReader(
5461
new InputStreamReader(
@@ -62,7 +69,7 @@ public void executeScript(String scriptFilePath) throws SQLException {
6269
if (line.endsWith(";")) { // Execute statement when semicolon is found
6370
String sql = sqlBuffer.toString().trim();
6471
try {
65-
int rowsUpdated = statement.executeUpdate(sql);
72+
int rowsUpdated = realmSchemaStatement.executeUpdate(sql);
6673
LOGGER.debug("Query {} executed {} rows affected", sql, rowsUpdated);
6774
} catch (SQLException e) {
6875
LOGGER.error("Error executing query {}", sql, e);
@@ -79,6 +86,10 @@ public void executeScript(String scriptFilePath) throws SQLException {
7986
} catch (SQLException e) {
8087
LOGGER.error("Error executing the script file", e);
8188
throw e;
89+
} finally {
90+
if (realmSchemaStatement != null) {
91+
realmSchemaStatement.close();
92+
}
8293
}
8394
}
8495

@@ -151,7 +162,6 @@ public void runWithinTransaction(TransactionCallback callback) throws SQLExcepti
151162
connection.close();
152163
} catch (SQLException e) {
153164
LOGGER.error("Error closing connection", e);
154-
throw e;
155165
}
156166
}
157167
}
@@ -171,6 +181,20 @@ public boolean isAlreadyExistsException(SQLException e) {
171181
}
172182

173183
private Connection borrowConnection() throws SQLException {
174-
return datasource.getConnection();
184+
Connection connection = datasource.getConnection();
185+
if (connection
186+
.getMetaData()
187+
.getDatabaseProductName()
188+
.toLowerCase(Locale.ROOT)
189+
.equals("postgresql")) {
190+
// connection.setSchema() doesn't work hence work this around.
191+
Statement s = connection.createStatement();
192+
s.execute("SET search_path TO " + realm);
193+
s.close();
194+
} else {
195+
connection.setSchema(realm);
196+
}
197+
198+
return connection;
175199
}
176200
}

Diff for: extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcCrudQueryGenerator.java

-3
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,6 @@ private static String getTableName(Class<?> entityClass) {
285285
throw new IllegalArgumentException("Unsupported entity class: " + entityClass.getName());
286286
}
287287

288-
// TODO: check if we want to make schema name configurable.
289-
tableName = "POLARIS_SCHEMA." + tableName;
290-
291288
return tableName;
292289
}
293290
}

Diff for: extension/persistence/relational-jdbc/src/main/java/org/apache/polaris/extension/persistence/relational/jdbc/JdbcMetaStoreManagerFactory.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import javax.sql.DataSource;
2727
import org.apache.polaris.core.PolarisDiagnostics;
2828
import org.apache.polaris.core.context.RealmContext;
29-
import org.apache.polaris.core.entity.*;
3029
import org.apache.polaris.core.persistence.*;
3130
import org.apache.polaris.core.persistence.bootstrap.RootCredentialsSet;
3231
import org.apache.polaris.core.storage.PolarisStorageIntegrationProvider;
@@ -49,11 +48,17 @@ public class JdbcMetaStoreManagerFactory extends LocalPolarisMetaStoreManagerFac
4948
@Inject PolarisStorageIntegrationProvider storageIntegrationProvider;
5049

5150
protected JdbcMetaStoreManagerFactory() {
52-
this(null);
51+
this(null, null, null);
5352
}
5453

55-
protected JdbcMetaStoreManagerFactory(@Nonnull PolarisDiagnostics diagnostics) {
54+
@Inject
55+
protected JdbcMetaStoreManagerFactory(
56+
@Nonnull PolarisDiagnostics diagnostics,
57+
@Nonnull DataSource dataSource,
58+
@Nonnull PolarisStorageIntegrationProvider storageIntegrationProvider) {
5659
super(diagnostics);
60+
this.dataSource = dataSource;
61+
this.storageIntegrationProvider = storageIntegrationProvider;
5762
}
5863

5964
/**
@@ -68,11 +73,12 @@ protected PolarisMetaStoreManager createNewMetaStoreManager() {
6873
@Override
6974
protected void initializeForRealm(
7075
RealmContext realmContext, RootCredentialsSet rootCredentialsSet) {
71-
DatasourceOperations databaseOperations = new DatasourceOperations(dataSource);
76+
DatasourceOperations databaseOperations =
77+
new DatasourceOperations(dataSource, realmContext.getRealmIdentifier());
7278
// TODO: see if we need to take script from Quarkus or can we just
7379
// use the script committed in the repo.
7480
try {
75-
databaseOperations.executeScript("scripts/postgres/schema-v1-postgres.sql");
81+
databaseOperations.executeScript("postgres/schema-v1-postgresql.sql");
7682
} catch (SQLException e) {
7783
throw new RuntimeException(
7884
String.format("Error executing sql script: %s", e.getMessage()), e);

Diff for: extension/persistence/relational-jdbc/src/main/resources/h2/schema-v1-h2.sql

-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
-- under the License.
1818
--
1919

20-
CREATE SCHEMA IF NOT EXISTS POLARIS_SCHEMA;
21-
SET SCHEMA POLARIS_SCHEMA;
2220
DROP TABLE IF EXISTS entities;
2321
CREATE TABLE IF NOT EXISTS entities (
2422
catalog_id BIGINT NOT NULL,

Diff for: scripts/postgres/schema-v1-postgresql.sql renamed to extension/persistence/relational-jdbc/src/main/resources/postgres/schema-v1-postgresql.sql

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ CREATE TABLE IF NOT EXISTS entities (
3737
purge_timestamp BIGINT NOT NULL,
3838
to_purge_timestamp BIGINT NOT NULL,
3939
last_update_timestamp BIGINT NOT NULL,
40-
properties JSONB not null default '{}'::JSONB,
41-
internal_properties JSONB not null default '{}'::JSONB,
40+
properties TEXT NOT NULL default '{}',
41+
internal_properties TEXT NOT NULL default '{}',
4242
grant_records_version INT NOT NULL,
4343
PRIMARY KEY (id),
4444
CONSTRAINT constraint_name UNIQUE (catalog_id, parent_id, type_code, name)

Diff for: extension/persistence/relational-jdbc/src/test/java/org/apache/polaris/extension/persistence/impl/relational/jdbc/DatasourceOperationsTest.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import static org.mockito.Mockito.when;
2727

2828
import java.sql.Connection;
29+
import java.sql.DatabaseMetaData;
2930
import java.sql.ResultSet;
3031
import java.sql.SQLException;
3132
import java.sql.Statement;
@@ -50,13 +51,17 @@ public class DatasourceOperationsTest {
5051

5152
@Mock private ResultSet mockResultSet;
5253

54+
@Mock private DatabaseMetaData mockDatabaseMetaData;
55+
5356
private DatasourceOperations datasourceOperations;
5457

5558
@BeforeEach
5659
void setUp() throws Exception {
5760
when(mockDataSource.getConnection()).thenReturn(mockConnection);
61+
when(mockConnection.getMetaData()).thenReturn(mockDatabaseMetaData);
62+
when(mockDatabaseMetaData.getDatabaseProductName()).thenReturn("H2");
5863
when(mockConnection.createStatement()).thenReturn(mockStatement);
59-
datasourceOperations = new DatasourceOperations(mockDataSource);
64+
datasourceOperations = new DatasourceOperations(mockDataSource, "realm");
6065
}
6166

6267
@Test

Diff for: extension/persistence/relational-jdbc/src/test/java/org/apache/polaris/extension/persistence/impl/relational/jdbc/JdbcAtomicMetastoreManagerTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ public static DataSource createH2DataSource() {
4646
@Override
4747
protected PolarisTestMetaStoreManager createPolarisTestMetaStoreManager() {
4848
PolarisDiagnostics diagServices = new PolarisDefaultDiagServiceImpl();
49-
DatasourceOperations datasourceOperations = new DatasourceOperations(createH2DataSource());
49+
DatasourceOperations datasourceOperations =
50+
new DatasourceOperations(createH2DataSource(), "REALM");
5051
try {
5152
datasourceOperations.executeScript("h2/schema-v1-h2.sql");
5253
} catch (SQLException e) {
53-
throw new RuntimeException(String.format("Error executing h2 script: %s", e.getMessage()), e);
54+
throw new RuntimeException(String.format("Error executing h2 script: %s", e.getMessage()));
5455
}
5556

5657
JdbcBasePersistenceImpl basePersistence =

Diff for: quarkus/admin/build.gradle.kts

+2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ dependencies {
4444
implementation(project(":polaris-api-iceberg-service"))
4545

4646
runtimeOnly(project(":polaris-eclipselink"))
47+
runtimeOnly(project(":polaris-relational-jdbc"))
4748

49+
implementation("io.quarkus:quarkus-jdbc-postgresql")
4850
implementation(enforcedPlatform(libs.quarkus.bom))
4951
implementation("io.quarkus:quarkus-picocli")
5052
implementation("io.quarkus:quarkus-container-image-docker")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.admintool.relational.jdbc;
20+
21+
import io.quarkus.test.junit.TestProfile;
22+
import org.apache.polaris.admintool.BootstrapCommandTestBase;
23+
24+
@TestProfile(RelationalJdbcProfile.class)
25+
public class RelationalJdbcBootstrapCommandTest extends BootstrapCommandTestBase {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.admintool.relational.jdbc;
20+
21+
import static org.apache.polaris.admintool.PostgresTestResourceLifecycleManager.INIT_SCRIPT;
22+
23+
import io.quarkus.test.junit.QuarkusTestProfile;
24+
import java.util.List;
25+
import java.util.Map;
26+
import org.apache.polaris.admintool.PostgresRelationalJdbcLifeCycleManagement;
27+
28+
public class RelationalJdbcProfile implements QuarkusTestProfile {
29+
@Override
30+
public Map<String, String> getConfigOverrides() {
31+
return Map.of();
32+
}
33+
34+
@Override
35+
public List<TestResourceEntry> testResources() {
36+
return List.of(
37+
new TestResourceEntry(
38+
PostgresRelationalJdbcLifeCycleManagement.class,
39+
Map.of(INIT_SCRIPT, "org/apache/polaris/admintool/relational-jdbc/init.sql")));
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.admintool.relational.jdbc;
20+
21+
import io.quarkus.test.junit.TestProfile;
22+
import java.util.Map;
23+
import org.apache.polaris.admintool.PurgeCommandTestBase;
24+
import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
25+
26+
@TestProfile(RelationalJdbcPurgeCommandTest.Profile.class)
27+
public class RelationalJdbcPurgeCommandTest extends PurgeCommandTestBase {
28+
public static class Profile extends RelationalJdbcProfile {
29+
@Override
30+
public Map<String, String> getConfigOverrides() {
31+
return ImmutableMap.<String, String>builder()
32+
.putAll(super.getConfigOverrides())
33+
.put("pre-bootstrap", "true")
34+
.build();
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)