Skip to content

Commit 7519d8e

Browse files
devhawkCopilotchuck-dbos
authored
custom sys db schema support (#208)
fixes #116 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: chuck-dbos <134347445+chuck-dbos@users.noreply.github.com>
1 parent cc601d6 commit 7519d8e

16 files changed

Lines changed: 663 additions & 277 deletions

File tree

transact-cli/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ export PGPASSWORD=password
4242
java -jar dbos.jar migrate
4343
```
4444

45+
### Database Schema Configuration
46+
47+
By default, DBOS creates its system tables in the `dbos` schema. You can specify a custom schema name using the `--schema` option.
48+
49+
Example:
50+
```bash
51+
# Use a custom schema for all DBOS system tables
52+
java -jar dbos.jar migrate --schema myapp_schema
53+
54+
# All workflow commands will use the specified schema
55+
java -jar dbos.jar workflow list --schema myapp_schema
56+
```
57+
58+
## Commands
59+
4560
### `dbos migrate`
4661
Create DBOS system tables in your database. This command runs the migration commands specified in the `database.migrate` section of your config file.
4762

transact-cli/src/main/java/dev/dbos/transact/cli/DatabaseOptions.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public class DatabaseOptions {
2525
interactive = true)
2626
private String password;
2727

28+
@Option(
29+
names = {"--schema"},
30+
description = "Database schema name [default: dbos")
31+
private String schema;
32+
2833
public String url() {
2934
return this.url;
3035
}
@@ -37,7 +42,11 @@ public String password() {
3742
return this.password;
3843
}
3944

45+
public String schema() {
46+
return this.schema;
47+
}
48+
4049
public DBOSClient createClient() {
41-
return new DBOSClient(url(), user(), password());
50+
return new DBOSClient(url(), user(), password(), schema());
4251
}
4352
}

transact-cli/src/main/java/dev/dbos/transact/cli/MigrateCommand.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ public Integer call() throws Exception {
3838
out.format(" System Database: %s\n", dbOptions.url());
3939
out.format(" System Database User: %s\n", dbOptions.user());
4040

41-
MigrationManager.runMigrations(dbOptions.url(), dbOptions.user(), dbOptions.password());
42-
grantDBOSSchemaPermissions(out);
41+
MigrationManager.runMigrations(
42+
dbOptions.url(), dbOptions.user(), dbOptions.password(), dbOptions.schema());
43+
grantDBOSSchemaPermissions(out, dbOptions.schema());
4344
return 0;
4445
}
4546

46-
void grantDBOSSchemaPermissions(PrintWriter out) throws SQLException {
47-
final var schema = "dbos";
47+
void grantDBOSSchemaPermissions(PrintWriter out, String schema) throws SQLException {
4848

4949
if (appRole == null || appRole.isEmpty()) {
5050
return;

transact-cli/src/test/java/dev/dbos/transact/cli/MigrateCommandTest.java

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.junit.jupiter.api.Assertions.assertFalse;
55
import static org.junit.jupiter.api.Assertions.assertTrue;
66

7+
import dev.dbos.transact.Constants;
78
import dev.dbos.transact.migrations.MigrationManager;
89

910
import java.io.PrintWriter;
@@ -13,24 +14,20 @@
1314
import java.util.Objects;
1415
import java.util.concurrent.TimeUnit;
1516

16-
import org.junit.jupiter.api.BeforeAll;
17-
import org.junit.jupiter.api.MethodOrderer;
18-
import org.junit.jupiter.api.Order;
17+
import org.junit.jupiter.api.BeforeEach;
1918
import org.junit.jupiter.api.Test;
20-
import org.junit.jupiter.api.TestMethodOrder;
2119
import org.junit.jupiter.api.Timeout;
2220
import picocli.CommandLine;
2321

2422
@Timeout(value = 2, unit = TimeUnit.MINUTES)
25-
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
2623
public class MigrateCommandTest {
2724

2825
static String db_url = "jdbc:postgresql://localhost:5432/migrate_cmd_test";
2926
static String db_user = Objects.requireNonNullElse(System.getenv("PGUSER"), "postgres");
3027
static String db_password = Objects.requireNonNullElse(System.getenv("PGPASSWORD"), "dbos");
3128

32-
@BeforeAll
33-
static void setup() throws Exception {
29+
@BeforeEach
30+
public void setup() throws Exception {
3431
var pair = MigrationManager.extractDbAndPostgresUrl(db_url);
3532
var dropDbSql = String.format("DROP DATABASE IF EXISTS %s WITH (FORCE)", pair.database());
3633
try (var conn = DriverManager.getConnection(pair.url(), db_user, db_password);
@@ -40,10 +37,9 @@ static void setup() throws Exception {
4037
}
4138

4239
@Test
43-
@Order(1)
4440
public void migrate() throws Exception {
4541

46-
assertFalse(checkConnection(db_url, db_user, db_password));
42+
assertFalse(checkConnection());
4743

4844
var cmd = new CommandLine(new DBOSCommand());
4945
var sw = new StringWriter();
@@ -52,14 +48,16 @@ public void migrate() throws Exception {
5248
var exitCode = cmd.execute("migrate", "-D=" + db_url, "-U=" + db_user);
5349
assertEquals(0, exitCode);
5450

55-
assertTrue(checkConnection(db_url, db_user, db_password));
51+
assertTrue(checkConnection());
52+
assertTrue(checkTable(Constants.DB_SCHEMA, "workflow_status"));
5653
}
5754

5855
@Test
59-
@Order(2)
60-
public void migrate_again() throws Exception {
56+
public void migrate_twice() throws Exception {
6157

62-
assertTrue(checkConnection(db_url, db_user, db_password));
58+
migrate();
59+
60+
assertTrue(checkConnection());
6361

6462
var app = new DBOSCommand();
6563
var cmd = new CommandLine(app);
@@ -70,16 +68,52 @@ public void migrate_again() throws Exception {
7068
var exitCode = cmd.execute("migrate", "-D=" + db_url, "-U=" + db_user);
7169
assertEquals(0, exitCode);
7270

73-
assertTrue(checkConnection(db_url, db_user, db_password));
71+
assertTrue(checkConnection());
72+
assertTrue(checkTable(Constants.DB_SCHEMA, "workflow_status"));
73+
}
74+
75+
@Test
76+
public void migrate_custom_schema() throws Exception {
77+
78+
assertFalse(checkConnection());
79+
80+
var schema = "C\"$+0m'";
81+
82+
var cmd = new CommandLine(new DBOSCommand());
83+
var sw = new StringWriter();
84+
cmd.setOut(new PrintWriter(sw));
85+
86+
var exitCode = cmd.execute("migrate", "-D=" + db_url, "-U=" + db_user, "--schema=" + schema);
87+
assertEquals(0, exitCode);
88+
89+
assertTrue(checkConnection());
90+
assertTrue(checkTable(schema, "workflow_status"));
7491
}
7592

76-
static boolean checkConnection(String url, String user, String password) {
77-
try (var conn = DriverManager.getConnection(url, user, password);
93+
static boolean checkConnection() {
94+
try (var conn = DriverManager.getConnection(db_url, db_user, db_password);
7895
var stmt = conn.createStatement()) {
7996
stmt.execute("SELECT 1");
8097
return true;
8198
} catch (SQLException e) {
8299
return false;
83100
}
84101
}
102+
103+
static boolean checkTable(String schema, String table) throws SQLException {
104+
var sql =
105+
"SELECT EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = ? AND table_name = ?)";
106+
try (var conn = DriverManager.getConnection(db_url, db_user, db_password);
107+
var stmt = conn.prepareStatement(sql)) {
108+
stmt.setString(1, schema);
109+
stmt.setString(2, table);
110+
try (var rs = stmt.executeQuery()) {
111+
if (rs.next()) {
112+
return rs.getBoolean("exists");
113+
} else {
114+
return false;
115+
}
116+
}
117+
}
118+
}
85119
}

transact/src/main/java/dev/dbos/transact/DBOSClient.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,20 @@ public WorkflowStatus getStatus() {
5959
* @param password System database credential / password
6060
*/
6161
public DBOSClient(String url, String user, String password) {
62+
this(url, user, password, null);
63+
}
64+
65+
/**
66+
* Construct a DBOSClient, by providing system database access credentials
67+
*
68+
* @param url System database JDBC URL
69+
* @param user System database user
70+
* @param password System database credential / password
71+
* @param schema Database schema for DBOS tables
72+
*/
73+
public DBOSClient(String url, String user, String password, String schema) {
6274
var dataSource = SystemDatabase.createDataSource(url, user, password, 0, 0);
63-
systemDatabase = new SystemDatabase(dataSource);
75+
systemDatabase = new SystemDatabase(dataSource, schema);
6476
}
6577

6678
/**
@@ -69,7 +81,17 @@ public DBOSClient(String url, String user, String password) {
6981
* @param dataSource System database data source
7082
*/
7183
public DBOSClient(HikariDataSource dataSource) {
72-
systemDatabase = new SystemDatabase(dataSource);
84+
this(dataSource, null);
85+
}
86+
87+
/**
88+
* Construct a DBOSClient, by providing a configured data source
89+
*
90+
* @param dataSource System database data source
91+
* @param schema Database schema for DBOS tables
92+
*/
93+
public DBOSClient(HikariDataSource dataSource, String schema) {
94+
systemDatabase = new SystemDatabase(dataSource, schema);
7395
}
7496

7597
@Override

0 commit comments

Comments
 (0)