]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/Database.java
88d344211d8ab5bd7d1c4905b7e0bc4382711ab8
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / storage / Database.java
1 package org.asamk.signal.manager.storage;
2
3 import com.zaxxer.hikari.HikariConfig;
4 import com.zaxxer.hikari.HikariDataSource;
5
6 import org.slf4j.Logger;
7 import org.sqlite.SQLiteConfig;
8
9 import java.io.File;
10 import java.sql.Connection;
11 import java.sql.SQLException;
12 import java.util.function.Function;
13
14 public abstract class Database implements AutoCloseable {
15
16 private final Logger logger;
17 private final long databaseVersion;
18 private final HikariDataSource dataSource;
19
20 protected Database(final Logger logger, final long databaseVersion, final HikariDataSource dataSource) {
21 this.logger = logger;
22 this.databaseVersion = databaseVersion;
23 this.dataSource = dataSource;
24 }
25
26 public static <T extends Database> T initDatabase(
27 File databaseFile, Function<HikariDataSource, T> newDatabase
28 ) throws SQLException {
29 HikariDataSource dataSource = null;
30
31 try {
32 dataSource = getHikariDataSource(databaseFile.getAbsolutePath());
33
34 final var result = newDatabase.apply(dataSource);
35 result.initDb();
36 dataSource = null;
37 return result;
38 } finally {
39 if (dataSource != null) {
40 dataSource.close();
41 }
42 }
43 }
44
45 public final Connection getConnection() throws SQLException {
46 return dataSource.getConnection();
47 }
48
49 @Override
50 public void close() throws SQLException {
51 dataSource.close();
52 }
53
54 protected final void initDb() throws SQLException {
55 try (final var connection = dataSource.getConnection()) {
56 final var userVersion = getUserVersion(connection);
57 logger.trace("Current database version: {} Program database version: {}", userVersion, databaseVersion);
58
59 if (userVersion > databaseVersion) {
60 logger.error("Database has been updated by a newer signal-cli version");
61 throw new SQLException("Database has been updated by a newer signal-cli version");
62 } else if (userVersion < databaseVersion) {
63 upgradeDatabase(connection, userVersion);
64 setUserVersion(connection, databaseVersion);
65 }
66 }
67 }
68
69 protected abstract void upgradeDatabase(final Connection connection, long oldVersion) throws SQLException;
70
71 private static long getUserVersion(final Connection connection) throws SQLException {
72 try (final var statement = connection.createStatement()) {
73 final var resultSet = statement.executeQuery("PRAGMA user_version");
74 return resultSet.getLong(1);
75 }
76 }
77
78 private static void setUserVersion(final Connection connection, long userVersion) throws SQLException {
79 try (final var statement = connection.createStatement()) {
80 statement.executeUpdate("PRAGMA user_version = " + userVersion);
81 }
82 }
83
84 private static HikariDataSource getHikariDataSource(final String databaseFile) {
85 final var sqliteConfig = new SQLiteConfig();
86 sqliteConfig.setBusyTimeout(10_000);
87 sqliteConfig.setTransactionMode(SQLiteConfig.TransactionMode.IMMEDIATE);
88
89 HikariConfig config = new HikariConfig();
90 config.setJdbcUrl("jdbc:sqlite:" + databaseFile);
91 config.setDataSourceProperties(sqliteConfig.toProperties());
92 config.setMinimumIdle(1);
93 config.setConnectionInitSql("PRAGMA foreign_keys=ON");
94 return new HikariDataSource(config);
95 }
96 }