Merge branch 'master' into chat-relays

This commit is contained in:
spaced4ndy
2026-01-05 11:31:15 +04:00
76 changed files with 1119 additions and 935 deletions
+3 -188
View File
@@ -99,18 +99,10 @@ module Simplex.Chat.Store.Groups
deleteGroupMember,
deleteGroupMemberConnection,
updateGroupMemberRole,
createIntroductions,
createIntrosOrUpdateVectors,
setMemberVectorNewRelations,
setMembersVectorsNewRelation,
setMemberVectorRelationConnected,
migrateGetMemberRelationsVector,
migrateMemberRelationsVector,
migrateMemberRelationsVector',
getMemberRelationsVector_,
updateIntroStatus,
getIntroduction,
getIntroducedGroupMemberIds,
getMemberRelationsVector,
createIntroReMember,
createIntroToMemberContact,
getMatchingContacts,
@@ -151,8 +143,6 @@ module Simplex.Chat.Store.Groups
setGroupChatTTL,
getGroupChatTTL,
getUserGroupsToExpire,
hasMembersWithoutVector,
getGMsWithoutVectorIds,
updateGroupAlias,
)
where
@@ -166,7 +156,6 @@ import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Char (toLower)
import Data.Either (rights)
import Data.Foldable (foldrM)
import Data.Int (Int64)
import Data.List (partition, sortOn)
import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing)
@@ -1609,75 +1598,6 @@ updateGroupMemberRole :: DB.Connection -> User -> GroupMember -> GroupMemberRole
updateGroupMemberRole db User {userId} GroupMember {groupMemberId} memRole =
DB.execute db "UPDATE group_members SET member_role = ? WHERE user_id = ? AND group_member_id = ?" (memRole, userId, groupMemberId)
createIntroductions :: DB.Connection -> VersionChat -> [GroupMember] -> GroupMember -> IO [GroupMember]
createIntroductions db chatV reMembers toMember
| null reMembers = pure []
| otherwise = do
currentTs <- getCurrentTime
catMaybes <$> mapM (createIntro_ currentTs) reMembers
where
createIntro_ :: UTCTime -> GroupMember -> IO (Maybe GroupMember)
createIntro_ ts reMember =
-- when members connect concurrently, host would try to create introductions between them in both directions;
-- this check avoids creating second (redundant) introduction
checkInverseIntro >>= \case
Just _ -> pure Nothing
Nothing -> do
DB.execute
db
[sql|
INSERT INTO group_member_intros
(re_group_member_id, to_group_member_id, intro_status, intro_chat_protocol_version, created_at, updated_at)
VALUES (?,?,?,?,?,?)
|]
(groupMemberId' reMember, groupMemberId' toMember, GMIntroPending, chatV, ts, ts)
pure $ Just reMember
where
checkInverseIntro :: IO (Maybe Int64)
checkInverseIntro =
maybeFirstRow fromOnly $
DB.query
db
"SELECT 1 FROM group_member_intros WHERE re_group_member_id = ? AND to_group_member_id = ? LIMIT 1"
(groupMemberId' toMember, groupMemberId' reMember)
-- Create introductions for members without vectors and update vectors for members with vectors.
-- Partitioning and updates happen in same transaction to avoid race conditions.
createIntrosOrUpdateVectors :: DB.Connection -> VersionRangeChat -> [GroupMember] -> GroupMember -> IO [GroupMember]
createIntrosOrUpdateVectors db vr reMembers toMember
| null reMembers = pure []
| otherwise = do
(memsWithVec, memsWithoutVec) <- partitionByVector reMembers
let GroupMember {indexInGroup} = toMember
setMembersVectorsNewRelation db memsWithVec indexInGroup IDSubjectIntroduced MRIntroduced
memsWithoutVec' <- createIntroductions db (maxVersion vr) memsWithoutVec toMember
pure $ memsWithoutVec' <> memsWithVec
where
partitionByVector :: [GroupMember] -> IO ([GroupMember], [GroupMember])
#if defined(dbPostgres)
partitionByVector members = do
let memberIds = map groupMemberId' members
-- Lock rows first to ensure partitioning doesn't change in case of concurrent updates
_ :: [Only Int] <-
DB.query
db
"SELECT 1 FROM group_members WHERE group_member_id IN ? FOR UPDATE"
(Only $ In memberIds)
memberIdsWithVec <- S.fromList . map fromOnly <$>
DB.query
db
"SELECT group_member_id FROM group_members WHERE group_member_id IN ? AND member_relations_vector IS NOT NULL"
(Only $ In memberIds)
pure $ partition (\m -> groupMemberId' m `S.member` memberIdsWithVec) members
#else
partitionByVector = foldrM checkMember ([], [])
where
checkMember m (withVec, withoutVec) = do
hasVec <- isJust <$> maybeFirstRow fromOnly
(DB.query db "SELECT 1 FROM group_members WHERE group_member_id = ? AND member_relations_vector IS NOT NULL" (Only $ groupMemberId' m) :: IO [Only Int64])
pure $ if hasVec then (m : withVec, withoutVec) else (withVec, m : withoutVec)
#endif
setMemberVectorNewRelations :: DB.Connection -> GroupMember -> [(Int64, (IntroductionDirection, MemberRelation))] -> IO ()
setMemberVectorNewRelations db GroupMember {groupMemberId} relations = do
v_ <- maybeFirstRow fromOnly $
@@ -1742,100 +1662,14 @@ setMemberVectorRelationConnected db GroupMember {groupMemberId} GroupMember {ind
|]
(Binary v', currentTs, groupMemberId)
migrateGetMemberRelationsVector :: DB.Connection -> GroupMember -> ExceptT StoreError IO ByteString
migrateGetMemberRelationsVector db m@GroupMember {groupMemberId} = do
liftIO $ migrateMemberRelationsVector db m
getMemberRelationsVector :: DB.Connection -> GroupMember -> ExceptT StoreError IO ByteString
getMemberRelationsVector db GroupMember {groupMemberId} =
ExceptT . firstRow fromOnly (SEGroupMemberNotFound groupMemberId) $
DB.query
db
"SELECT member_relations_vector FROM group_members WHERE group_member_id = ?"
(Only groupMemberId)
migrateMemberRelationsVector :: DB.Connection -> GroupMember -> IO ()
migrateMemberRelationsVector db GroupMember {groupMemberId} =
migrateMemberRelationsVector' db groupMemberId
migrateMemberRelationsVector' :: DB.Connection -> GroupMemberId -> IO ()
migrateMemberRelationsVector' db groupMemberId = do
currentTs <- liftIO getCurrentTime
liftIO $ do
#if defined(dbPostgres)
-- Lock the row first to ensure computation runs only after lock is acquired
_ :: [Only Int] <-
DB.query
db
"SELECT 1 FROM group_members WHERE group_member_id = ? AND member_relations_vector IS NULL FOR UPDATE"
(Only groupMemberId)
#endif
DB.execute
db
[sql|
UPDATE group_members
SET
member_relations_vector = (
SELECT migrate_relations_vector(idx, direction, intro_status)
FROM (
SELECT m.index_in_group AS idx, 0 AS direction, i.intro_status
FROM group_member_intros i
JOIN group_members m ON m.group_member_id = i.to_group_member_id
WHERE i.re_group_member_id = group_members.group_member_id
UNION ALL
SELECT m.index_in_group AS idx, 1 AS direction, i.intro_status
FROM group_member_intros i
JOIN group_members m ON m.group_member_id = i.re_group_member_id
WHERE i.to_group_member_id = group_members.group_member_id
) AS relations
),
updated_at = ?
WHERE group_member_id = ?
AND member_relations_vector IS NULL
|]
(currentTs, groupMemberId)
getMemberRelationsVector_ :: DB.Connection -> GroupMember -> IO (Maybe ByteString)
getMemberRelationsVector_ db GroupMember {groupMemberId} =
maybeFirstRow fromOnly $
DB.query
db
"SELECT member_relations_vector FROM group_members WHERE group_member_id = ?"
(Only groupMemberId)
updateIntroStatus :: DB.Connection -> Int64 -> GroupMemberIntroStatus -> IO ()
updateIntroStatus db introId introStatus = do
currentTs <- getCurrentTime
DB.execute
db
[sql|
UPDATE group_member_intros
SET intro_status = ?, updated_at = ?
WHERE group_member_intro_id = ?
|]
(introStatus, currentTs, introId)
getIntroduction :: DB.Connection -> GroupMember -> GroupMember -> IO (Maybe GroupMemberIntro)
getIntroduction db reMember toMember =
maybeFirstRow toIntro $
DB.query
db
[sql|
SELECT group_member_intro_id, intro_status
FROM group_member_intros
WHERE re_group_member_id = ? AND to_group_member_id = ?
|]
(groupMemberId' reMember, groupMemberId' toMember)
where
toIntro :: (Int64, GroupMemberIntroStatus) -> GroupMemberIntro
toIntro (introId, introStatus) =
GroupMemberIntro {introId, reMember, toMember, introStatus}
getIntroducedGroupMemberIds :: DB.Connection -> GroupMember -> IO [GroupMemberId]
getIntroducedGroupMemberIds db invitee =
map fromOnly <$>
DB.query
db
"SELECT re_group_member_id FROM group_member_intros WHERE to_group_member_id = ?"
(Only $ groupMemberId' invitee)
createIntroReMember :: DB.Connection -> User -> GroupInfo -> GroupMember -> VersionChat -> MemberInfo -> Maybe MemberRestrictions -> (CommandId, ConnId) -> SubscriptionMode -> ExceptT StoreError IO GroupMember
createIntroReMember
db
@@ -2725,25 +2559,6 @@ getUserGroupsToExpire db User {userId} globalTTL =
where
cond = if globalTTL == 0 then "" else " OR chat_item_ttl IS NULL"
hasMembersWithoutVector :: DB.Connection -> IO Bool
hasMembersWithoutVector db =
fromOnly . head
<$> DB.query_
db
"SELECT EXISTS (SELECT 1 FROM group_members WHERE member_relations_vector IS NULL LIMIT 1)"
getGMsWithoutVectorIds :: DB.Connection -> IO [GroupMemberId]
getGMsWithoutVectorIds db =
map fromOnly <$>
DB.query_
db
[sql|
SELECT group_member_id
FROM group_members
WHERE member_relations_vector IS NULL
LIMIT 1000
|]
updateGroupAlias :: DB.Connection -> UserId -> GroupInfo -> LocalAlias -> IO GroupInfo
updateGroupAlias db userId g@GroupInfo {groupId} localAlias = do
updatedAt <- getCurrentTime
+8 -8
View File
@@ -335,24 +335,24 @@ updateSndMsgDeliveryStatus db connId agentMsgId sndMsgDeliveryStatus = do
|]
(sndMsgDeliveryStatus, currentTs, connId, agentMsgId)
createPendingGroupMessage :: DB.Connection -> Int64 -> MessageId -> Maybe Int64 -> IO ()
createPendingGroupMessage db groupMemberId messageId introId_ = do
createPendingGroupMessage :: DB.Connection -> Int64 -> MessageId -> IO ()
createPendingGroupMessage db groupMemberId messageId = do
currentTs <- getCurrentTime
DB.execute
db
[sql|
INSERT INTO pending_group_messages
(group_member_id, message_id, group_member_intro_id, created_at, updated_at) VALUES (?,?,?,?,?)
(group_member_id, message_id, created_at, updated_at) VALUES (?,?,?,?)
|]
(groupMemberId, messageId, introId_, currentTs, currentTs)
(groupMemberId, messageId, currentTs, currentTs)
getPendingGroupMessages :: DB.Connection -> Int64 -> IO [(SndMessage, ACMEventTag, Maybe Int64)]
getPendingGroupMessages :: DB.Connection -> Int64 -> IO [SndMessage]
getPendingGroupMessages db groupMemberId =
map pendingGroupMessage
<$> DB.query
db
[sql|
SELECT pgm.message_id, m.shared_msg_id, m.msg_body, m.chat_msg_event, pgm.group_member_intro_id
SELECT pgm.message_id, m.shared_msg_id, m.msg_body
FROM pending_group_messages pgm
JOIN messages m USING (message_id)
WHERE pgm.group_member_id = ?
@@ -360,8 +360,8 @@ getPendingGroupMessages db groupMemberId =
|]
(Only groupMemberId)
where
pendingGroupMessage (msgId, sharedMsgId, msgBody, cmEventTag, introId_) =
(SndMessage {msgId, sharedMsgId, msgBody}, cmEventTag, introId_)
pendingGroupMessage (msgId, sharedMsgId, msgBody) =
SndMessage {msgId, sharedMsgId, msgBody}
deletePendingGroupMessage :: DB.Connection -> Int64 -> MessageId -> IO ()
deletePendingGroupMessage db groupMemberId messageId =
@@ -22,7 +22,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20250922_remove_unused_connection
import Simplex.Chat.Store.Postgres.Migrations.M20251007_connections_sync
import Simplex.Chat.Store.Postgres.Migrations.M20251017_chat_tags_cascade
import Simplex.Chat.Store.Postgres.Migrations.M20251117_member_relations_vector
-- import Simplex.Chat.Store.Postgres.Migrations.M20251128_member_relations_vector_stage_2
import Simplex.Chat.Store.Postgres.Migrations.M20251128_migrate_member_relations
import Simplex.Chat.Store.Postgres.Migrations.M20251212_chat_relays
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
@@ -46,7 +46,7 @@ schemaMigrations =
("20251007_connections_sync", m20251007_connections_sync, Just down_m20251007_connections_sync),
("20251017_chat_tags_cascade", m20251017_chat_tags_cascade, Just down_m20251017_chat_tags_cascade),
("20251117_member_relations_vector", m20251117_member_relations_vector, Just down_m20251117_member_relations_vector),
-- ("20251128_member_relations_vector_stage_2", m20251128_member_relations_vector_stage_2, Just down_m20251128_member_relations_vector_stage_2)
("20251128_migrate_member_relations", m20251128_migrate_member_relations, Just down_m20251128_migrate_member_relations),
("20251212_chat_relays", m20251212_chat_relays, Just down_m20251212_chat_relays)
]
@@ -7,7 +7,7 @@ import qualified Data.Text as T
import Text.RawString.QQ (r)
-- This migration creates custom aggregate function migrate_relations_vector(idx, direction, intro_status).
-- Used in live migration and stage 2 migration (M20251128_member_relations_vector_stage_2).
-- Used in live migration and stage 2 migration (M20251128_migrate_member_relations).
--
-- Vector byte encoding: 4 reserved | 1 direction | 3 status
-- Direction: 0 = IDSubjectIntroduced, 1 = IDReferencedIntroduced
@@ -1,9 +1,9 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.Postgres.Migrations.M20251128_member_relations_vector_stage_2 where
module Simplex.Chat.Store.Postgres.Migrations.M20251128_migrate_member_relations where
import Data.Text (Text)
import qualified Data.Text as T
import Text.RawString.QQ (r)
-- Build member_relations_vector for all members that don't have it yet.
@@ -13,11 +13,9 @@ import Text.RawString.QQ (r)
-- - direction 0 (IDSubjectIntroduced): current member (subject) is re_group_member_id, was introduced to referenced member
-- - direction 1 (IDReferencedIntroduced): current member (subject) is to_group_member_id, referenced member was introduced to it
-- TODO [relations vector] drop group_member_intros in the end of migration
m20251128_member_relations_vector_stage_2 :: Text
m20251128_member_relations_vector_stage_2 =
T.pack
[r|
m20251128_migrate_member_relations :: Text
m20251128_migrate_member_relations =
[r|
UPDATE group_members
SET member_relations_vector = (
SELECT migrate_relations_vector(idx, direction, intro_status)
@@ -34,12 +32,14 @@ SET member_relations_vector = (
) AS relations
)
WHERE member_relations_vector IS NULL;
DROP INDEX idx_pending_group_messages_group_member_intro_id;
ALTER TABLE pending_group_messages DROP COLUMN group_member_intro_id;
|]
-- TODO [relations vector] re-create group_member_intros
down_m20251128_member_relations_vector_stage_2 :: Text
down_m20251128_member_relations_vector_stage_2 =
T.pack
[r|
down_m20251128_migrate_member_relations :: Text
down_m20251128_migrate_member_relations =
[r|
ALTER TABLE pending_group_messages ADD COLUMN group_member_intro_id BIGINT REFERENCES group_member_intros ON DELETE CASCADE;
CREATE INDEX idx_pending_group_messages_group_member_intro_id ON pending_group_messages(group_member_intro_id);
|]
@@ -1060,7 +1060,6 @@ CREATE TABLE test_chat_schema.pending_group_messages (
pending_group_message_id bigint NOT NULL,
group_member_id bigint NOT NULL,
message_id bigint NOT NULL,
group_member_intro_id bigint,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL
);
@@ -2321,10 +2320,6 @@ CREATE INDEX idx_pending_group_messages_group_member_id ON test_chat_schema.pend
CREATE INDEX idx_pending_group_messages_group_member_intro_id ON test_chat_schema.pending_group_messages USING btree (group_member_intro_id);
CREATE INDEX idx_pending_group_messages_message_id ON test_chat_schema.pending_group_messages USING btree (message_id);
@@ -2991,11 +2986,6 @@ ALTER TABLE ONLY test_chat_schema.pending_group_messages
ALTER TABLE ONLY test_chat_schema.pending_group_messages
ADD CONSTRAINT pending_group_messages_group_member_intro_id_fkey FOREIGN KEY (group_member_intro_id) REFERENCES test_chat_schema.group_member_intros(group_member_intro_id) ON DELETE CASCADE;
ALTER TABLE ONLY test_chat_schema.pending_group_messages
ADD CONSTRAINT pending_group_messages_message_id_fkey FOREIGN KEY (message_id) REFERENCES test_chat_schema.messages(message_id) ON DELETE CASCADE;
+2 -2
View File
@@ -145,7 +145,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20250922_remove_unused_connections
import Simplex.Chat.Store.SQLite.Migrations.M20251007_connections_sync
import Simplex.Chat.Store.SQLite.Migrations.M20251017_chat_tags_cascade
import Simplex.Chat.Store.SQLite.Migrations.M20251117_member_relations_vector
-- import Simplex.Chat.Store.SQLite.Migrations.M20251128_member_relations_vector_stage_2
import Simplex.Chat.Store.SQLite.Migrations.M20251128_migrate_member_relations
import Simplex.Chat.Store.SQLite.Migrations.M20251212_chat_relays
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
@@ -292,7 +292,7 @@ schemaMigrations =
("20251007_connections_sync", m20251007_connections_sync, Just down_m20251007_connections_sync),
("20251017_chat_tags_cascade", m20251017_chat_tags_cascade, Just down_m20251017_chat_tags_cascade),
("20251117_member_relations_vector", m20251117_member_relations_vector, Just down_m20251117_member_relations_vector),
-- ("20251128_member_relations_vector_stage_2", m20251128_member_relations_vector_stage_2, Just down_m20251128_member_relations_vector_stage_2)
("20251128_migrate_member_relations", m20251128_migrate_member_relations, Just down_m20251128_migrate_member_relations),
("20251212_chat_relays", m20251212_chat_relays, Just down_m20251212_chat_relays)
]
@@ -15,7 +15,7 @@ import Simplex.Messaging.Agent.Store.SQLite.Util (SQLiteFunc, SQLiteFuncFinal, m
-- This module defines custom aggregate function migrate_relations_vector(idx, direction, intro_status).
-- It is passed via DBOpts and registered on DB open.
-- Used in live migration and stage 2 migration (M20251128_member_relations_vector_stage_2).
-- Used in live migration and stage 2 migration (M20251128_migrate_member_relations).
--
-- Vector byte encoding: 4 reserved | 1 direction | 3 status
-- Direction: 0 = IDSubjectIntroduced, 1 = IDReferencedIntroduced
@@ -1,6 +1,6 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.SQLite.Migrations.M20251128_member_relations_vector_stage_2 where
module Simplex.Chat.Store.SQLite.Migrations.M20251128_migrate_member_relations where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
@@ -12,9 +12,8 @@ import Database.SQLite.Simple.QQ (sql)
-- - direction 0 (IDSubjectIntroduced): current member (subject) is re_group_member_id, was introduced to referenced member
-- - direction 1 (IDReferencedIntroduced): current member (subject) is to_group_member_id, referenced member was introduced to it
-- TODO [relations vector] drop group_member_intros in the end of migration
m20251128_member_relations_vector_stage_2 :: Query
m20251128_member_relations_vector_stage_2 =
m20251128_migrate_member_relations :: Query
m20251128_migrate_member_relations =
[sql|
UPDATE group_members
SET member_relations_vector = (
@@ -32,11 +31,14 @@ SET member_relations_vector = (
)
)
WHERE member_relations_vector IS NULL;
DROP INDEX idx_pending_group_messages_group_member_intro_id;
ALTER TABLE pending_group_messages DROP COLUMN group_member_intro_id;
|]
-- TODO [relations vector] re-create group_member_intros
down_m20251128_member_relations_vector_stage_2 :: Query
down_m20251128_member_relations_vector_stage_2 =
down_m20251128_migrate_member_relations :: Query
down_m20251128_migrate_member_relations =
[sql|
ALTER TABLE pending_group_messages ADD COLUMN group_member_intro_id INTEGER REFERENCES group_member_intros ON DELETE CASCADE;
CREATE INDEX idx_pending_group_messages_group_member_intro_id ON pending_group_messages(group_member_intro_id);
|]
@@ -882,7 +882,7 @@ Query:
FROM rcv_queues q
JOIN servers s ON q.host = s.host AND q.port = s.port
JOIN connections c ON q.conn_id = c.conn_id
WHERE c.deleted = 0 AND q.deleted = 0 AND c.user_id = ? AND q.host = ? AND q.port = ?
WHERE c.deleted = 0 AND q.deleted = 0 AND c.user_id = ? AND q.host = ? AND q.port = ? AND COALESCE(q.server_key_hash, s.key_hash) = ?
Plan:
SEARCH s USING PRIMARY KEY (host=? AND port=?)
SEARCH q USING PRIMARY KEY (host=? AND port=?)
@@ -894,7 +894,7 @@ Query:
FROM rcv_queues q
JOIN servers s ON q.host = s.host AND q.port = s.port
JOIN connections c ON q.conn_id = c.conn_id
WHERE q.to_subscribe = 1 AND c.deleted = 0 AND q.deleted = 0 AND c.user_id = ? AND q.host = ? AND q.port = ?
WHERE q.to_subscribe = 1 AND c.deleted = 0 AND q.deleted = 0 AND c.user_id = ? AND q.host = ? AND q.port = ? AND COALESCE(q.server_key_hash, s.key_hash) = ?
Plan:
SEARCH q USING INDEX idx_rcv_queues_to_subscribe (to_subscribe=? AND host=? AND port=?)
SEARCH c USING PRIMARY KEY (conn_id=?)
@@ -3444,14 +3444,6 @@ Query:
Plan:
SEARCH chat_item_reactions USING INDEX idx_chat_item_reactions_group (group_id=? AND shared_msg_id=?)
Query:
SELECT group_member_intro_id, intro_status
FROM group_member_intros
WHERE re_group_member_id = ? AND to_group_member_id = ?
Plan:
SEARCH group_member_intros USING INDEX sqlite_autoindex_group_member_intros_1 (re_group_member_id=? AND to_group_member_id=?)
Query:
SELECT group_scope_tag, group_scope_group_member_id
FROM chat_items
@@ -3501,7 +3493,7 @@ SEARCH m USING INDEX idx_group_members_user_id (user_id=?)
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT pgm.message_id, m.shared_msg_id, m.msg_body, m.chat_msg_event, pgm.group_member_intro_id
SELECT pgm.message_id, m.shared_msg_id, m.msg_body
FROM pending_group_messages pgm
JOIN messages m USING (message_id)
WHERE pgm.group_member_id = ?
@@ -3732,52 +3724,6 @@ SEARCH connections USING INDEX idx_connections_group_member_id (group_member_id=
LIST SUBQUERY 1
SCAN group_members USING COVERING INDEX idx_group_members_user_id_local_display_name
Query:
UPDATE group_member_intros SET intro_status='fwd'
WHERE re_group_member_id IN (SELECT group_member_id FROM group_members WHERE local_display_name = ?)
AND to_group_member_id IN (SELECT group_member_id FROM group_members WHERE local_display_name = ?)
Plan:
SEARCH group_member_intros USING INDEX sqlite_autoindex_group_member_intros_1 (re_group_member_id=? AND to_group_member_id=?)
LIST SUBQUERY 1
SCAN group_members USING COVERING INDEX idx_group_members_user_id_local_display_name
LIST SUBQUERY 2
SCAN group_members USING COVERING INDEX idx_group_members_user_id_local_display_name
Query:
UPDATE group_members
SET
member_relations_vector = (
SELECT migrate_relations_vector(idx, direction, intro_status)
FROM (
SELECT m.index_in_group AS idx, 0 AS direction, i.intro_status
FROM group_member_intros i
JOIN group_members m ON m.group_member_id = i.to_group_member_id
WHERE i.re_group_member_id = group_members.group_member_id
UNION ALL
SELECT m.index_in_group AS idx, 1 AS direction, i.intro_status
FROM group_member_intros i
JOIN group_members m ON m.group_member_id = i.re_group_member_id
WHERE i.to_group_member_id = group_members.group_member_id
) AS relations
),
updated_at = ?
WHERE group_member_id = ?
AND member_relations_vector IS NULL
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
CORRELATED SCALAR SUBQUERY 3
CO-ROUTINE relations
COMPOUND QUERY
LEFT-MOST SUBQUERY
SEARCH i USING INDEX idx_group_member_intros_re_group_member_id (re_group_member_id=?)
SEARCH m USING INTEGER PRIMARY KEY (rowid=?)
UNION ALL
SEARCH i USING INDEX idx_group_member_intros_to_group_member_id (to_group_member_id=?)
SEARCH m USING INTEGER PRIMARY KEY (rowid=?)
SCAN relations
Query:
UPDATE group_members
SET contact_id = ?, local_display_name = ?, contact_profile_id = ?, updated_at = ?
@@ -4449,7 +4395,7 @@ Plan:
Query:
INSERT INTO pending_group_messages
(group_member_id, message_id, group_member_intro_id, created_at, updated_at) VALUES (?,?,?,?,?)
(group_member_id, message_id, created_at, updated_at) VALUES (?,?,?,?)
Plan:
@@ -6111,10 +6057,6 @@ Plan:
Query: INSERT INTO xftp_file_descriptions (user_id, file_descr_text, file_descr_part_no, file_descr_complete, created_at, updated_at) VALUES (?,?,?,?,?,?)
Plan:
Query: SELECT 1 FROM group_members WHERE group_member_id = ? AND member_relations_vector IS NOT NULL
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query: SELECT 1 FROM settings WHERE user_id = ? LIMIT 1
Plan:
SEARCH settings USING COVERING INDEX idx_settings_user_id (user_id=?)
@@ -6145,12 +6087,6 @@ SCAN CONSTANT ROW
SCALAR SUBQUERY 1
SEARCH chat_items USING COVERING INDEX idx_chat_items_contacts_created_at (user_id=? AND contact_id=?)
Query: SELECT EXISTS (SELECT 1 FROM group_members WHERE member_relations_vector IS NULL LIMIT 1)
Plan:
SCAN CONSTANT ROW
SCALAR SUBQUERY 1
SCAN group_members
Query: SELECT accepted_at FROM operator_usage_conditions WHERE server_operator_id = ? AND conditions_commit = ?
Plan:
SEARCH operator_usage_conditions USING INDEX idx_operator_usage_conditions_conditions_commit (conditions_commit=? AND server_operator_id=?)
@@ -6531,14 +6467,6 @@ Query: UPDATE files SET private_snd_file_descr = ?, updated_at = ? WHERE user_id
Plan:
SEARCH files USING INTEGER PRIMARY KEY (rowid=?)
Query: UPDATE group_member_intros SET intro_status='con'
Plan:
SCAN group_member_intros
Query: UPDATE group_member_intros SET intro_status='fwd'
Plan:
SCAN group_member_intros
Query: UPDATE group_members SET contact_id = ?, updated_at = ? WHERE contact_profile_id = ?
Plan:
SEARCH group_members USING COVERING INDEX idx_group_members_contact_profile_id (contact_profile_id=?)
@@ -395,7 +395,6 @@ CREATE TABLE pending_group_messages(
pending_group_message_id INTEGER PRIMARY KEY,
group_member_id INTEGER NOT NULL REFERENCES group_members ON DELETE CASCADE,
message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE,
group_member_intro_id INTEGER REFERENCES group_member_intros ON DELETE CASCADE,
created_at TEXT NOT NULL DEFAULT(datetime('now')),
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
);
@@ -820,9 +819,6 @@ CREATE INDEX idx_group_profiles_user_id ON group_profiles(user_id);
CREATE INDEX idx_groups_chat_item_id ON groups(chat_item_id);
CREATE INDEX idx_groups_group_profile_id ON groups(group_profile_id);
CREATE INDEX idx_messages_group_id ON messages(group_id);
CREATE INDEX idx_pending_group_messages_group_member_intro_id ON pending_group_messages(
group_member_intro_id
);
CREATE INDEX idx_pending_group_messages_message_id ON pending_group_messages(
message_id
);