SHA256
Каналы: типы 0/1/100/200, CreateChannel v3, state для chat200, новые API и деплой на prod
This commit is contained in:
@@ -397,11 +397,13 @@ public final class DatabaseInitializer {
|
||||
// 9) channel_names_state (global normalized channel names)
|
||||
st.executeUpdate("""
|
||||
CREATE TABLE IF NOT EXISTS channel_names_state (
|
||||
slug TEXT NOT NULL PRIMARY KEY,
|
||||
slug TEXT NOT NULL,
|
||||
display_name TEXT NOT NULL,
|
||||
channel_description TEXT NOT NULL DEFAULT '',
|
||||
owner_login TEXT NOT NULL,
|
||||
owner_bch_name TEXT NOT NULL,
|
||||
channel_type_code INTEGER NOT NULL DEFAULT 1,
|
||||
channel_type_version INTEGER NOT NULL DEFAULT 1,
|
||||
channel_root_block_number INTEGER NOT NULL,
|
||||
channel_root_block_hash BLOB NOT NULL,
|
||||
created_at_ms INTEGER NOT NULL
|
||||
@@ -409,8 +411,8 @@ public final class DatabaseInitializer {
|
||||
""");
|
||||
|
||||
st.executeUpdate("""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_channel_names_state_slug
|
||||
ON channel_names_state (slug);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_channel_names_state_owner_type_slug
|
||||
ON channel_names_state (owner_bch_name, channel_type_code, slug);
|
||||
""");
|
||||
|
||||
st.executeUpdate("""
|
||||
@@ -423,6 +425,48 @@ public final class DatabaseInitializer {
|
||||
ON channel_names_state (owner_login, owner_bch_name);
|
||||
""");
|
||||
|
||||
// 9.1) chat200_state
|
||||
st.executeUpdate("""
|
||||
CREATE TABLE IF NOT EXISTS chat200_state (
|
||||
owner_login TEXT NOT NULL,
|
||||
owner_bch_name TEXT NOT NULL,
|
||||
channel_root_block_number INTEGER NOT NULL,
|
||||
channel_root_block_hash BLOB NOT NULL,
|
||||
channel_name TEXT NOT NULL,
|
||||
channel_type_version INTEGER NOT NULL,
|
||||
chat_title TEXT NOT NULL DEFAULT '',
|
||||
updated_at_ms INTEGER NOT NULL,
|
||||
PRIMARY KEY (owner_bch_name, channel_root_block_number)
|
||||
);
|
||||
""");
|
||||
st.executeUpdate("""
|
||||
CREATE INDEX IF NOT EXISTS idx_chat200_state_owner
|
||||
ON chat200_state (owner_login, owner_bch_name);
|
||||
""");
|
||||
|
||||
// 9.2) chat200_members_state
|
||||
st.executeUpdate("""
|
||||
CREATE TABLE IF NOT EXISTS chat200_members_state (
|
||||
owner_bch_name TEXT NOT NULL,
|
||||
channel_root_block_number INTEGER NOT NULL,
|
||||
member_login TEXT NOT NULL,
|
||||
member_channel_name TEXT NOT NULL,
|
||||
is_active INTEGER NOT NULL,
|
||||
updated_at_ms INTEGER NOT NULL,
|
||||
updated_by_block_number INTEGER NOT NULL,
|
||||
PRIMARY KEY (
|
||||
owner_bch_name,
|
||||
channel_root_block_number,
|
||||
member_login,
|
||||
member_channel_name
|
||||
)
|
||||
);
|
||||
""");
|
||||
st.executeUpdate("""
|
||||
CREATE INDEX IF NOT EXISTS idx_chat200_members_owner
|
||||
ON chat200_members_state (owner_bch_name, channel_root_block_number, is_active);
|
||||
""");
|
||||
|
||||
// 10) direct_messages
|
||||
st.executeUpdate("""
|
||||
CREATE TABLE IF NOT EXISTS direct_messages (
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.sql.Statement;
|
||||
public final class SqliteDbController {
|
||||
|
||||
private static volatile SqliteDbController instance;
|
||||
private static final int LATEST_SCHEMA_VERSION = 2;
|
||||
private static final int LATEST_SCHEMA_VERSION = 3;
|
||||
|
||||
private final String jdbcUrl;
|
||||
|
||||
@@ -85,6 +85,7 @@ public final class SqliteDbController {
|
||||
switch (targetVersion) {
|
||||
case 1 -> migrateToV1();
|
||||
case 2 -> migrateToV2();
|
||||
case 3 -> migrateToV3();
|
||||
default -> throw new RuntimeException("Unknown DB migration target version: " + targetVersion);
|
||||
}
|
||||
}
|
||||
@@ -107,6 +108,7 @@ public final class SqliteDbController {
|
||||
}
|
||||
|
||||
ensureChannelNamesDescriptionColumn(c, st);
|
||||
ensureChannelNamesTypeColumns(c, st);
|
||||
ensureSignedMessageReceiptUniq(c, st);
|
||||
DatabaseTriggersInstaller.createAllTriggers(st);
|
||||
setSchemaVersion(c, 1);
|
||||
@@ -147,6 +149,69 @@ public final class SqliteDbController {
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateToV3() {
|
||||
try (Connection c = DriverManager.getConnection(jdbcUrl);
|
||||
Statement st = c.createStatement()) {
|
||||
c.setAutoCommit(false);
|
||||
try {
|
||||
ensureChat200StateTables(st);
|
||||
setSchemaVersion(c, 3);
|
||||
c.commit();
|
||||
} catch (Exception e) {
|
||||
try { c.rollback(); } catch (Exception ignored) {}
|
||||
throw new RuntimeException("DB migration to v3 failed", e);
|
||||
} finally {
|
||||
try { c.setAutoCommit(true); } catch (Exception ignored) {}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("DB migration to v3 failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureChat200StateTables(Statement st) throws SQLException {
|
||||
st.executeUpdate("""
|
||||
CREATE TABLE IF NOT EXISTS chat200_state (
|
||||
owner_login TEXT NOT NULL,
|
||||
owner_bch_name TEXT NOT NULL,
|
||||
channel_root_block_number INTEGER NOT NULL,
|
||||
channel_root_block_hash BLOB NOT NULL,
|
||||
channel_name TEXT NOT NULL,
|
||||
channel_type_version INTEGER NOT NULL,
|
||||
chat_title TEXT NOT NULL DEFAULT '',
|
||||
updated_at_ms INTEGER NOT NULL,
|
||||
PRIMARY KEY (owner_bch_name, channel_root_block_number)
|
||||
);
|
||||
""");
|
||||
|
||||
st.executeUpdate("""
|
||||
CREATE TABLE IF NOT EXISTS chat200_members_state (
|
||||
owner_bch_name TEXT NOT NULL,
|
||||
channel_root_block_number INTEGER NOT NULL,
|
||||
member_login TEXT NOT NULL,
|
||||
member_channel_name TEXT NOT NULL,
|
||||
is_active INTEGER NOT NULL,
|
||||
updated_at_ms INTEGER NOT NULL,
|
||||
updated_by_block_number INTEGER NOT NULL,
|
||||
PRIMARY KEY (
|
||||
owner_bch_name,
|
||||
channel_root_block_number,
|
||||
member_login,
|
||||
member_channel_name
|
||||
)
|
||||
);
|
||||
""");
|
||||
|
||||
st.executeUpdate("""
|
||||
CREATE INDEX IF NOT EXISTS idx_chat200_state_owner
|
||||
ON chat200_state (owner_login, owner_bch_name);
|
||||
""");
|
||||
|
||||
st.executeUpdate("""
|
||||
CREATE INDEX IF NOT EXISTS idx_chat200_members_owner
|
||||
ON chat200_members_state (owner_bch_name, channel_root_block_number, is_active);
|
||||
""");
|
||||
}
|
||||
|
||||
private int getCurrentSchemaVersion() {
|
||||
try (Connection c = DriverManager.getConnection(jdbcUrl)) {
|
||||
if (!tableExists(c, DatabaseInitializer.DB_SCHEMA_VERSION_TABLE)) {
|
||||
@@ -256,11 +321,13 @@ public final class SqliteDbController {
|
||||
private static void ensureChannelNamesStateTable(Statement st) throws SQLException {
|
||||
st.executeUpdate("""
|
||||
CREATE TABLE IF NOT EXISTS channel_names_state (
|
||||
slug TEXT NOT NULL PRIMARY KEY,
|
||||
slug TEXT NOT NULL,
|
||||
display_name TEXT NOT NULL,
|
||||
channel_description TEXT NOT NULL DEFAULT '',
|
||||
owner_login TEXT NOT NULL,
|
||||
owner_bch_name TEXT NOT NULL,
|
||||
channel_type_code INTEGER NOT NULL DEFAULT 1,
|
||||
channel_type_version INTEGER NOT NULL DEFAULT 1,
|
||||
channel_root_block_number INTEGER NOT NULL,
|
||||
channel_root_block_hash BLOB NOT NULL,
|
||||
created_at_ms INTEGER NOT NULL
|
||||
@@ -286,10 +353,33 @@ public final class SqliteDbController {
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureChannelNamesTypeColumns(Connection c, Statement st) throws SQLException {
|
||||
boolean hasTypeCode = false;
|
||||
boolean hasTypeVersion = false;
|
||||
|
||||
try (Statement probe = c.createStatement();
|
||||
ResultSet rs = probe.executeQuery("PRAGMA table_info(channel_names_state)")) {
|
||||
while (rs.next()) {
|
||||
String name = rs.getString("name");
|
||||
if ("channel_type_code".equalsIgnoreCase(name)) hasTypeCode = true;
|
||||
if ("channel_type_version".equalsIgnoreCase(name)) hasTypeVersion = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasTypeCode) {
|
||||
st.executeUpdate("ALTER TABLE channel_names_state ADD COLUMN channel_type_code INTEGER NOT NULL DEFAULT 1");
|
||||
}
|
||||
if (!hasTypeVersion) {
|
||||
st.executeUpdate("ALTER TABLE channel_names_state ADD COLUMN channel_type_version INTEGER NOT NULL DEFAULT 1");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureChannelNamesIndexes(Statement st) throws SQLException {
|
||||
st.executeUpdate("DROP INDEX IF EXISTS uq_channel_names_state_slug");
|
||||
st.executeUpdate("DROP INDEX IF EXISTS uq_channel_names_state_owner_slug");
|
||||
st.executeUpdate("""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_channel_names_state_slug
|
||||
ON channel_names_state (slug);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_channel_names_state_owner_type_slug
|
||||
ON channel_names_state (owner_bch_name, channel_type_code, slug);
|
||||
""");
|
||||
st.executeUpdate("""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_channel_names_state_target
|
||||
|
||||
@@ -24,19 +24,26 @@ public final class ChannelNameStateDAO {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public boolean existsBySlug(Connection c, String slug) throws SQLException {
|
||||
String sql = "SELECT 1 FROM channel_names_state WHERE slug = ? LIMIT 1";
|
||||
public boolean existsByOwnerTypeAndSlug(Connection c, String ownerBchName, int channelTypeCode, String slug) throws SQLException {
|
||||
String sql = """
|
||||
SELECT 1
|
||||
FROM channel_names_state
|
||||
WHERE owner_bch_name = ? AND channel_type_code = ? AND slug = ?
|
||||
LIMIT 1
|
||||
""";
|
||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||
ps.setString(1, slug);
|
||||
ps.setString(1, ownerBchName);
|
||||
ps.setInt(2, channelTypeCode);
|
||||
ps.setString(3, slug);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
return rs.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean existsBySlug(String slug) throws SQLException {
|
||||
public boolean existsByOwnerTypeAndSlug(String ownerBchName, int channelTypeCode, String slug) throws SQLException {
|
||||
try (Connection c = db.getConnection()) {
|
||||
return existsBySlug(c, slug);
|
||||
return existsByOwnerTypeAndSlug(c, ownerBchName, channelTypeCode, slug);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +61,12 @@ public final class ChannelNameStateDAO {
|
||||
channel_description,
|
||||
owner_login,
|
||||
owner_bch_name,
|
||||
channel_type_code,
|
||||
channel_type_version,
|
||||
channel_root_block_number,
|
||||
channel_root_block_hash,
|
||||
created_at_ms
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||
ps.setString(1, entry.getSlug());
|
||||
@@ -65,9 +74,11 @@ public final class ChannelNameStateDAO {
|
||||
ps.setString(3, entry.getChannelDescription() == null ? "" : entry.getChannelDescription());
|
||||
ps.setString(4, entry.getOwnerLogin());
|
||||
ps.setString(5, entry.getOwnerBlockchainName());
|
||||
ps.setInt(6, entry.getChannelRootBlockNumber());
|
||||
ps.setBytes(7, entry.getChannelRootBlockHash());
|
||||
ps.setLong(8, entry.getCreatedAtMs());
|
||||
ps.setInt(6, entry.getChannelTypeCode());
|
||||
ps.setInt(7, entry.getChannelTypeVersion());
|
||||
ps.setInt(8, entry.getChannelRootBlockNumber());
|
||||
ps.setBytes(9, entry.getChannelRootBlockHash());
|
||||
ps.setLong(10, entry.getCreatedAtMs());
|
||||
ps.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ public class ChannelNameStateEntry {
|
||||
private String channelDescription;
|
||||
private String ownerLogin;
|
||||
private String ownerBlockchainName;
|
||||
private int channelTypeCode;
|
||||
private int channelTypeVersion;
|
||||
private int channelRootBlockNumber;
|
||||
private byte[] channelRootBlockHash;
|
||||
private long createdAtMs;
|
||||
@@ -52,6 +54,22 @@ public class ChannelNameStateEntry {
|
||||
this.ownerBlockchainName = ownerBlockchainName;
|
||||
}
|
||||
|
||||
public int getChannelTypeCode() {
|
||||
return channelTypeCode;
|
||||
}
|
||||
|
||||
public void setChannelTypeCode(int channelTypeCode) {
|
||||
this.channelTypeCode = channelTypeCode;
|
||||
}
|
||||
|
||||
public int getChannelTypeVersion() {
|
||||
return channelTypeVersion;
|
||||
}
|
||||
|
||||
public void setChannelTypeVersion(int channelTypeVersion) {
|
||||
this.channelTypeVersion = channelTypeVersion;
|
||||
}
|
||||
|
||||
public int getChannelRootBlockNumber() {
|
||||
return channelRootBlockNumber;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user