Да вроде всё работает и тесты проходят.

И блоки добавляются все что надо для MVP
This commit is contained in:
AidarKC
2026-01-22 01:57:02 +03:00
parent 97840a45d6
commit 3f5f94a53f
5 changed files with 566 additions and 310 deletions
@@ -14,17 +14,17 @@ import java.sql.Statement;
/**
* DatabaseInitializer — создание новой SQLite-БД по схеме SHiNE.
*
* Таблицы:
* - solana_users
* - active_sessions
* - users_params
* - ip_geo_cache
* - blockchain_state
* - blocks
* - connections_state
* - message_stats
* В этой версии:
* - создаём ТОЛЬКО таблицы/индексы
* - в конце вызываем DatabaseTriggersInstaller.createAllTriggers(st)
*
* Зачем так:
* - триггеры часто ломают совместимость с внешними SQLite-просмотрщиками/сборками
* - проще поддерживать/мигрировать
*/
public class DatabaseInitializer {
public final class DatabaseInitializer {
private DatabaseInitializer() {}
/* ===================== TEXT (msg_type=1) ===================== */
@@ -46,7 +46,6 @@ public class DatabaseInitializer {
public static final short REACTION_LIKE = 1;
/* ===================== CONNECTION (msg_type=3) ===================== */
// Приведено к твоему shine.db.MsgSubType:
// FRIEND=10/11, CONTACT=20/21, FOLLOW=30/31
public static final short CONNECTION_FRIEND = 10;
public static final short CONNECTION_UNFRIEND = 11;
@@ -264,138 +263,6 @@ public class DatabaseInitializer {
ON blocks (bch_name, line_code, this_line_number);
""");
// 6.1) TRIGGER: проверка целостности линии (только если line-поля реально переданы)
/* пока просто отключил этот тригер
st.executeUpdate("""
CREATE TRIGGER IF NOT EXISTS trg_blocks_line_integrity_bi
BEFORE INSERT ON blocks
WHEN
NEW.line_code IS NOT NULL
OR NEW.prev_line_number IS NOT NULL
OR NEW.prev_line_hash IS NOT NULL
OR NEW.this_line_number IS NOT NULL
BEGIN
-- ============================================================
-- LINE-INTEGRITY (BodyHasLine)
--
-- Этот триггер срабатывает ТОЛЬКО если при вставке передали хотя бы одно line-поле.
--
-- Типы, которые МОГУТ быть линейными (BodyHasLine в коде проекта):
-- - TECH (msg_type=0): CreateChannelBody (и т.п. тех-блоки с линией)
-- - TEXT (msg_type=1): TextBody в режиме линии (пост/редактирование поста в канале)
-- - CONNECTION (msg_type=3): ConnectionBody
-- - USER_PARAM (msg_type=4): UserParamBody
--
-- Проверки:
-- 1) Если передали line-поля -> обязаны передать ВСЕ 4:
-- line_code, prev_line_number, prev_line_hash, this_line_number.
-- 2) prev блок линии существует и p.block_hash == NEW.prev_line_hash
-- 3) line_code корректный:
-- - либо NEW.prev_line_number == NEW.line_code (первый шаг после root),
-- - либо у prev блока p.line_code == NEW.line_code
-- 4) this_line_number корректный:
-- - первый шаг после root:
-- TEXT: this=0
-- TECH/CONNECTION/USER_PARAM: this=1
-- - дальше:
-- TEXT: допускаем this = prev.this или prev.this + 1
-- TECH/CONNECTION/USER_PARAM: строго this = prev.this + 1
--
-- Ошибки: RAISE(ABORT, 'LINE_ERR_...') — чтобы Java могла понять причину.
-- ============================================================
-- 0) line-поля нельзя у неожиданных типов
SELECT RAISE(ABORT,
'LINE_ERR_UNSUPPORTED_TYPE_WITH_LINE: msg_type=' || NEW.msg_type || ' msg_sub_type=' || NEW.msg_sub_type
)
WHERE NOT (NEW.msg_type IN (0, 1, 3, 4));
-- 1) line-поля должны быть заполнены полностью (без “частично”)
SELECT RAISE(ABORT,
'LINE_ERR_PARTIAL_FIELDS: all of (line_code, prev_line_number, prev_line_hash, this_line_number) must be NOT NULL'
)
WHERE NEW.line_code IS NULL
OR NEW.prev_line_number IS NULL
OR NEW.prev_line_hash IS NULL
OR NEW.this_line_number IS NULL;
-- 2) prev существует?
SELECT RAISE(ABORT,
'LINE_ERR_NO_PREV: bch=' || NEW.bch_name || ' block=' || NEW.block_number || ' prev=' || NEW.prev_line_number
)
WHERE NOT EXISTS(
SELECT 1
FROM blocks p
WHERE p.bch_name = NEW.bch_name
AND p.block_number = NEW.prev_line_number
LIMIT 1
);
-- 3) prev hash совпадает?
SELECT RAISE(ABORT,
'LINE_ERR_PREV_HASH_MISMATCH: bch=' || NEW.bch_name || ' block=' || NEW.block_number || ' prev=' || NEW.prev_line_number
)
WHERE NOT EXISTS(
SELECT 1
FROM blocks p
WHERE p.bch_name = NEW.bch_name
AND p.block_number = NEW.prev_line_number
AND p.block_hash = NEW.prev_line_hash
LIMIT 1
);
-- 4) line_code корректный:
-- либо это первый шаг после root (prev_line_number == line_code),
-- либо prev уже в этой линии (p.line_code == NEW.line_code).
SELECT RAISE(ABORT,
'LINE_ERR_LINE_CODE_MISMATCH: bch=' || NEW.bch_name || ' block=' || NEW.block_number ||
' line_code=' || NEW.line_code || ' prev=' || NEW.prev_line_number
)
WHERE NEW.prev_line_number <> NEW.line_code
AND NOT EXISTS(
SELECT 1
FROM blocks p
WHERE p.bch_name = NEW.bch_name
AND p.block_number = NEW.prev_line_number
AND p.line_code = NEW.line_code
LIMIT 1
);
-- 5) первый шаг после root: this_line_number
SELECT RAISE(ABORT,
'LINE_ERR_FIRST_STEP_BAD_THIS: expected this_line_number=0 for TEXT or =1 for other types'
)
WHERE NEW.prev_line_number = NEW.line_code
AND NEW.this_line_number <> (CASE WHEN NEW.msg_type = 1 THEN 0 ELSE 1 END);
-- 6) обычный шаг: this_line_number относительно prev
SELECT RAISE(ABORT,
'LINE_ERR_THIS_LINE_BAD_STEP: bch=' || NEW.bch_name || ' block=' || NEW.block_number ||
' this=' || NEW.this_line_number || ' prev=' || NEW.prev_line_number
)
WHERE NEW.prev_line_number <> NEW.line_code
AND NOT EXISTS(
SELECT 1
FROM blocks p
WHERE p.bch_name = NEW.bch_name
AND p.block_number = NEW.prev_line_number
AND p.this_line_number IS NOT NULL
AND (
-- TEXT: допускаем same или +1 (поддерживает “edit не увеличивает thisLineNumber”)
(NEW.msg_type = 1 AND
(NEW.this_line_number = p.this_line_number OR NEW.this_line_number = p.this_line_number + 1)
)
OR
-- TECH/CONNECTION/USER_PARAM: строго +1
(NEW.msg_type IN (0,3,4) AND
NEW.this_line_number = p.this_line_number + 1
)
)
LIMIT 1
);
END;
""");
*/
// 7) connections_state
st.executeUpdate("""
CREATE TABLE IF NOT EXISTS connections_state (
@@ -427,59 +294,7 @@ public class DatabaseInitializer {
ON connections_state (login, to_login);
""");
// 8) Trigger: connection state
st.executeUpdate("""
CREATE TRIGGER IF NOT EXISTS trg_blocks_connection_state_ai
AFTER INSERT ON blocks
WHEN NEW.msg_type = 3
BEGIN
INSERT INTO connections_state (
login, rel_type, to_login, to_bch_name, to_block_number, to_block_hash
)
SELECT
NEW.login,
NEW.msg_sub_type,
NEW.to_login,
NEW.to_bch_name,
NEW.to_block_number,
NEW.to_block_hash
WHERE NEW.msg_sub_type IN (%d, %d, %d)
AND NEW.to_login IS NOT NULL
AND NEW.to_bch_name IS NOT NULL
ON CONFLICT(login, rel_type, to_login)
DO UPDATE SET
to_bch_name = excluded.to_bch_name,
to_block_number = excluded.to_block_number,
to_block_hash = excluded.to_block_hash;
DELETE FROM connections_state
WHERE login = NEW.login
AND to_login = NEW.to_login
AND rel_type = CASE NEW.msg_sub_type
WHEN %d THEN %d
WHEN %d THEN %d
WHEN %d THEN %d
ELSE rel_type
END
AND NEW.msg_sub_type IN (%d, %d, %d);
END;
""".formatted(
(int) CONNECTION_FRIEND,
(int) CONNECTION_CONTACT,
(int) CONNECTION_FOLLOW,
(int) CONNECTION_UNFRIEND, (int) CONNECTION_FRIEND,
(int) CONNECTION_UNCONTACT, (int) CONNECTION_CONTACT,
(int) CONNECTION_UNFOLLOW, (int) CONNECTION_FOLLOW,
(int) CONNECTION_UNFRIEND,
(int) CONNECTION_UNCONTACT,
(int) CONNECTION_UNFOLLOW
));
// 9) message_stats
// 8) message_stats
st.executeUpdate("""
CREATE TABLE IF NOT EXISTS message_stats (
to_login TEXT NOT NULL,
@@ -510,110 +325,8 @@ public class DatabaseInitializer {
ON message_stats (to_login);
""");
// 10) Trigger: LIKE
st.executeUpdate("""
CREATE TRIGGER IF NOT EXISTS trg_blocks_message_stats_like_ai
AFTER INSERT ON blocks
WHEN NEW.msg_type = 2 AND NEW.msg_sub_type = %d
BEGIN
INSERT INTO message_stats (
to_login,
to_bch_name,
to_block_number,
to_block_hash,
likes_count,
replies_count,
edits_count
)
SELECT
NEW.to_login,
NEW.to_bch_name,
NEW.to_block_number,
NEW.to_block_hash,
1,
0,
0
WHERE NEW.to_login IS NOT NULL
AND NEW.to_bch_name IS NOT NULL
AND NEW.to_block_number IS NOT NULL
AND NEW.to_block_hash IS NOT NULL
ON CONFLICT(to_login, to_bch_name, to_block_number, to_block_hash)
DO UPDATE SET
likes_count = message_stats.likes_count + 1;
END;
""".formatted((int) REACTION_LIKE));
// 11) Trigger: REPLY
st.executeUpdate("""
CREATE TRIGGER IF NOT EXISTS trg_blocks_message_stats_reply_ai
AFTER INSERT ON blocks
WHEN NEW.msg_type = 1 AND NEW.msg_sub_type = %d
BEGIN
INSERT INTO message_stats (
to_login,
to_bch_name,
to_block_number,
to_block_hash,
likes_count,
replies_count,
edits_count
)
SELECT
NEW.to_login,
NEW.to_bch_name,
NEW.to_block_number,
NEW.to_block_hash,
0,
1,
0
WHERE NEW.to_login IS NOT NULL
AND NEW.to_bch_name IS NOT NULL
AND NEW.to_block_number IS NOT NULL
AND NEW.to_block_hash IS NOT NULL
ON CONFLICT(to_login, to_bch_name, to_block_number, to_block_hash)
DO UPDATE SET
replies_count = message_stats.replies_count + 1;
END;
""".formatted((int) TEXT_REPLY));
// 12) Trigger: EDIT
st.executeUpdate("""
CREATE TRIGGER IF NOT EXISTS trg_blocks_edit_apply_ai
AFTER INSERT ON blocks
WHEN NEW.msg_type = 1 AND NEW.msg_sub_type = %d
BEGIN
UPDATE blocks
SET edited_by_block_number = NEW.block_number
WHERE login = NEW.login
AND bch_name = NEW.bch_name
AND block_number = NEW.to_block_number;
INSERT INTO message_stats (
to_login,
to_bch_name,
to_block_number,
to_block_hash,
likes_count,
replies_count,
edits_count
)
SELECT
NEW.to_login,
NEW.to_bch_name,
NEW.to_block_number,
NEW.to_block_hash,
0,
0,
1
WHERE NEW.to_login IS NOT NULL
AND NEW.to_bch_name IS NOT NULL
AND NEW.to_block_number IS NOT NULL
AND NEW.to_block_hash IS NOT NULL
ON CONFLICT(to_login, to_bch_name, to_block_number, to_block_hash)
DO UPDATE SET
edits_count = message_stats.edits_count + 1;
END;
""".formatted((int) TEXT_EDIT));
// ВАЖНО: триггеры ставим отдельно
DatabaseTriggersInstaller.createAllTriggers(st);
}
}
}