From 3ea0644002089db26739b5c5247f2ace98a367fc Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 28 Jun 2017 11:06:38 +0100 Subject: [PATCH] Storage functions for invite events --- .../roomserver/storage/invite_table.go | 161 ++++++++++++++++++ .../dendrite/roomserver/storage/sql.go | 50 ++---- .../dendrite/roomserver/storage/storage.go | 38 +++++ 3 files changed, 215 insertions(+), 34 deletions(-) create mode 100644 src/github.com/matrix-org/dendrite/roomserver/storage/invite_table.go diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/invite_table.go b/src/github.com/matrix-org/dendrite/roomserver/storage/invite_table.go new file mode 100644 index 000000000..059e12293 --- /dev/null +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/invite_table.go @@ -0,0 +1,161 @@ +// Copyright 2017 Vector Creations Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package storage + +import ( + "database/sql" + + "github.com/matrix-org/dendrite/roomserver/types" +) + +const inviteSchema = ` +CREATE TABLE invites ( + -- The numeric ID of the invite event itself. + invite_event_nid BIGINT NOT NULL CONSTRAINT invite_event_nid_unique UNIQUE, + -- The numeric ID of the room the invite m.room.member event is in. + room_nid BIGINT NOT NULL, + -- The numeric ID for the state key of the invite m.room.member event. + -- This tells us who the invite is for. + -- This is used to query the active invites for a user. + target_state_key_nid BIGINT NOT NULL, + -- The numeric ID for the sender of the invite m.room.member event. + -- This tells us who sent the invite. + -- This is used to work out which matrix server we should talk to when + -- we try to join the room. + sender_state_key_nid BIGINT NOT NULL DEFAULT 0, + -- The numeric ID of the join or leave event that replaced the invite event. + -- This is used to track whether the invite is still active. + -- This is set implicitly when processing a KIND_NEW events and explici + -- is set + replaced_by_event_nid BIGINT NOT NULL DEFAULT 0, + -- Whether the invite has been sent to the output stream. + -- We maintain a separate output stream of invite events since they don't + -- always occur within a room we have state for. + sent_invite_to_output BOOLEAN NOT NULL DEFAULT FALSE, + -- Whether the replacement has been sent to the output stream. + sent_replaced_to_output BOOLEAN NOT NULL DEFAULT FALSE, +); + +CREATE INDEX invites_active_idx ON invites (target_state_key_nid, room_nid) + WHERE replaced_by_event_nid == 0; +` + +const upsertInviteEventSQL = "" + + "INSERT INTO invites (invite_event_nid, room_nid, target_state_key_nid," + + " sender_state_key_nid) VALUES ($1, $2, $3, $4)" + + " ON CONFLICT ON invite_event_nid_unique" + + " DO UPDATE SET sender_state_key_nid = $4" + +const upsertInviteReplacedBySQL = "" + + "INSERT INTO invites (invite_event_nid, room_nid, target_state_key_nid," + + " replaced_by_event_nid) VALUES ($1, $2, $3, $4)" + + " ON CONFLICT ON invite_event_nid_unique" + + " DO UPDATE SET replaced_by_event_nid = $4" + +const selectInviteSQL = "" + + "SELECT replaced_by_event_nid, sent_invite_to_output," + + " sent_replaced_to_output FROM invites" + + " WHERE invite_event_nid = $1" + +const selectActiveInviteForUserInRoomSQL = "" + + "SELECT invite_event_nid, sender_state_key_nid FROM invites" + + " WHERE target_state_key_nid = $1 AND room_nid = $2" + + " AND replaced_by_event_nid = 0" + +const updateInviteSentInviteToOutputSQL = "" + + "UPDATE invites SET sent_invite_to_output = TRUE" + + " WHERE invite_event_nid = $1" + +const updateInviteSentReplacedToOutputSQL = "" + + "UPDATE invites SET sent_replaced_to_output = TRUE" + + " WHERE invite_event_nid = $1" + +type inviteStatements struct { + upsertInviteEventStmt *sql.Stmt + upsertInviteReplacedByStmt *sql.Stmt + selectInviteStmt *sql.Stmt + selectActiveInviteForUserInRoomStmt *sql.Stmt + updateInviteSentInviteToOutputStmt *sql.Stmt + updateInviteSentReplacedToOutputStmt *sql.Stmt +} + +func (s *inviteStatements) prepare(db *sql.DB) (err error) { + _, err = db.Exec(inviteSchema) + if err != nil { + return + } + + return statementList{ + {&s.upsertInviteEventStmt, upsertInviteEventSQL}, + {&s.upsertInviteReplacedByStmt, upsertInviteReplacedBySQL}, + {&s.selectInviteStmt, selectInviteSQL}, + {&s.selectActiveInviteForUserInRoomStmt, selectActiveInviteForUserInRoomSQL}, + {&s.updateInviteSentInviteToOutputStmt, updateInviteSentInviteToOutputSQL}, + {&s.updateInviteSentReplacedToOutputStmt, updateInviteSentReplacedToOutputSQL}, + }.prepare(db) +} + +func (s *inviteStatements) upsertInviteEvent( + inviteEventNID types.EventNID, roomNID types.RoomNID, + targetStateKeyNID, senderStateKeyNID types.EventStateKeyNID, +) error { + _, err := s.upsertInviteEventStmt.Exec( + inviteEventNID, roomNID, targetStateKeyNID, senderStateKeyNID, + ) + return err +} + +func (s *inviteStatements) upsertInviteReplacedBy( + inviteEventNID types.EventNID, roomNID types.RoomNID, + targetStateKeyNID types.EventStateKeyNID, + replacedByEventNID types.EventNID, +) error { + _, err := s.upsertInviteReplacedByStmt.Exec( + inviteEventNID, roomNID, targetStateKeyNID, replacedByEventNID, + ) + return err +} + +func (s *inviteStatements) selectInvite( + inviteEventNID types.EventNID, +) (replacedByNID types.EventNID, sentInviteToOutput, sentReplacedToOutput bool, err error) { + err = s.selectInviteStmt.QueryRow(inviteEventNID).Scan( + &replacedByNID, &sentInviteToOutput, &sentReplacedToOutput, + ) + return +} + +func (s *inviteStatements) selectActiveInviteForUserInRoom( + targetStateKeyNID types.EventStateKeyNID, roomNID types.RoomNID, +) (inviteEventNID types.EventNID, senderStateKeyNID types.EventStateKeyNID, err error) { + err = s.selectActiveInviteForUserInRoomStmt.QueryRow( + targetStateKeyNID, roomNID, + ).Scan(&inviteEventNID, &senderStateKeyNID) + return +} + +func (s *inviteStatements) updateInviteSentInviteToOutput( + inviteEventNID types.EventNID, +) error { + _, err := s.updateInviteSentInviteToOutputStmt.Exec(inviteEventNID) + return err +} + +func (s *inviteStatements) updateInviteSentReplacedToOutput( + inviteEventNID types.EventNID, +) error { + _, err := s.updateInviteSentReplacedToOutputStmt.Exec(inviteEventNID) + return err +} diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/sql.go b/src/github.com/matrix-org/dendrite/roomserver/storage/sql.go index fcc5eb882..37d43e024 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/sql.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/sql.go @@ -30,45 +30,27 @@ type statements struct { stateSnapshotStatements stateBlockStatements previousEventStatements + inviteStatements } func (s *statements) prepare(db *sql.DB) error { var err error - if err = s.PartitionOffsetStatements.Prepare(db); err != nil { - return err - } - - if err = s.eventTypeStatements.prepare(db); err != nil { - return err - } - - if err = s.eventStateKeyStatements.prepare(db); err != nil { - return err - } - - if err = s.roomStatements.prepare(db); err != nil { - return err - } - - if err = s.eventStatements.prepare(db); err != nil { - return err - } - - if err = s.eventJSONStatements.prepare(db); err != nil { - return err - } - - if err = s.stateSnapshotStatements.prepare(db); err != nil { - return err - } - - if err = s.stateBlockStatements.prepare(db); err != nil { - return err - } - - if err = s.previousEventStatements.prepare(db); err != nil { - return err + for _, prepare := range []func(db *sql.DB) error{ + s.PartitionOffsetStatements.Prepare, + s.eventTypeStatements.prepare, + s.eventStateKeyStatements.prepare, + s.roomStatements.prepare, + s.eventStatements.prepare, + s.eventJSONStatements.prepare, + s.stateSnapshotStatements.prepare, + s.stateBlockStatements.prepare, + s.previousEventStatements.prepare, + s.inviteStatements.prepare, + } { + if err = prepare(db); err != nil { + return err + } } return nil diff --git a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go index 1dfc89d4c..162dd7fda 100644 --- a/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go +++ b/src/github.com/matrix-org/dendrite/roomserver/storage/storage.go @@ -372,3 +372,41 @@ func (d *Database) StateEntriesForTuples( ) ([]types.StateEntryList, error) { return d.statements.bulkSelectFilteredStateBlockEntries(stateBlockNIDs, stateKeyTuples) } + +// StoreInvite implements input.EventDatabase +func (d *Database) StoreInvite( + roomNID types.RoomNID, inviteEventNID types.EventNID, + targetNID types.EventStateKeyNID, senderID string, +) (replacedByNID types.EventNID, sentInviteToOutput bool, err error) { + var senderNID types.EventStateKeyNID + if senderNID, err = d.assignStateKeyNID(senderID); err != nil { + return + } + + if err = d.statements.upsertInviteEvent(inviteEventNID, roomNID, targetNID, senderNID); err != nil { + return + } + + if replacedByNID, sentInviteToOutput, _, err = d.statements.selectInvite(inviteEventNID); err != nil { + return + } + + return +} + +// MarkInviteAsSent implements input.EventDatabase +func (d *Database) MarkInviteAsSent(inviteEventNID types.EventNID) error { + return d.statements.updateInviteSentInviteToOutput(inviteEventNID) +} + +// MarkInviteReplacedAsSent implements input.EventDatabase +func (d *Database) MarkInviteReplacedAsSent(inviteEventNID types.EventNID) error { + return d.statements.updateInviteSentReplacedToOutput(inviteEventNID) +} + +// LookupInviteForUserInRoom implements query.RoomserverQueryAPIDB +func (d *Database) LookupInviteForUserInRoom( + roomNID types.RoomNID, targetNID types.EventStateKeyNID, +) (eventNID types.EventNID, senderNID types.EventStateKeyNID, err error) { + return d.statements.selectActiveInviteForUserInRoom(targetNID, roomNID) +}