From fb8dfa02f285f9f274f0360c7f986f8c9620a0e6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 12 Jan 2022 10:35:52 +0000 Subject: [PATCH] merge database migration, rename field in group_members: inv_queue_info to sent_inv_queue_info (#200) --- migrations/20210612_initial.sql | 137 +++++------- migrations/20211205_user_contacts.sql | 29 --- migrations/20211229_messages.sql | 202 ------------------ .../20220106_group_members_inv_queue_info.sql | 3 - src/Simplex/Chat/Store.hs | 8 +- 5 files changed, 61 insertions(+), 318 deletions(-) delete mode 100644 migrations/20211205_user_contacts.sql delete mode 100644 migrations/20211229_messages.sql delete mode 100644 migrations/20220106_group_members_inv_queue_info.sql diff --git a/migrations/20210612_initial.sql b/migrations/20210612_initial.sql index a9f2e8b24d..daf04bf768 100644 --- a/migrations/20210612_initial.sql +++ b/migrations/20210612_initial.sql @@ -90,7 +90,7 @@ CREATE TABLE groups ( user_id INTEGER NOT NULL REFERENCES users, local_display_name TEXT NOT NULL, -- local group name without spaces group_profile_id INTEGER REFERENCES group_profiles, -- shared group profile - inv_queue_info BLOB, + inv_queue_info BLOB, -- received FOREIGN KEY (user_id, local_display_name) REFERENCES display_names (user_id, local_display_name) ON DELETE RESTRICT @@ -107,8 +107,9 @@ CREATE TABLE group_members ( -- group members, excluding the local user member_category TEXT NOT NULL, -- see GroupMemberCategory member_status TEXT NOT NULL, -- see GroupMemberStatus invited_by INTEGER REFERENCES contacts (contact_id) ON DELETE RESTRICT, -- NULL for the members who joined before the current user and for the group creator - group_queue_info BLOB, - direct_queue_info BLOB, + sent_inv_queue_info BLOB, -- sent + group_queue_info BLOB, -- received + direct_queue_info BLOB, -- received user_id INTEGER NOT NULL REFERENCES users, local_display_name TEXT NOT NULL, -- should be the same as contact contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles ON DELETE RESTRICT, @@ -120,6 +121,8 @@ CREATE TABLE group_members ( -- group members, excluding the local user UNIQUE (group_id, member_id) ); +CREATE INDEX idx_groups_inv_queue_info ON groups (inv_queue_info); + CREATE TABLE group_member_intros ( group_member_intro_id INTEGER PRIMARY KEY, re_group_member_id INTEGER NOT NULL REFERENCES group_members (group_member_id) ON DELETE CASCADE, @@ -182,6 +185,7 @@ CREATE TABLE connections ( -- all SMP agent connections via_contact INTEGER REFERENCES contacts (contact_id), conn_status TEXT NOT NULL, conn_type TEXT NOT NULL, -- contact, member, rcv_file, snd_file + user_contact_link_id INTEGER REFERENCES user_contact_links ON DELETE RESTRICT, contact_id INTEGER REFERENCES contacts ON DELETE RESTRICT, group_member_id INTEGER REFERENCES group_members ON DELETE RESTRICT, snd_file_id INTEGER, @@ -194,87 +198,60 @@ CREATE TABLE connections ( -- all SMP agent connections DEFERRABLE INITIALLY DEFERRED ); --- PLEASE NOTE: all tables below were unused and are removed in the migration 20211227_messages.sql +CREATE TABLE user_contact_links ( + user_contact_link_id INTEGER PRIMARY KEY, + conn_req_contact BLOB NOT NULL, + local_display_name TEXT NOT NULL DEFAULT '', + created_at TEXT NOT NULL DEFAULT (datetime('now')), + user_id INTEGER NOT NULL REFERENCES users, + UNIQUE (user_id, local_display_name) +); -CREATE TABLE events ( -- messages received by the agent, append only - event_id INTEGER PRIMARY KEY, - agent_msg_id INTEGER NOT NULL, -- internal message ID - external_msg_id INTEGER NOT NULL, -- external message ID (sent or received) - agent_meta TEXT NOT NULL, -- JSON with timestamps etc. sent in MSG - connection_id INTEGER NOT NULL REFERENCES connections, - received INTEGER NOT NULL, -- 0 for received, 1 for sent - chat_event_id INTEGER, - continuation_of INTEGER, -- references chat_event_id, but can be incorrect - event_type TEXT NOT NULL, -- event type - see protocol/types.ts - event_encoding INTEGER NOT NULL, -- format of event_body: 0 - binary, 1 - text utf8, 2 - JSON (utf8) - content_type TEXT NOT NULL, -- content type - see protocol/types.ts - event_body BLOB, -- agent message body as sent - event_hash BLOB NOT NULL, - integrity TEXT NOT NULL DEFAULT '', +CREATE TABLE contact_requests ( + contact_request_id INTEGER PRIMARY KEY, + user_contact_link_id INTEGER NOT NULL REFERENCES user_contact_links + ON UPDATE CASCADE ON DELETE CASCADE, + agent_invitation_id BLOB NOT NULL, + contact_profile_id INTEGER REFERENCES contact_profiles + DEFERRABLE INITIALLY DEFERRED, -- NULL if it's an incognito profile + local_display_name TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + user_id INTEGER NOT NULL REFERENCES users, + FOREIGN KEY (user_id, local_display_name) + REFERENCES display_names (user_id, local_display_name) + ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + UNIQUE (user_id, local_display_name), + UNIQUE (user_id, contact_profile_id) +); + +-- all message events as received or sent, append only +-- maps to message deliveries as one-to-many for group messages +CREATE TABLE messages ( + message_id INTEGER PRIMARY KEY, + msg_sent INTEGER NOT NULL, -- 0 for received, 1 for sent + chat_msg_event TEXT NOT NULL, -- message event type (the constructor of ChatMsgEvent) + msg_body BLOB, -- agent message body as received or sent created_at TEXT NOT NULL DEFAULT (datetime('now')) ); -CREATE INDEX events_external_msg_id_index ON events (connection_id, external_msg_id); - -CREATE TABLE event_body_parts ( - event_body_part_id INTEGER PRIMARY KEY, - event_id REFERENCES events, - full_size INTEGER NOT NULL, - part_status TEXT, -- full, partial - content_type TEXT NOT NULL, - event_part BLOB +-- message deliveries communicated with the agent, append only +CREATE TABLE msg_deliveries ( + msg_delivery_id INTEGER PRIMARY KEY, + message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE, -- non UNIQUE for group messages + connection_id INTEGER NOT NULL REFERENCES connections ON DELETE CASCADE, + agent_msg_id INTEGER, -- internal agent message ID (NULL while pending) + agent_msg_meta TEXT, -- JSON with timestamps etc. sent in MSG, NULL for sent + chat_ts TEXT NOT NULL DEFAULT (datetime('now')), -- broker_ts for received, created_at for sent + UNIQUE (connection_id, agent_msg_id) ); -CREATE TABLE contact_profile_events ( - event_id INTEGER NOT NULL UNIQUE REFERENCES events, - contact_profile_id INTEGER NOT NULL REFERENCES contact_profiles -); - -CREATE TABLE group_profile_events ( - event_id INTEGER NOT NULL UNIQUE REFERENCES events, - group_profile_id INTEGER NOT NULL REFERENCES group_profiles -); - -CREATE TABLE group_events ( - event_id INTEGER NOT NULL UNIQUE REFERENCES events, - group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT, - group_member_id INTEGER REFERENCES group_members -- NULL for current user -); - -CREATE TABLE group_event_parents ( - group_event_parent_id INTEGER PRIMARY KEY, - event_id INTEGER NOT NULL REFERENCES group_events (event_id), - parent_group_member_id INTEGER REFERENCES group_members (group_member_id), -- can be NULL if parent_member_id is incorrect - parent_member_id BLOB, -- shared member ID, unique per group - parent_event_id INTEGER REFERENCES events (event_id) ON DELETE CASCADE, -- this can be NULL if received event references another event that's not received yet - parent_chat_event_id INTEGER NOT NULL, - parent_event_hash BLOB NOT NULL -); - -CREATE INDEX group_event_parents_parent_chat_event_id_index - ON group_event_parents (parent_member_id, parent_chat_event_id); - -CREATE TABLE messages ( -- mutable messages presented to user - message_id INTEGER PRIMARY KEY, - contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, -- 1 for sent messages - group_id INTEGER REFERENCES groups ON DELETE RESTRICT, -- NULL for direct messages - deleted INTEGER NOT NULL, -- 1 for deleted - msg_type TEXT NOT NULL, - content_type TEXT NOT NULL, - msg_text TEXT NOT NULL, -- textual representation - msg_props TEXT NOT NULL -- JSON -); - -CREATE TABLE message_content ( - message_content_id INTEGER PRIMARY KEY, - message_id INTEGER REFERENCES messages ON DELETE CASCADE, - content_type TEXT NOT NULL, - content_size INTEGER, -- full expected content size - content_status TEXT, -- empty, part, full - content BLOB NOT NULL -); - -CREATE TABLE message_events ( - event_id INTEGER NOT NULL UNIQUE REFERENCES events, - message_id INTEGER NOT NULL REFERENCES messages +-- TODO recovery for received messages with "rcv_agent" status - acknowledge to agent +-- changes of messagy delivery status, append only +CREATE TABLE msg_delivery_events ( + msg_delivery_event_id INTEGER PRIMARY KEY, + msg_delivery_id INTEGER NOT NULL REFERENCES msg_deliveries ON DELETE CASCADE, -- non UNIQUE for multiple events per msg delivery + delivery_status TEXT NOT NULL, -- see MsgDeliveryStatus for allowed values + created_at TEXT NOT NULL DEFAULT (datetime('now')), + UNIQUE (msg_delivery_id, delivery_status) ); diff --git a/migrations/20211205_user_contacts.sql b/migrations/20211205_user_contacts.sql deleted file mode 100644 index faca794c8e..0000000000 --- a/migrations/20211205_user_contacts.sql +++ /dev/null @@ -1,29 +0,0 @@ -CREATE TABLE user_contact_links ( - user_contact_link_id INTEGER PRIMARY KEY, - conn_req_contact BLOB NOT NULL, - local_display_name TEXT NOT NULL DEFAULT '', - created_at TEXT NOT NULL DEFAULT (datetime('now')), - user_id INTEGER NOT NULL REFERENCES users, - UNIQUE (user_id, local_display_name) -); - -CREATE TABLE contact_requests ( - contact_request_id INTEGER PRIMARY KEY, - user_contact_link_id INTEGER NOT NULL REFERENCES user_contact_links - ON UPDATE CASCADE ON DELETE CASCADE, - agent_invitation_id BLOB NOT NULL, - contact_profile_id INTEGER REFERENCES contact_profiles - DEFERRABLE INITIALLY DEFERRED, -- NULL if it's an incognito profile - local_display_name TEXT NOT NULL, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - user_id INTEGER NOT NULL REFERENCES users, - FOREIGN KEY (user_id, local_display_name) - REFERENCES display_names (user_id, local_display_name) - ON UPDATE CASCADE - DEFERRABLE INITIALLY DEFERRED, - UNIQUE (user_id, local_display_name), - UNIQUE (user_id, contact_profile_id) -); - -ALTER TABLE connections ADD user_contact_link_id INTEGER -REFERENCES user_contact_links ON DELETE RESTRICT; diff --git a/migrations/20211229_messages.sql b/migrations/20211229_messages.sql deleted file mode 100644 index 86e2fc56ee..0000000000 --- a/migrations/20211229_messages.sql +++ /dev/null @@ -1,202 +0,0 @@ -DROP TABLE event_body_parts; -DROP TABLE contact_profile_events; -DROP TABLE group_profile_events; -DROP TABLE group_event_parents; -DROP TABLE group_events; -DROP TABLE message_events; -DROP TABLE message_content; -DROP TABLE events; -DROP TABLE messages; - --- all message events as received or sent, append only --- maps to message deliveries as one-to-many for group messages -CREATE TABLE messages ( - message_id INTEGER PRIMARY KEY, - msg_sent INTEGER NOT NULL, -- 0 for received, 1 for sent - chat_msg_event TEXT NOT NULL, -- message event type (the constructor of ChatMsgEvent) - msg_body BLOB, -- agent message body as received or sent - created_at TEXT NOT NULL DEFAULT (datetime('now')) -); - --- message deliveries communicated with the agent, append only -CREATE TABLE msg_deliveries ( - msg_delivery_id INTEGER PRIMARY KEY, - message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE, -- non UNIQUE for group messages - connection_id INTEGER NOT NULL REFERENCES connections ON DELETE CASCADE, - agent_msg_id INTEGER, -- internal agent message ID (NULL while pending) - agent_msg_meta TEXT, -- JSON with timestamps etc. sent in MSG, NULL for sent - chat_ts TEXT NOT NULL DEFAULT (datetime('now')), -- broker_ts for received, created_at for sent - UNIQUE (connection_id, agent_msg_id) -); - --- TODO recovery for received messages with "rcv_agent" status - acknowledge to agent --- changes of messagy delivery status, append only -CREATE TABLE msg_delivery_events ( - msg_delivery_event_id INTEGER PRIMARY KEY, - msg_delivery_id INTEGER NOT NULL REFERENCES msg_deliveries ON DELETE CASCADE, -- non UNIQUE for multiple events per msg delivery - delivery_status TEXT NOT NULL, -- see MsgDeliveryStatus for allowed values - created_at TEXT NOT NULL DEFAULT (datetime('now')), - UNIQUE (msg_delivery_id, delivery_status) -); - --- TODO delete all message views when merging migrations - -CREATE VIEW direct_messages AS -SELECT - ct.local_display_name AS contact, - m.message_id AS message_id, - m.msg_sent AS msg_sent, - m.chat_msg_event AS chat_msg_event, - m.msg_body AS msg_body, - md.msg_delivery_id AS delivery_id, - datetime(md.chat_ts) AS chat_dt, - md.agent_msg_meta AS msg_meta, - mde.delivery_status AS delivery_status, - datetime(mde.created_at) AS delivery_status_dt -FROM messages m -JOIN msg_deliveries md ON md.message_id = m.message_id -JOIN ( - SELECT msg_delivery_id, MAX(created_at) MaxDate - FROM msg_delivery_events - GROUP BY msg_delivery_id -) MaxDates ON MaxDates.msg_delivery_id = md.msg_delivery_id -JOIN msg_delivery_events mde ON mde.msg_delivery_id = MaxDates.msg_delivery_id - AND mde.created_at = MaxDates.MaxDate -JOIN connections c ON c.connection_id = md.connection_id -JOIN contacts ct ON ct.contact_id = c.contact_id -ORDER BY chat_dt DESC; - -CREATE VIEW direct_messages_plain AS -SELECT - dm.contact AS contact, - dm.msg_sent AS msg_sent, - dm.msg_body AS msg_body, - dm.chat_dt AS chat_dt -FROM direct_messages dm -WHERE dm.chat_msg_event = 'x.msg.new'; - -CREATE VIEW group_messages AS -SELECT - g.local_display_name AS group_name, - gm.local_display_name AS contact, - m.message_id AS message_id, - m.msg_sent AS msg_sent, - m.chat_msg_event AS chat_msg_event, - m.msg_body AS msg_body, - md.msg_delivery_id AS delivery_id, - datetime(md.chat_ts) AS chat_dt, - md.agent_msg_meta AS msg_meta, - mde.delivery_status AS delivery_status, - datetime(mde.created_at) AS delivery_status_dt -FROM messages m -JOIN msg_deliveries md ON md.message_id = m.message_id -JOIN ( - SELECT msg_delivery_id, MAX(created_at) MaxDate - FROM msg_delivery_events - GROUP BY msg_delivery_id -) MaxDates ON MaxDates.msg_delivery_id = md.msg_delivery_id -JOIN msg_delivery_events mde ON mde.msg_delivery_id = MaxDates.msg_delivery_id - AND mde.created_at = MaxDates.MaxDate -JOIN connections c ON c.connection_id = md.connection_id -JOIN group_members gm ON gm.group_member_id = c.group_member_id -JOIN groups g ON g.group_id = gm.group_id -ORDER BY chat_dt DESC; - -CREATE VIEW group_messages_plain AS -SELECT - gm.group_name AS group_name, - (CASE WHEN gm.msg_sent = 0 THEN gm.contact ELSE gm.group_name END) AS contact, - gm.msg_sent AS msg_sent, - gm.msg_body AS msg_body, - gm.chat_dt AS chat_dt -FROM group_messages gm -JOIN ( - SELECT message_id, MIN(delivery_id) MinDeliveryId - FROM group_messages - GROUP BY message_id -) Deduplicated ON Deduplicated.message_id = gm.message_id - AND Deduplicated.MinDeliveryId = gm.delivery_id -WHERE gm.chat_msg_event = 'x.msg.new'; - -CREATE VIEW all_messages ( - group_name, - contact, - message_id, - msg_sent, - chat_msg_event, - msg_body, - delivery_id, - chat_dt, - msg_meta, - delivery_status, - delivery_status_dt -) AS - SELECT * FROM ( - SELECT NULL AS group_name, * FROM direct_messages - UNION - SELECT * FROM group_messages - ) - ORDER BY chat_dt DESC; - -CREATE VIEW all_messages_plain ( - group_name, - contact, - msg_sent, - msg_body, - chat_dt -) AS - SELECT * FROM ( - SELECT NULL AS group_name, * FROM direct_messages_plain - UNION - SELECT * FROM group_messages_plain - ) - ORDER BY chat_dt DESC; - --- TODO group message parents and chat items not to be implemented in current scope - --- CREATE TABLE group_message_parents ( --- group_message_parent_id INTEGER PRIMARY KEY, --- message_id INTEGER NOT NULL REFERENCES group_messages (event_id), --- parent_group_member_id INTEGER REFERENCES group_members (group_member_id), -- can be NULL if parent_member_id is incorrect --- parent_member_id BLOB, -- shared member ID, unique per group --- parent_message_id INTEGER REFERENCES messages (message_id) ON DELETE CASCADE, -- can be NULL if received message references another message that's not received yet --- parent_chat_msg_id INTEGER NOT NULL, --- parent_msg_body_hash BLOB NOT NULL --- ); - --- CREATE INDEX group_event_parents_parent_chat_event_id_index --- ON group_message_parents (parent_member_id, parent_chat_msg_id); - --- CREATE TABLE chat_items ( -- mutable chat_items presented to user --- chat_item_id INTEGER PRIMARY KEY, --- chat_msg_id INTEGER NOT NULL, -- sent as part of the message that created the item --- item_deleted INTEGER NOT NULL, -- 1 for deleted --- item_type TEXT NOT NULL, --- item_text TEXT NOT NULL, -- textual representation --- item_props TEXT NOT NULL -- JSON --- ); - --- CREATE TABLE direct_chat_items ( --- chat_item_id INTEGER NOT NULL UNIQUE REFERENCES chat_items ON DELETE CASCADE, --- contact_id INTEGER NOT NULL REFERENCES contacts ON DELETE RESTRICT, --- item_sent INTEGER -- 1 for sent, 0 for received --- ); - --- CREATE TABLE group_chat_items ( --- chat_item_id INTEGER NOT NULL UNIQUE REFERENCES chat_items ON DELETE CASCADE, --- group_member_id INTEGER REFERENCES group_members ON DELETE RESTRICT, -- NULL for sent --- group_id INTEGER NOT NULL REFERENCES groups ON DELETE RESTRICT --- ); - --- CREATE TABLE chat_item_content ( --- chat_item_content_id INTEGER PRIMARY KEY, --- chat_item_id INTEGER NOT NULL REFERENCES chat_items ON DELETE CASCADE, --- content_type TEXT NOT NULL, --- content_size INTEGER NOT NULL, --- content BLOB NOT NULL --- ); - --- CREATE TABLE chat_item_messages ( --- message_id INTEGER NOT NULL UNIQUE REFERENCES messages, --- chat_item_id INTEGER NOT NULL REFERENCES chat_items --- ); diff --git a/migrations/20220106_group_members_inv_queue_info.sql b/migrations/20220106_group_members_inv_queue_info.sql deleted file mode 100644 index 17f4f88f55..0000000000 --- a/migrations/20220106_group_members_inv_queue_info.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE group_members ADD inv_queue_info BLOB; - -CREATE INDEX idx_groups_inv_queue_info ON groups (inv_queue_info); diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 9187673a2f..6b3ff2ba6c 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -1043,7 +1043,7 @@ getMemberInvitation :: StoreMonad m => SQLiteStore -> User -> Int64 -> m (Maybe getMemberInvitation st User {userId} groupMemberId = liftIO . withTransaction st $ \db -> join . listToMaybe . map fromOnly - <$> DB.query db "SELECT inv_queue_info FROM group_members WHERE group_member_id = ? AND user_id = ?;" (groupMemberId, userId) + <$> DB.query db "SELECT sent_inv_queue_info FROM group_members WHERE group_member_id = ? AND user_id = ?;" (groupMemberId, userId) createMemberConnection :: MonadUnliftIO m => SQLiteStore -> UserId -> GroupMember -> ConnId -> m () createMemberConnection st userId GroupMember {groupMemberId} agentConnId = @@ -1289,12 +1289,12 @@ createContactMemberInv_ db User {userId, userContactId} groupId userOrContact Me [sql| INSERT INTO group_members ( group_id, member_id, member_role, member_category, member_status, invited_by, - user_id, local_display_name, contact_profile_id, contact_id, inv_queue_info) + user_id, local_display_name, contact_profile_id, contact_id, sent_inv_queue_info) VALUES (:group_id,:member_id,:member_role,:member_category,:member_status,:invited_by, :user_id,:local_display_name, (SELECT contact_profile_id FROM contacts WHERE contact_id = :contact_id), - :contact_id, :inv_queue_info) + :contact_id, :sent_inv_queue_info) |] [ ":group_id" := groupId, ":member_id" := memberId, @@ -1305,7 +1305,7 @@ createContactMemberInv_ db User {userId, userContactId} groupId userOrContact Me ":user_id" := userId, ":local_display_name" := localDisplayName' userOrContact, ":contact_id" := contactId' userOrContact, - ":inv_queue_info" := connRequest + ":sent_inv_queue_info" := connRequest ] getViaGroupMember :: MonadUnliftIO m => SQLiteStore -> User -> Contact -> m (Maybe (GroupName, GroupMember))