2020-05-26 14:42:42 +00:00
package shared
import (
"context"
2020-05-26 17:23:39 +00:00
"database/sql"
"encoding/json"
2020-07-21 14:48:21 +00:00
"fmt"
2020-09-03 17:27:02 +00:00
"sort"
2020-05-26 14:42:42 +00:00
2020-08-25 11:32:29 +00:00
"github.com/matrix-org/dendrite/internal/caching"
2020-06-12 13:55:57 +00:00
"github.com/matrix-org/dendrite/internal/sqlutil"
2020-05-26 14:42:42 +00:00
"github.com/matrix-org/dendrite/roomserver/storage/tables"
"github.com/matrix-org/dendrite/roomserver/types"
2020-05-26 17:23:39 +00:00
"github.com/matrix-org/gomatrixserverlib"
2020-09-03 17:27:02 +00:00
"github.com/matrix-org/util"
2022-02-22 13:40:08 +00:00
"github.com/sirupsen/logrus"
2020-07-06 16:49:15 +00:00
"github.com/tidwall/gjson"
2020-05-26 14:42:42 +00:00
)
2020-07-06 16:49:15 +00:00
// Ideally, when we have both events we should redact the event JSON and forget about the redaction, but we currently
// don't because the redaction code is brand new. When we are more certain that redactions don't misbehave or are
// vulnerable to attacks from remote servers (e.g a server bypassing event auth rules shouldn't redact our data)
// then we should flip this to true. This will mean redactions /actually delete information irretrievably/ which
// will be necessary for compliance with the law. Note that downstream components (syncapi) WILL delete information
// in their database on receipt of a redaction. Also note that we still modify the event JSON to set the field
// unsigned.redacted_because - we just don't clear out the content fields yet.
2020-08-25 14:44:19 +00:00
const redactionsArePermanent = true
2020-07-06 16:49:15 +00:00
2020-05-26 14:42:42 +00:00
type Database struct {
2022-02-04 10:39:34 +00:00
DB * sql . DB
Cache caching . RoomServerCaches
Writer sqlutil . Writer
EventsTable tables . Events
EventJSONTable tables . EventJSON
EventTypesTable tables . EventTypes
EventStateKeysTable tables . EventStateKeys
RoomsTable tables . Rooms
StateSnapshotTable tables . StateSnapshot
StateBlockTable tables . StateBlock
RoomAliasesTable tables . RoomAliases
PrevEventsTable tables . PreviousEvents
InvitesTable tables . Invites
MembershipTable tables . Membership
PublishedTable tables . Published
RedactionsTable tables . Redactions
GetRoomUpdaterFn func ( ctx context . Context , roomInfo * types . RoomInfo ) ( * RoomUpdater , error )
2020-05-26 14:42:42 +00:00
}
2020-08-20 15:24:33 +00:00
func ( d * Database ) SupportsConcurrentRoomInputs ( ) bool {
return true
}
2020-05-26 14:42:42 +00:00
func ( d * Database ) EventTypeNIDs (
ctx context . Context , eventTypes [ ] string ,
2022-02-04 10:39:34 +00:00
) ( map [ string ] types . EventTypeNID , error ) {
return d . eventTypeNIDs ( ctx , nil , eventTypes )
}
func ( d * Database ) eventTypeNIDs (
ctx context . Context , txn * sql . Tx , eventTypes [ ] string ,
2020-05-26 14:42:42 +00:00
) ( map [ string ] types . EventTypeNID , error ) {
2020-08-25 11:32:29 +00:00
result := make ( map [ string ] types . EventTypeNID )
2022-02-18 10:58:41 +00:00
nids , err := d . EventTypesTable . BulkSelectEventTypeNID ( ctx , txn , eventTypes )
if err != nil {
return nil , err
2020-08-25 11:32:29 +00:00
}
2022-02-18 10:58:41 +00:00
for eventType , nid := range nids {
result [ eventType ] = nid
2020-08-25 11:32:29 +00:00
}
return result , nil
2020-05-26 14:42:42 +00:00
}
func ( d * Database ) EventStateKeys (
ctx context . Context , eventStateKeyNIDs [ ] types . EventStateKeyNID ,
) ( map [ types . EventStateKeyNID ] string , error ) {
2022-02-04 10:39:34 +00:00
return d . EventStateKeysTable . BulkSelectEventStateKey ( ctx , nil , eventStateKeyNIDs )
2020-05-26 14:42:42 +00:00
}
func ( d * Database ) EventStateKeyNIDs (
ctx context . Context , eventStateKeys [ ] string ,
2022-02-04 10:39:34 +00:00
) ( map [ string ] types . EventStateKeyNID , error ) {
return d . eventStateKeyNIDs ( ctx , nil , eventStateKeys )
}
func ( d * Database ) eventStateKeyNIDs (
ctx context . Context , txn * sql . Tx , eventStateKeys [ ] string ,
2020-05-26 14:42:42 +00:00
) ( map [ string ] types . EventStateKeyNID , error ) {
2020-08-25 11:32:29 +00:00
result := make ( map [ string ] types . EventStateKeyNID )
2022-02-18 10:58:41 +00:00
nids , err := d . EventStateKeysTable . BulkSelectEventStateKeyNID ( ctx , txn , eventStateKeys )
if err != nil {
return nil , err
2020-08-25 11:32:29 +00:00
}
2022-02-18 10:58:41 +00:00
for eventStateKey , nid := range nids {
result [ eventStateKey ] = nid
2020-08-25 11:32:29 +00:00
}
return result , nil
2020-05-26 14:42:42 +00:00
}
2020-05-26 15:45:28 +00:00
func ( d * Database ) StateEntriesForEventIDs (
ctx context . Context , eventIDs [ ] string ,
) ( [ ] types . StateEntry , error ) {
2022-02-04 10:39:34 +00:00
return d . EventsTable . BulkSelectStateEventByID ( ctx , nil , eventIDs )
2020-05-26 15:45:28 +00:00
}
2020-05-27 08:36:09 +00:00
func ( d * Database ) StateEntriesForTuples (
ctx context . Context ,
stateBlockNIDs [ ] types . StateBlockNID ,
stateKeyTuples [ ] types . StateKeyTuple ,
2022-02-04 10:39:34 +00:00
) ( [ ] types . StateEntryList , error ) {
return d . stateEntriesForTuples ( ctx , nil , stateBlockNIDs , stateKeyTuples )
}
func ( d * Database ) stateEntriesForTuples (
ctx context . Context , txn * sql . Tx ,
stateBlockNIDs [ ] types . StateBlockNID ,
stateKeyTuples [ ] types . StateKeyTuple ,
2020-05-27 08:36:09 +00:00
) ( [ ] types . StateEntryList , error ) {
2021-04-26 12:25:57 +00:00
entries , err := d . StateBlockTable . BulkSelectStateBlockEntries (
2022-02-04 10:39:34 +00:00
ctx , txn , stateBlockNIDs ,
2020-05-27 08:36:09 +00:00
)
2021-04-26 12:25:57 +00:00
if err != nil {
return nil , fmt . Errorf ( "d.StateBlockTable.BulkSelectStateBlockEntries: %w" , err )
}
lists := [ ] types . StateEntryList { }
for i , entry := range entries {
2022-02-04 10:39:34 +00:00
entries , err := d . EventsTable . BulkSelectStateEventByNID ( ctx , txn , entry , stateKeyTuples )
2021-04-26 12:25:57 +00:00
if err != nil {
return nil , fmt . Errorf ( "d.EventsTable.BulkSelectStateEventByNID: %w" , err )
}
lists = append ( lists , types . StateEntryList {
StateBlockNID : stateBlockNIDs [ i ] ,
StateEntries : entries ,
} )
}
return lists , nil
2020-05-27 08:36:09 +00:00
}
2020-09-01 11:40:49 +00:00
func ( d * Database ) RoomInfo ( ctx context . Context , roomID string ) ( * types . RoomInfo , error ) {
2022-02-04 10:39:34 +00:00
return d . roomInfo ( ctx , nil , roomID )
}
func ( d * Database ) roomInfo ( ctx context . Context , txn * sql . Tx , roomID string ) ( * types . RoomInfo , error ) {
2020-12-16 12:15:12 +00:00
if roomInfo , ok := d . Cache . GetRoomInfo ( roomID ) ; ok {
return & roomInfo , nil
}
2022-02-04 10:39:34 +00:00
roomInfo , err := d . RoomsTable . SelectRoomInfo ( ctx , txn , roomID )
2020-12-16 12:15:12 +00:00
if err == nil && roomInfo != nil {
d . Cache . StoreRoomServerRoomID ( roomInfo . RoomNID , roomID )
d . Cache . StoreRoomInfo ( roomID , * roomInfo )
}
return roomInfo , err
2020-09-01 11:40:49 +00:00
}
2020-05-27 08:36:09 +00:00
func ( d * Database ) AddState (
ctx context . Context ,
roomNID types . RoomNID ,
stateBlockNIDs [ ] types . StateBlockNID ,
state [ ] types . StateEntry ,
2022-02-04 10:39:34 +00:00
) ( stateNID types . StateSnapshotNID , err error ) {
return d . addState ( ctx , nil , roomNID , stateBlockNIDs , state )
}
func ( d * Database ) addState (
ctx context . Context , txn * sql . Tx ,
roomNID types . RoomNID ,
stateBlockNIDs [ ] types . StateBlockNID ,
state [ ] types . StateEntry ,
2020-05-27 08:36:09 +00:00
) ( stateNID types . StateSnapshotNID , err error ) {
2021-06-21 08:41:12 +00:00
if len ( stateBlockNIDs ) > 0 && len ( state ) > 0 {
2021-04-26 12:25:57 +00:00
// Check to see if the event already appears in any of the existing state
// blocks. If it does then we should not add it again, as this will just
// result in excess state blocks and snapshots.
// TODO: Investigate why this is happening - probably input_events.go!
2022-02-04 10:39:34 +00:00
blocks , berr := d . StateBlockTable . BulkSelectStateBlockEntries ( ctx , txn , stateBlockNIDs )
2021-04-26 12:25:57 +00:00
if berr != nil {
return 0 , fmt . Errorf ( "d.StateBlockTable.BulkSelectStateBlockEntries: %w" , berr )
}
2021-11-02 16:47:39 +00:00
var found bool
2021-04-26 12:25:57 +00:00
for i := len ( state ) - 1 ; i >= 0 ; i -- {
2021-11-02 16:47:39 +00:00
found = false
2021-04-26 12:25:57 +00:00
for _ , events := range blocks {
for _ , event := range events {
if state [ i ] . EventNID == event {
2021-11-02 16:47:39 +00:00
found = true
break
2021-04-26 12:25:57 +00:00
}
}
}
2021-11-02 16:47:39 +00:00
if found {
state = append ( state [ : i ] , state [ i + 1 : ] ... )
i --
}
2021-04-26 12:25:57 +00:00
}
}
2022-02-04 10:39:34 +00:00
err = d . Writer . Do ( d . DB , txn , func ( txn * sql . Tx ) error {
2020-05-27 08:36:09 +00:00
if len ( state ) > 0 {
2021-04-26 12:25:57 +00:00
// If there's any state left to add then let's add new blocks.
2020-05-27 08:36:09 +00:00
var stateBlockNID types . StateBlockNID
stateBlockNID , err = d . StateBlockTable . BulkInsertStateData ( ctx , txn , state )
if err != nil {
2020-08-19 14:38:27 +00:00
return fmt . Errorf ( "d.StateBlockTable.BulkInsertStateData: %w" , err )
2020-05-27 08:36:09 +00:00
}
stateBlockNIDs = append ( stateBlockNIDs [ : len ( stateBlockNIDs ) : len ( stateBlockNIDs ) ] , stateBlockNID )
}
stateNID , err = d . StateSnapshotTable . InsertState ( ctx , txn , roomNID , stateBlockNIDs )
2020-08-19 14:38:27 +00:00
if err != nil {
return fmt . Errorf ( "d.StateSnapshotTable.InsertState: %w" , err )
}
return nil
2020-05-27 08:36:09 +00:00
} )
if err != nil {
2020-08-19 14:38:27 +00:00
return 0 , fmt . Errorf ( "d.Writer.Do: %w" , err )
2020-05-27 08:36:09 +00:00
}
return
}
2020-05-26 15:45:28 +00:00
func ( d * Database ) EventNIDs (
ctx context . Context , eventIDs [ ] string ,
) ( map [ string ] types . EventNID , error ) {
2022-02-17 13:53:48 +00:00
return d . eventNIDs ( ctx , nil , eventIDs , NoFilter )
2022-02-04 10:39:34 +00:00
}
2022-02-17 13:53:48 +00:00
type UnsentFilter bool
const (
NoFilter UnsentFilter = false
FilterUnsentOnly UnsentFilter = true
)
2022-02-04 10:39:34 +00:00
func ( d * Database ) eventNIDs (
2022-02-17 13:53:48 +00:00
ctx context . Context , txn * sql . Tx , eventIDs [ ] string , filter UnsentFilter ,
2022-02-04 10:39:34 +00:00
) ( map [ string ] types . EventNID , error ) {
2022-02-17 13:53:48 +00:00
switch filter {
case FilterUnsentOnly :
return d . EventsTable . BulkSelectUnsentEventNID ( ctx , txn , eventIDs )
case NoFilter :
return d . EventsTable . BulkSelectEventNID ( ctx , txn , eventIDs )
default :
panic ( "impossible case" )
}
2020-05-26 15:45:28 +00:00
}
func ( d * Database ) SetState (
ctx context . Context , eventNID types . EventNID , stateNID types . StateSnapshotNID ,
) error {
2020-08-20 15:24:33 +00:00
return d . Writer . Do ( d . DB , nil , func ( txn * sql . Tx ) error {
return d . EventsTable . UpdateEventState ( ctx , txn , eventNID , stateNID )
2020-08-19 14:38:27 +00:00
} )
2020-05-26 15:45:28 +00:00
}
func ( d * Database ) StateAtEventIDs (
ctx context . Context , eventIDs [ ] string ,
) ( [ ] types . StateAtEvent , error ) {
2022-02-04 10:39:34 +00:00
return d . EventsTable . BulkSelectStateAtEventByID ( ctx , nil , eventIDs )
2020-05-26 15:45:28 +00:00
}
func ( d * Database ) SnapshotNIDFromEventID (
ctx context . Context , eventID string ,
) ( types . StateSnapshotNID , error ) {
2022-02-04 10:39:34 +00:00
return d . snapshotNIDFromEventID ( ctx , nil , eventID )
}
func ( d * Database ) snapshotNIDFromEventID (
ctx context . Context , txn * sql . Tx , eventID string ,
) ( types . StateSnapshotNID , error ) {
_ , stateNID , err := d . EventsTable . SelectEvent ( ctx , txn , eventID )
2020-05-26 15:45:28 +00:00
return stateNID , err
}
func ( d * Database ) EventIDs (
ctx context . Context , eventNIDs [ ] types . EventNID ,
) ( map [ types . EventNID ] string , error ) {
2022-02-04 10:39:34 +00:00
return d . EventsTable . BulkSelectEventID ( ctx , nil , eventNIDs )
2020-05-26 15:45:28 +00:00
}
2020-05-26 17:23:39 +00:00
func ( d * Database ) EventsFromIDs ( ctx context . Context , eventIDs [ ] string ) ( [ ] types . Event , error ) {
2022-02-17 13:53:48 +00:00
return d . eventsFromIDs ( ctx , nil , eventIDs , NoFilter )
2022-02-04 10:39:34 +00:00
}
2022-02-17 13:53:48 +00:00
func ( d * Database ) eventsFromIDs ( ctx context . Context , txn * sql . Tx , eventIDs [ ] string , filter UnsentFilter ) ( [ ] types . Event , error ) {
nidMap , err := d . eventNIDs ( ctx , txn , eventIDs , filter )
2020-05-26 17:23:39 +00:00
if err != nil {
return nil , err
}
var nids [ ] types . EventNID
for _ , nid := range nidMap {
nids = append ( nids , nid )
}
2022-02-04 10:39:34 +00:00
return d . events ( ctx , txn , nids )
2020-05-26 17:23:39 +00:00
}
func ( d * Database ) LatestEventIDs (
ctx context . Context , roomNID types . RoomNID ,
) ( references [ ] gomatrixserverlib . EventReference , currentStateSnapshotNID types . StateSnapshotNID , depth int64 , err error ) {
2020-08-20 08:24:52 +00:00
var eventNIDs [ ] types . EventNID
eventNIDs , currentStateSnapshotNID , err = d . RoomsTable . SelectLatestEventNIDs ( ctx , nil , roomNID )
if err != nil {
return
}
references , err = d . EventsTable . BulkSelectEventReference ( ctx , nil , eventNIDs )
if err != nil {
return
}
depth , err = d . EventsTable . SelectMaxEventDepth ( ctx , nil , eventNIDs )
if err != nil {
return
}
2020-05-26 17:23:39 +00:00
return
}
2020-05-27 08:36:09 +00:00
func ( d * Database ) StateBlockNIDs (
ctx context . Context , stateNIDs [ ] types . StateSnapshotNID ,
) ( [ ] types . StateBlockNIDList , error ) {
2022-02-04 10:39:34 +00:00
return d . stateBlockNIDs ( ctx , nil , stateNIDs )
}
func ( d * Database ) stateBlockNIDs (
ctx context . Context , txn * sql . Tx , stateNIDs [ ] types . StateSnapshotNID ,
) ( [ ] types . StateBlockNIDList , error ) {
return d . StateSnapshotTable . BulkSelectStateBlockNIDs ( ctx , txn , stateNIDs )
2020-05-27 08:36:09 +00:00
}
func ( d * Database ) StateEntries (
ctx context . Context , stateBlockNIDs [ ] types . StateBlockNID ,
2022-02-04 10:39:34 +00:00
) ( [ ] types . StateEntryList , error ) {
return d . stateEntries ( ctx , nil , stateBlockNIDs )
}
func ( d * Database ) stateEntries (
ctx context . Context , txn * sql . Tx , stateBlockNIDs [ ] types . StateBlockNID ,
2020-05-27 08:36:09 +00:00
) ( [ ] types . StateEntryList , error ) {
2021-04-26 12:25:57 +00:00
entries , err := d . StateBlockTable . BulkSelectStateBlockEntries (
2022-02-04 10:39:34 +00:00
ctx , txn , stateBlockNIDs ,
2021-04-26 12:25:57 +00:00
)
if err != nil {
return nil , fmt . Errorf ( "d.StateBlockTable.BulkSelectStateBlockEntries: %w" , err )
}
lists := make ( [ ] types . StateEntryList , 0 , len ( entries ) )
for i , entry := range entries {
2022-02-04 10:39:34 +00:00
eventNIDs , err := d . EventsTable . BulkSelectStateEventByNID ( ctx , txn , entry , nil )
2021-04-26 12:25:57 +00:00
if err != nil {
return nil , fmt . Errorf ( "d.EventsTable.BulkSelectStateEventByNID: %w" , err )
}
lists = append ( lists , types . StateEntryList {
StateBlockNID : stateBlockNIDs [ i ] ,
StateEntries : eventNIDs ,
} )
}
return lists , nil
2020-05-27 08:36:09 +00:00
}
func ( d * Database ) SetRoomAlias ( ctx context . Context , alias string , roomID string , creatorUserID string ) error {
2020-08-20 15:24:33 +00:00
return d . Writer . Do ( d . DB , nil , func ( txn * sql . Tx ) error {
return d . RoomAliasesTable . InsertRoomAlias ( ctx , txn , alias , roomID , creatorUserID )
2020-08-19 14:38:27 +00:00
} )
2020-05-27 08:36:09 +00:00
}
func ( d * Database ) GetRoomIDForAlias ( ctx context . Context , alias string ) ( string , error ) {
2022-02-04 10:39:34 +00:00
return d . RoomAliasesTable . SelectRoomIDFromAlias ( ctx , nil , alias )
2020-05-27 08:36:09 +00:00
}
func ( d * Database ) GetAliasesForRoomID ( ctx context . Context , roomID string ) ( [ ] string , error ) {
2022-02-04 10:39:34 +00:00
return d . RoomAliasesTable . SelectAliasesFromRoomID ( ctx , nil , roomID )
2020-05-27 08:36:09 +00:00
}
func ( d * Database ) GetCreatorIDForAlias (
ctx context . Context , alias string ,
) ( string , error ) {
2022-02-04 10:39:34 +00:00
return d . RoomAliasesTable . SelectCreatorIDFromAlias ( ctx , nil , alias )
2020-05-27 08:36:09 +00:00
}
func ( d * Database ) RemoveRoomAlias ( ctx context . Context , alias string ) error {
2020-08-20 15:24:33 +00:00
return d . Writer . Do ( d . DB , nil , func ( txn * sql . Tx ) error {
return d . RoomAliasesTable . DeleteRoomAlias ( ctx , txn , alias )
2020-08-19 14:38:27 +00:00
} )
2020-05-27 08:36:09 +00:00
}
2020-11-05 10:19:23 +00:00
func ( d * Database ) GetMembership ( ctx context . Context , roomNID types . RoomNID , requestSenderUserID string ) ( membershipEventNID types . EventNID , stillInRoom , isRoomforgotten bool , err error ) {
2020-08-19 14:38:27 +00:00
var requestSenderUserNID types . EventStateKeyNID
2020-08-20 15:24:33 +00:00
err = d . Writer . Do ( d . DB , nil , func ( txn * sql . Tx ) error {
requestSenderUserNID , err = d . assignStateKeyNID ( ctx , txn , requestSenderUserID )
2020-08-19 14:38:27 +00:00
return err
} )
2020-05-27 10:03:47 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return 0 , false , false , fmt . Errorf ( "d.assignStateKeyNID: %w" , err )
2020-05-27 10:03:47 +00:00
}
2020-11-05 10:19:23 +00:00
senderMembershipEventNID , senderMembership , isRoomforgotten , err :=
2020-05-27 10:03:47 +00:00
d . MembershipTable . SelectMembershipFromRoomAndTarget (
2022-02-04 10:39:34 +00:00
ctx , nil , roomNID , requestSenderUserNID ,
2020-05-27 10:03:47 +00:00
)
if err == sql . ErrNoRows {
// The user has never been a member of that room
2020-11-05 10:19:23 +00:00
return 0 , false , false , nil
2020-05-27 10:03:47 +00:00
} else if err != nil {
return
}
2020-11-05 10:19:23 +00:00
return senderMembershipEventNID , senderMembership == tables . MembershipStateJoin , isRoomforgotten , nil
2020-05-27 10:03:47 +00:00
}
func ( d * Database ) GetMembershipEventNIDsForRoom (
ctx context . Context , roomNID types . RoomNID , joinOnly bool , localOnly bool ,
2022-02-04 10:39:34 +00:00
) ( [ ] types . EventNID , error ) {
return d . getMembershipEventNIDsForRoom ( ctx , nil , roomNID , joinOnly , localOnly )
}
func ( d * Database ) getMembershipEventNIDsForRoom (
ctx context . Context , txn * sql . Tx , roomNID types . RoomNID , joinOnly bool , localOnly bool ,
2020-05-27 10:03:47 +00:00
) ( [ ] types . EventNID , error ) {
if joinOnly {
return d . MembershipTable . SelectMembershipsFromRoomAndMembership (
2022-02-04 10:39:34 +00:00
ctx , txn , roomNID , tables . MembershipStateJoin , localOnly ,
2020-05-27 10:03:47 +00:00
)
}
2022-02-04 10:39:34 +00:00
return d . MembershipTable . SelectMembershipsFromRoom ( ctx , txn , roomNID , localOnly )
2020-05-27 10:03:47 +00:00
}
func ( d * Database ) GetInvitesForUser (
ctx context . Context ,
roomNID types . RoomNID ,
targetUserNID types . EventStateKeyNID ,
2020-06-26 10:07:52 +00:00
) ( senderUserIDs [ ] types . EventStateKeyNID , eventIDs [ ] string , err error ) {
2022-02-04 10:39:34 +00:00
return d . InvitesTable . SelectInviteActiveForUserInRoom ( ctx , nil , targetUserNID , roomNID )
2020-05-27 10:03:47 +00:00
}
2020-05-26 17:23:39 +00:00
func ( d * Database ) Events (
ctx context . Context , eventNIDs [ ] types . EventNID ,
) ( [ ] types . Event , error ) {
2022-02-04 10:39:34 +00:00
return d . events ( ctx , nil , eventNIDs )
}
func ( d * Database ) events (
ctx context . Context , txn * sql . Tx , eventNIDs [ ] types . EventNID ,
) ( [ ] types . Event , error ) {
eventJSONs , err := d . EventJSONTable . BulkSelectEventJSON ( ctx , txn , eventNIDs )
2020-05-26 17:23:39 +00:00
if err != nil {
return nil , err
}
2022-02-04 10:39:34 +00:00
eventIDs , _ := d . EventsTable . BulkSelectEventID ( ctx , txn , eventNIDs )
2020-12-04 10:41:07 +00:00
if err != nil {
eventIDs = map [ types . EventNID ] string { }
}
2020-12-16 10:33:28 +00:00
var roomNIDs map [ types . EventNID ] types . RoomNID
2022-02-04 10:39:34 +00:00
roomNIDs , err = d . EventsTable . SelectRoomNIDsForEventNIDs ( ctx , txn , eventNIDs )
2020-12-16 10:33:28 +00:00
if err != nil {
return nil , err
}
uniqueRoomNIDs := make ( map [ types . RoomNID ] struct { } )
for _ , n := range roomNIDs {
uniqueRoomNIDs [ n ] = struct { } { }
}
2020-12-16 12:15:12 +00:00
roomVersions := make ( map [ types . RoomNID ] gomatrixserverlib . RoomVersion )
fetchNIDList := make ( [ ] types . RoomNID , 0 , len ( uniqueRoomNIDs ) )
2020-12-16 10:33:28 +00:00
for n := range uniqueRoomNIDs {
2020-12-16 12:15:12 +00:00
if roomID , ok := d . Cache . GetRoomServerRoomID ( n ) ; ok {
if roomInfo , ok := d . Cache . GetRoomInfo ( roomID ) ; ok {
roomVersions [ n ] = roomInfo . RoomVersion
continue
}
}
fetchNIDList = append ( fetchNIDList , n )
2020-12-16 10:33:28 +00:00
}
2022-02-04 10:39:34 +00:00
dbRoomVersions , err := d . RoomsTable . SelectRoomVersionsForRoomNIDs ( ctx , txn , fetchNIDList )
2020-12-16 10:33:28 +00:00
if err != nil {
return nil , err
}
2020-12-16 12:15:12 +00:00
for n , v := range dbRoomVersions {
roomVersions [ n ] = v
}
2020-05-26 17:23:39 +00:00
results := make ( [ ] types . Event , len ( eventJSONs ) )
for i , eventJSON := range eventJSONs {
result := & results [ i ]
result . EventNID = eventJSON . EventNID
2020-12-16 10:33:28 +00:00
roomNID := roomNIDs [ result . EventNID ]
roomVersion := roomVersions [ roomNID ]
2020-12-04 10:41:07 +00:00
result . Event , err = gomatrixserverlib . NewEventFromTrustedJSONWithEventID (
eventIDs [ eventJSON . EventNID ] , eventJSON . EventJSON , false , roomVersion ,
2020-05-26 17:23:39 +00:00
)
if err != nil {
return nil , err
}
}
2020-07-06 16:49:15 +00:00
if ! redactionsArePermanent {
d . applyRedactions ( results )
}
2020-05-26 17:23:39 +00:00
return results , nil
}
2020-05-28 10:15:21 +00:00
func ( d * Database ) MembershipUpdater (
ctx context . Context , roomID , targetUserID string ,
targetLocal bool , roomVersion gomatrixserverlib . RoomVersion ,
2020-08-19 12:24:54 +00:00
) ( * MembershipUpdater , error ) {
txn , err := d . DB . Begin ( )
if err != nil {
return nil , err
}
2020-08-20 08:24:52 +00:00
var updater * MembershipUpdater
_ = d . Writer . Do ( d . DB , txn , func ( txn * sql . Tx ) error {
updater , err = NewMembershipUpdater ( ctx , d , txn , roomID , targetUserID , targetLocal , roomVersion )
2020-09-10 13:39:18 +00:00
return err
2020-08-20 08:24:52 +00:00
} )
return updater , err
2020-05-28 10:15:21 +00:00
}
2022-02-04 10:39:34 +00:00
func ( d * Database ) GetRoomUpdater (
ctx context . Context , roomInfo * types . RoomInfo ,
) ( * RoomUpdater , error ) {
if d . GetRoomUpdaterFn != nil {
return d . GetRoomUpdaterFn ( ctx , roomInfo )
2020-10-20 18:32:33 +00:00
}
2020-08-19 12:24:54 +00:00
txn , err := d . DB . Begin ( )
if err != nil {
return nil , err
}
2022-02-04 10:39:34 +00:00
var updater * RoomUpdater
2020-08-20 08:24:52 +00:00
_ = d . Writer . Do ( d . DB , txn , func ( txn * sql . Tx ) error {
2022-02-04 10:39:34 +00:00
updater , err = NewRoomUpdater ( ctx , d , txn , roomInfo )
2020-09-10 13:39:18 +00:00
return err
2020-08-20 08:24:52 +00:00
} )
return updater , err
2020-05-28 10:15:21 +00:00
}
2020-05-26 17:23:39 +00:00
func ( d * Database ) StoreEvent (
2020-11-16 15:44:53 +00:00
ctx context . Context , event * gomatrixserverlib . Event ,
2021-11-22 09:13:12 +00:00
authEventNIDs [ ] types . EventNID , isRejected bool ,
2022-02-04 10:39:34 +00:00
) ( types . EventNID , types . RoomNID , types . StateAtEvent , * gomatrixserverlib . Event , string , error ) {
return d . storeEvent ( ctx , nil , event , authEventNIDs , isRejected )
}
func ( d * Database ) storeEvent (
ctx context . Context , updater * RoomUpdater , event * gomatrixserverlib . Event ,
authEventNIDs [ ] types . EventNID , isRejected bool ,
2021-12-09 15:03:26 +00:00
) ( types . EventNID , types . RoomNID , types . StateAtEvent , * gomatrixserverlib . Event , string , error ) {
2020-05-26 17:23:39 +00:00
var (
roomNID types . RoomNID
eventTypeNID types . EventTypeNID
eventStateKeyNID types . EventStateKeyNID
eventNID types . EventNID
stateNID types . StateSnapshotNID
2020-07-07 11:51:55 +00:00
redactionEvent * gomatrixserverlib . Event
redactedEventID string
2020-05-26 17:23:39 +00:00
err error
)
2022-02-04 10:39:34 +00:00
var txn * sql . Tx
2022-02-11 17:40:14 +00:00
if updater != nil && updater . txn != nil {
2022-02-04 10:39:34 +00:00
txn = updater . txn
}
err = d . Writer . Do ( d . DB , txn , func ( txn * sql . Tx ) error {
2020-05-26 17:23:39 +00:00
// TODO: Here we should aim to have two different code paths for new rooms
// vs existing ones.
// Get the default room version. If the client doesn't supply a room_version
// then we will use our configured default to create the room.
// https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-createroom
// Note that the below logic depends on the m.room.create event being the
// first event that is persisted to the database when creating or joining a
// room.
var roomVersion gomatrixserverlib . RoomVersion
if roomVersion , err = extractRoomVersionFromCreateEvent ( event ) ; err != nil {
2020-07-21 14:48:21 +00:00
return fmt . Errorf ( "extractRoomVersionFromCreateEvent: %w" , err )
2020-05-26 17:23:39 +00:00
}
if roomNID , err = d . assignRoomNID ( ctx , txn , event . RoomID ( ) , roomVersion ) ; err != nil {
2020-07-21 14:48:21 +00:00
return fmt . Errorf ( "d.assignRoomNID: %w" , err )
2020-05-26 17:23:39 +00:00
}
if eventTypeNID , err = d . assignEventTypeNID ( ctx , txn , event . Type ( ) ) ; err != nil {
2020-07-21 14:48:21 +00:00
return fmt . Errorf ( "d.assignEventTypeNID: %w" , err )
2020-05-26 17:23:39 +00:00
}
eventStateKey := event . StateKey ( )
// Assigned a numeric ID for the state_key if there is one present.
// Otherwise set the numeric ID for the state_key to 0.
if eventStateKey != nil {
if eventStateKeyNID , err = d . assignStateKeyNID ( ctx , txn , * eventStateKey ) ; err != nil {
2020-07-21 14:48:21 +00:00
return fmt . Errorf ( "d.assignStateKeyNID: %w" , err )
2020-05-26 17:23:39 +00:00
}
}
if eventNID , stateNID , err = d . EventsTable . InsertEvent (
ctx ,
txn ,
roomNID ,
eventTypeNID ,
eventStateKeyNID ,
event . EventID ( ) ,
event . EventReference ( ) . EventSHA256 ,
authEventNIDs ,
event . Depth ( ) ,
2020-09-16 12:00:52 +00:00
isRejected ,
2020-05-26 17:23:39 +00:00
) ; err != nil {
if err == sql . ErrNoRows {
// We've already inserted the event so select the numeric event ID
eventNID , stateNID , err = d . EventsTable . SelectEvent ( ctx , txn , event . EventID ( ) )
2022-02-10 10:05:14 +00:00
} else if err != nil {
return fmt . Errorf ( "d.EventsTable.InsertEvent: %w" , err )
2020-05-26 17:23:39 +00:00
}
if err != nil {
2020-10-15 13:14:17 +00:00
return fmt . Errorf ( "d.EventsTable.SelectEvent: %w" , err )
2020-05-26 17:23:39 +00:00
}
}
if err = d . EventJSONTable . InsertEventJSON ( ctx , txn , eventNID , event . JSON ( ) ) ; err != nil {
2020-07-21 14:48:21 +00:00
return fmt . Errorf ( "d.EventJSONTable.InsertEventJSON: %w" , err )
2020-05-26 17:23:39 +00:00
}
2020-09-16 12:00:52 +00:00
if ! isRejected { // ignore rejected redaction events
redactionEvent , redactedEventID , err = d . handleRedactions ( ctx , txn , eventNID , event )
2020-10-15 13:14:17 +00:00
if err != nil {
return fmt . Errorf ( "d.handleRedactions: %w" , err )
}
2020-09-16 12:00:52 +00:00
}
2020-07-21 14:48:21 +00:00
return nil
2020-05-26 17:23:39 +00:00
} )
if err != nil {
2021-12-09 15:03:26 +00:00
return 0 , 0 , types . StateAtEvent { } , nil , "" , fmt . Errorf ( "d.Writer.Do: %w" , err )
2020-05-26 17:23:39 +00:00
}
2020-10-07 13:05:33 +00:00
// We should attempt to update the previous events table with any
// references that this new event makes. We do this using a latest
// events updater because it somewhat works as a mutex, ensuring
// that there's a row-level lock on the latest room events (well,
// on Postgres at least).
if prevEvents := event . PrevEvents ( ) ; len ( prevEvents ) > 0 {
2020-10-20 10:42:54 +00:00
// Create an updater - NB: on sqlite this WILL create a txn as we are directly calling the shared DB form of
// GetLatestEventsForUpdate - not via the SQLiteDatabase form which has `nil` txns. This
// function only does SELECTs though so the created txn (at this point) is just a read txn like
// any other so this is fine. If we ever update GetLatestEventsForUpdate or NewLatestEventsUpdater
// to do writes however then this will need to go inside `Writer.Do`.
2022-02-04 10:39:34 +00:00
succeeded := false
if updater == nil {
var roomInfo * types . RoomInfo
roomInfo , err = d . RoomInfo ( ctx , event . RoomID ( ) )
if err != nil {
return 0 , 0 , types . StateAtEvent { } , nil , "" , fmt . Errorf ( "d.RoomInfo: %w" , err )
2020-10-20 10:42:54 +00:00
}
2022-02-04 10:39:34 +00:00
if roomInfo == nil && len ( prevEvents ) > 0 {
return 0 , 0 , types . StateAtEvent { } , nil , "" , fmt . Errorf ( "expected room %q to exist" , event . RoomID ( ) )
}
updater , err = d . GetRoomUpdater ( ctx , roomInfo )
if err != nil {
return 0 , 0 , types . StateAtEvent { } , nil , "" , fmt . Errorf ( "GetRoomUpdater: %w" , err )
}
defer sqlutil . EndTransactionWithCheck ( updater , & succeeded , & err )
}
if err = updater . StorePreviousEvents ( eventNID , prevEvents ) ; err != nil {
return 0 , 0 , types . StateAtEvent { } , nil , "" , fmt . Errorf ( "updater.StorePreviousEvents: %w" , err )
2020-10-07 13:05:33 +00:00
}
2022-02-04 10:39:34 +00:00
succeeded = true
2020-10-07 13:05:33 +00:00
}
2021-12-09 15:03:26 +00:00
return eventNID , roomNID , types . StateAtEvent {
2020-05-26 17:23:39 +00:00
BeforeStateSnapshotNID : stateNID ,
StateEntry : types . StateEntry {
StateKeyTuple : types . StateKeyTuple {
EventTypeNID : eventTypeNID ,
EventStateKeyNID : eventStateKeyNID ,
} ,
EventNID : eventNID ,
} ,
2020-10-07 13:05:33 +00:00
} , redactionEvent , redactedEventID , err
2020-05-26 17:23:39 +00:00
}
2020-07-02 14:41:18 +00:00
func ( d * Database ) PublishRoom ( ctx context . Context , roomID string , publish bool ) error {
2020-08-20 15:24:33 +00:00
return d . Writer . Do ( d . DB , nil , func ( txn * sql . Tx ) error {
return d . PublishedTable . UpsertRoomPublished ( ctx , txn , roomID , publish )
2020-08-19 14:38:27 +00:00
} )
2020-07-02 14:41:18 +00:00
}
func ( d * Database ) GetPublishedRooms ( ctx context . Context ) ( [ ] string , error ) {
2022-02-04 10:39:34 +00:00
return d . PublishedTable . SelectAllPublishedRooms ( ctx , nil , true )
2020-07-02 14:41:18 +00:00
}
2022-02-23 15:41:32 +00:00
func ( d * Database ) MissingAuthPrevEvents (
ctx context . Context , e * gomatrixserverlib . Event ,
) ( missingAuth , missingPrev [ ] string , err error ) {
authEventNIDs , err := d . EventNIDs ( ctx , e . AuthEventIDs ( ) )
if err != nil {
return nil , nil , fmt . Errorf ( "d.EventNIDs: %w" , err )
}
for _ , authEventID := range e . AuthEventIDs ( ) {
if _ , ok := authEventNIDs [ authEventID ] ; ! ok {
missingAuth = append ( missingAuth , authEventID )
}
}
for _ , prevEventID := range e . PrevEventIDs ( ) {
state , err := d . StateAtEventIDs ( ctx , [ ] string { prevEventID } )
if err != nil || len ( state ) == 0 || ( ! state [ 0 ] . IsCreate ( ) && state [ 0 ] . BeforeStateSnapshotNID == 0 ) {
missingPrev = append ( missingPrev , prevEventID )
}
}
return
}
2020-05-26 17:23:39 +00:00
func ( d * Database ) assignRoomNID (
ctx context . Context , txn * sql . Tx ,
roomID string , roomVersion gomatrixserverlib . RoomVersion ,
) ( types . RoomNID , error ) {
2020-12-16 12:15:12 +00:00
if roomInfo , ok := d . Cache . GetRoomInfo ( roomID ) ; ok {
return roomInfo . RoomNID , nil
2020-08-25 11:32:29 +00:00
}
2020-05-26 17:23:39 +00:00
// Check if we already have a numeric ID in the database.
roomNID , err := d . RoomsTable . SelectRoomNID ( ctx , txn , roomID )
if err == sql . ErrNoRows {
// We don't have a numeric ID so insert one into the database.
roomNID , err = d . RoomsTable . InsertRoomNID ( ctx , txn , roomID , roomVersion )
if err == sql . ErrNoRows {
// We raced with another insert so run the select again.
roomNID , err = d . RoomsTable . SelectRoomNID ( ctx , txn , roomID )
}
}
return roomNID , err
}
func ( d * Database ) assignEventTypeNID (
ctx context . Context , txn * sql . Tx , eventType string ,
2020-08-25 11:32:29 +00:00
) ( types . EventTypeNID , error ) {
2020-05-26 17:23:39 +00:00
// Check if we already have a numeric ID in the database.
2020-08-25 11:32:29 +00:00
eventTypeNID , err := d . EventTypesTable . SelectEventTypeNID ( ctx , txn , eventType )
2020-05-26 17:23:39 +00:00
if err == sql . ErrNoRows {
// We don't have a numeric ID so insert one into the database.
eventTypeNID , err = d . EventTypesTable . InsertEventTypeNID ( ctx , txn , eventType )
if err == sql . ErrNoRows {
// We raced with another insert so run the select again.
eventTypeNID , err = d . EventTypesTable . SelectEventTypeNID ( ctx , txn , eventType )
}
}
2020-08-25 11:32:29 +00:00
return eventTypeNID , err
2020-05-26 17:23:39 +00:00
}
func ( d * Database ) assignStateKeyNID (
ctx context . Context , txn * sql . Tx , eventStateKey string ,
) ( types . EventStateKeyNID , error ) {
// Check if we already have a numeric ID in the database.
eventStateKeyNID , err := d . EventStateKeysTable . SelectEventStateKeyNID ( ctx , txn , eventStateKey )
if err == sql . ErrNoRows {
// We don't have a numeric ID so insert one into the database.
eventStateKeyNID , err = d . EventStateKeysTable . InsertEventStateKeyNID ( ctx , txn , eventStateKey )
if err == sql . ErrNoRows {
// We raced with another insert so run the select again.
eventStateKeyNID , err = d . EventStateKeysTable . SelectEventStateKeyNID ( ctx , txn , eventStateKey )
}
}
return eventStateKeyNID , err
}
2020-11-16 15:44:53 +00:00
func extractRoomVersionFromCreateEvent ( event * gomatrixserverlib . Event ) (
2020-05-26 17:23:39 +00:00
gomatrixserverlib . RoomVersion , error ,
) {
var err error
var roomVersion gomatrixserverlib . RoomVersion
// Look for m.room.create events.
if event . Type ( ) != gomatrixserverlib . MRoomCreate {
return gomatrixserverlib . RoomVersion ( "" ) , nil
}
roomVersion = gomatrixserverlib . RoomVersionV1
var createContent gomatrixserverlib . CreateContent
// The m.room.create event contains an optional "room_version" key in
// the event content, so we need to unmarshal that first.
if err = json . Unmarshal ( event . Content ( ) , & createContent ) ; err != nil {
return gomatrixserverlib . RoomVersion ( "" ) , err
}
// A room version was specified in the event content?
if createContent . RoomVersion != nil {
roomVersion = gomatrixserverlib . RoomVersion ( * createContent . RoomVersion )
}
return roomVersion , err
}
2020-07-06 16:49:15 +00:00
// handleRedactions manages the redacted status of events. There's two cases to consider in order to comply with the spec:
// "servers should not apply or send redactions to clients until both the redaction event and original event have been seen, and are valid."
// https://matrix.org/docs/spec/rooms/v3#authorization-rules-for-events
// These cases are:
// - This is a redaction event, redact the event it references if we know about it.
// - This is a normal event which may have been previously redacted.
// In the first case, check if we have the referenced event then apply the redaction, else store it
// in the redactions table with validated=FALSE. In the second case, check if there is a redaction for it:
// if there is then apply the redactions and set validated=TRUE.
//
// When an event is redacted, the redacted event JSON is modified to add an `unsigned.redacted_because` field. We use this field
// when loading events to determine whether to apply redactions. This keeps the hot-path of reading events quick as we don't need
// to cross-reference with other tables when loading.
2020-07-07 11:51:55 +00:00
//
// Returns the redaction event and the event ID of the redacted event if this call resulted in a redaction.
func ( d * Database ) handleRedactions (
2020-11-16 15:44:53 +00:00
ctx context . Context , txn * sql . Tx , eventNID types . EventNID , event * gomatrixserverlib . Event ,
2020-07-07 11:51:55 +00:00
) ( * gomatrixserverlib . Event , string , error ) {
var err error
isRedactionEvent := event . Type ( ) == gomatrixserverlib . MRoomRedaction && event . StateKey ( ) == nil
if isRedactionEvent {
// an event which redacts itself should be ignored
if event . EventID ( ) == event . Redacts ( ) {
return nil , "" , nil
}
err = d . RedactionsTable . InsertRedaction ( ctx , txn , tables . RedactionInfo {
Validated : false ,
RedactionEventID : event . EventID ( ) ,
RedactsEventID : event . Redacts ( ) ,
} )
if err != nil {
2020-10-15 13:14:17 +00:00
return nil , "" , fmt . Errorf ( "d.RedactionsTable.InsertRedaction: %w" , err )
2020-07-07 11:51:55 +00:00
}
}
2020-07-06 16:49:15 +00:00
redactionEvent , redactedEvent , validated , err := d . loadRedactionPair ( ctx , txn , eventNID , event )
if err != nil {
2020-10-15 13:14:17 +00:00
return nil , "" , fmt . Errorf ( "d.loadRedactionPair: %w" , err )
2020-07-06 16:49:15 +00:00
}
if validated || redactedEvent == nil || redactionEvent == nil {
// we've seen this redaction before or there is nothing to redact
2020-07-07 11:51:55 +00:00
return nil , "" , nil
2020-07-06 16:49:15 +00:00
}
2020-07-08 16:45:39 +00:00
if redactedEvent . RoomID ( ) != redactionEvent . RoomID ( ) {
// redactions across rooms aren't allowed
return nil , "" , nil
}
2020-07-06 16:49:15 +00:00
// mark the event as redacted
err = redactedEvent . SetUnsignedField ( "redacted_because" , redactionEvent )
if err != nil {
2020-10-15 13:14:17 +00:00
return nil , "" , fmt . Errorf ( "redactedEvent.SetUnsignedField: %w" , err )
2020-07-06 16:49:15 +00:00
}
if redactionsArePermanent {
redactedEvent . Event = redactedEvent . Redact ( )
}
// overwrite the eventJSON table
err = d . EventJSONTable . InsertEventJSON ( ctx , txn , redactedEvent . EventNID , redactedEvent . JSON ( ) )
if err != nil {
2020-10-15 13:14:17 +00:00
return nil , "" , fmt . Errorf ( "d.EventJSONTable.InsertEventJSON: %w" , err )
}
err = d . RedactionsTable . MarkRedactionValidated ( ctx , txn , redactionEvent . EventID ( ) , true )
if err != nil {
err = fmt . Errorf ( "d.RedactionsTable.MarkRedactionValidated: %w" , err )
2020-07-06 16:49:15 +00:00
}
2020-11-16 15:44:53 +00:00
return redactionEvent . Event , redactedEvent . EventID ( ) , err
2020-07-06 16:49:15 +00:00
}
// loadRedactionPair returns both the redaction event and the redacted event, else nil.
func ( d * Database ) loadRedactionPair (
2020-11-16 15:44:53 +00:00
ctx context . Context , txn * sql . Tx , eventNID types . EventNID , event * gomatrixserverlib . Event ,
2020-07-06 16:49:15 +00:00
) ( * types . Event , * types . Event , bool , error ) {
var redactionEvent , redactedEvent * types . Event
var info * tables . RedactionInfo
var err error
isRedactionEvent := event . Type ( ) == gomatrixserverlib . MRoomRedaction && event . StateKey ( ) == nil
2020-07-07 11:51:55 +00:00
var eventBeingRedacted string
2020-07-06 16:49:15 +00:00
if isRedactionEvent {
2020-07-07 11:51:55 +00:00
eventBeingRedacted = event . Redacts ( )
2020-07-06 16:49:15 +00:00
redactionEvent = & types . Event {
EventNID : eventNID ,
Event : event ,
}
} else {
2020-07-07 11:51:55 +00:00
eventBeingRedacted = event . EventID ( ) // maybe, we'll see if we have info
2020-07-06 16:49:15 +00:00
redactedEvent = & types . Event {
EventNID : eventNID ,
Event : event ,
}
}
2020-07-07 11:51:55 +00:00
info , err = d . RedactionsTable . SelectRedactionInfoByEventBeingRedacted ( ctx , txn , eventBeingRedacted )
if err != nil {
return nil , nil , false , err
}
if info == nil {
// this event hasn't been redacted or we don't have the redaction for it yet
return nil , nil , false , nil
}
if isRedactionEvent {
redactedEvent = d . loadEvent ( ctx , info . RedactsEventID )
} else {
redactionEvent = d . loadEvent ( ctx , info . RedactionEventID )
}
2020-07-06 16:49:15 +00:00
return redactionEvent , redactedEvent , info . Validated , nil
}
// applyRedactions will redact events that have an `unsigned.redacted_because` field.
func ( d * Database ) applyRedactions ( events [ ] types . Event ) {
for i := range events {
if result := gjson . GetBytes ( events [ i ] . Unsigned ( ) , "redacted_because" ) ; result . Exists ( ) {
events [ i ] . Event = events [ i ] . Redact ( )
}
}
}
2020-07-07 11:51:55 +00:00
// loadEvent loads a single event or returns nil on any problems/missing event
func ( d * Database ) loadEvent ( ctx context . Context , eventID string ) * types . Event {
nids , err := d . EventNIDs ( ctx , [ ] string { eventID } )
if err != nil {
return nil
}
if len ( nids ) == 0 {
return nil
}
evs , err := d . Events ( ctx , [ ] types . EventNID { nids [ eventID ] } )
if err != nil {
return nil
}
if len ( evs ) != 1 {
return nil
}
return & evs [ 0 ]
}
2020-09-03 16:20:54 +00:00
// GetStateEvent returns the current state event of a given type for a given room with a given state key
// If no event could be found, returns nil
// If there was an issue during the retrieval, returns an error
func ( d * Database ) GetStateEvent ( ctx context . Context , roomID , evType , stateKey string ) ( * gomatrixserverlib . HeaderedEvent , error ) {
2020-09-03 17:27:02 +00:00
roomInfo , err := d . RoomInfo ( ctx , roomID )
if err != nil {
return nil , err
}
2022-01-21 14:23:37 +00:00
if roomInfo == nil {
2021-07-21 15:53:50 +00:00
return nil , fmt . Errorf ( "room %s doesn't exist" , roomID )
}
2022-01-21 14:23:37 +00:00
// e.g invited rooms
if roomInfo . IsStub {
return nil , nil
}
2020-09-03 17:27:02 +00:00
eventTypeNID , err := d . EventTypesTable . SelectEventTypeNID ( ctx , nil , evType )
2020-09-04 18:40:21 +00:00
if err == sql . ErrNoRows {
// No rooms have an event of this type, otherwise we'd have an event type NID
return nil , nil
}
2020-09-03 17:27:02 +00:00
if err != nil {
return nil , err
}
stateKeyNID , err := d . EventStateKeysTable . SelectEventStateKeyNID ( ctx , nil , stateKey )
2021-07-19 17:33:05 +00:00
if err == sql . ErrNoRows {
// No rooms have a state event with this state key, otherwise we'd have an state key NID
return nil , nil
}
2020-09-03 17:27:02 +00:00
if err != nil {
return nil , err
}
entries , err := d . loadStateAtSnapshot ( ctx , roomInfo . StateSnapshotNID )
if err != nil {
return nil , err
}
2020-12-04 10:41:07 +00:00
var eventNIDs [ ] types . EventNID
for _ , e := range entries {
if e . EventTypeNID == eventTypeNID && e . EventStateKeyNID == stateKeyNID {
eventNIDs = append ( eventNIDs , e . EventNID )
}
}
2022-02-04 10:39:34 +00:00
eventIDs , _ := d . EventsTable . BulkSelectEventID ( ctx , nil , eventNIDs )
2020-12-04 10:41:07 +00:00
if err != nil {
eventIDs = map [ types . EventNID ] string { }
}
2020-09-03 17:27:02 +00:00
// return the event requested
for _ , e := range entries {
if e . EventTypeNID == eventTypeNID && e . EventStateKeyNID == stateKeyNID {
2022-02-04 10:39:34 +00:00
data , err := d . EventJSONTable . BulkSelectEventJSON ( ctx , nil , [ ] types . EventNID { e . EventNID } )
2020-09-03 17:27:02 +00:00
if err != nil {
return nil , err
}
if len ( data ) == 0 {
return nil , fmt . Errorf ( "GetStateEvent: no json for event nid %d" , e . EventNID )
}
2020-12-04 10:41:07 +00:00
ev , err := gomatrixserverlib . NewEventFromTrustedJSONWithEventID ( eventIDs [ e . EventNID ] , data [ 0 ] . EventJSON , false , roomInfo . RoomVersion )
2020-09-03 17:27:02 +00:00
if err != nil {
return nil , err
}
2020-11-16 15:44:53 +00:00
return ev . Headered ( roomInfo . RoomVersion ) , nil
2020-09-03 16:20:54 +00:00
}
2020-09-03 17:27:02 +00:00
}
2020-09-04 18:40:21 +00:00
return nil , nil
2020-09-03 16:20:54 +00:00
}
// GetRoomsByMembership returns a list of room IDs matching the provided membership and user ID (as state_key).
func ( d * Database ) GetRoomsByMembership ( ctx context . Context , userID , membership string ) ( [ ] string , error ) {
var membershipState tables . MembershipState
switch membership {
case "join" :
membershipState = tables . MembershipStateJoin
case "invite" :
membershipState = tables . MembershipStateInvite
case "leave" :
membershipState = tables . MembershipStateLeaveOrBan
case "ban" :
membershipState = tables . MembershipStateLeaveOrBan
default :
return nil , fmt . Errorf ( "GetRoomsByMembership: invalid membership %s" , membership )
}
stateKeyNID , err := d . EventStateKeysTable . SelectEventStateKeyNID ( ctx , nil , userID )
if err != nil {
2020-09-04 13:25:01 +00:00
if err == sql . ErrNoRows {
return nil , nil
}
2020-09-03 16:20:54 +00:00
return nil , fmt . Errorf ( "GetRoomsByMembership: cannot map user ID to state key NID: %w" , err )
}
2022-02-04 10:39:34 +00:00
roomNIDs , err := d . MembershipTable . SelectRoomsWithMembership ( ctx , nil , stateKeyNID , membershipState )
2020-09-03 16:20:54 +00:00
if err != nil {
2020-09-04 13:25:01 +00:00
return nil , fmt . Errorf ( "GetRoomsByMembership: failed to SelectRoomsWithMembership: %w" , err )
2020-09-03 16:20:54 +00:00
}
2022-02-04 10:39:34 +00:00
roomIDs , err := d . RoomsTable . BulkSelectRoomIDs ( ctx , nil , roomNIDs )
2020-09-03 16:20:54 +00:00
if err != nil {
2020-09-04 13:25:01 +00:00
return nil , fmt . Errorf ( "GetRoomsByMembership: failed to lookup room nids: %w" , err )
2020-09-03 16:20:54 +00:00
}
if len ( roomIDs ) != len ( roomNIDs ) {
return nil , fmt . Errorf ( "GetRoomsByMembership: missing room IDs, got %d want %d" , len ( roomIDs ) , len ( roomNIDs ) )
}
return roomIDs , nil
}
// GetBulkStateContent returns all state events which match a given room ID and a given state key tuple. Both must be satisfied for a match.
// If a tuple has the StateKey of '*' and allowWildcards=true then all state events with the EventType should be returned.
2020-09-07 11:38:09 +00:00
func ( d * Database ) GetBulkStateContent ( ctx context . Context , roomIDs [ ] string , tuples [ ] gomatrixserverlib . StateKeyTuple , allowWildcards bool ) ( [ ] tables . StrippedEvent , error ) {
eventTypes := make ( [ ] string , 0 , len ( tuples ) )
for _ , tuple := range tuples {
eventTypes = append ( eventTypes , tuple . EventType )
}
// we don't bother failing the request if we get asked for event types we don't know about, as all that would result in is no matches which
// isn't a failure.
2022-02-04 10:39:34 +00:00
eventTypeNIDMap , err := d . EventTypesTable . BulkSelectEventTypeNID ( ctx , nil , eventTypes )
2020-09-07 11:38:09 +00:00
if err != nil {
return nil , fmt . Errorf ( "GetBulkStateContent: failed to map event type nids: %w" , err )
}
typeNIDSet := make ( map [ types . EventTypeNID ] bool )
for _ , nid := range eventTypeNIDMap {
typeNIDSet [ nid ] = true
}
allowWildcard := make ( map [ types . EventTypeNID ] bool )
eventStateKeys := make ( [ ] string , 0 , len ( tuples ) )
for _ , tuple := range tuples {
if allowWildcards && tuple . StateKey == "*" {
allowWildcard [ eventTypeNIDMap [ tuple . EventType ] ] = true
continue
}
eventStateKeys = append ( eventStateKeys , tuple . StateKey )
}
2022-02-04 10:39:34 +00:00
eventStateKeyNIDMap , err := d . EventStateKeysTable . BulkSelectEventStateKeyNID ( ctx , nil , eventStateKeys )
2020-09-07 11:38:09 +00:00
if err != nil {
return nil , fmt . Errorf ( "GetBulkStateContent: failed to map state key nids: %w" , err )
}
stateKeyNIDSet := make ( map [ types . EventStateKeyNID ] bool )
for _ , nid := range eventStateKeyNIDMap {
stateKeyNIDSet [ nid ] = true
}
var eventNIDs [ ] types . EventNID
eventNIDToVer := make ( map [ types . EventNID ] gomatrixserverlib . RoomVersion )
// TODO: This feels like this is going to be really slow...
for _ , roomID := range roomIDs {
roomInfo , err2 := d . RoomInfo ( ctx , roomID )
if err2 != nil {
return nil , fmt . Errorf ( "GetBulkStateContent: failed to load room info for room %s : %w" , roomID , err2 )
}
// for unknown rooms or rooms which we don't have the current state, skip them.
if roomInfo == nil || roomInfo . IsStub {
continue
}
entries , err2 := d . loadStateAtSnapshot ( ctx , roomInfo . StateSnapshotNID )
if err2 != nil {
return nil , fmt . Errorf ( "GetBulkStateContent: failed to load state for room %s : %w" , roomID , err2 )
}
for _ , entry := range entries {
if typeNIDSet [ entry . EventTypeNID ] {
if allowWildcard [ entry . EventTypeNID ] || stateKeyNIDSet [ entry . EventStateKeyNID ] {
eventNIDs = append ( eventNIDs , entry . EventNID )
eventNIDToVer [ entry . EventNID ] = roomInfo . RoomVersion
}
}
}
}
2022-02-04 10:39:34 +00:00
eventIDs , _ := d . EventsTable . BulkSelectEventID ( ctx , nil , eventNIDs )
2020-12-04 10:41:07 +00:00
if err != nil {
eventIDs = map [ types . EventNID ] string { }
}
2022-02-04 10:39:34 +00:00
events , err := d . EventJSONTable . BulkSelectEventJSON ( ctx , nil , eventNIDs )
2020-09-07 11:38:09 +00:00
if err != nil {
return nil , fmt . Errorf ( "GetBulkStateContent: failed to load event JSON for event nids: %w" , err )
}
result := make ( [ ] tables . StrippedEvent , len ( events ) )
for i := range events {
roomVer := eventNIDToVer [ events [ i ] . EventNID ]
2020-12-04 10:41:07 +00:00
ev , err := gomatrixserverlib . NewEventFromTrustedJSONWithEventID ( eventIDs [ events [ i ] . EventNID ] , events [ i ] . EventJSON , false , roomVer )
2020-09-07 11:38:09 +00:00
if err != nil {
return nil , fmt . Errorf ( "GetBulkStateContent: failed to load event JSON for event NID %v : %w" , events [ i ] . EventNID , err )
}
result [ i ] = tables . StrippedEvent {
EventType : ev . Type ( ) ,
RoomID : ev . RoomID ( ) ,
StateKey : * ev . StateKey ( ) ,
2020-11-16 15:44:53 +00:00
ContentValue : tables . ExtractContentValue ( ev . Headered ( roomVer ) ) ,
2020-09-07 11:38:09 +00:00
}
}
return result , nil
2020-09-03 16:20:54 +00:00
}
2022-03-01 13:01:38 +00:00
// JoinedUsersSetInRooms returns a map of how many times the given users appear in the specified rooms.
func ( d * Database ) JoinedUsersSetInRooms ( ctx context . Context , roomIDs , userIDs [ ] string ) ( map [ string ] int , error ) {
2022-02-04 10:39:34 +00:00
roomNIDs , err := d . RoomsTable . BulkSelectRoomNIDs ( ctx , nil , roomIDs )
2020-09-03 17:27:02 +00:00
if err != nil {
return nil , err
}
2022-03-01 13:01:38 +00:00
userNIDsMap , err := d . EventStateKeysTable . BulkSelectEventStateKeyNID ( ctx , nil , userIDs )
if err != nil {
return nil , err
}
userNIDs := make ( [ ] types . EventStateKeyNID , 0 , len ( userNIDsMap ) )
nidToUserID := make ( map [ types . EventStateKeyNID ] string , len ( userNIDsMap ) )
for id , nid := range userNIDsMap {
userNIDs = append ( userNIDs , nid )
nidToUserID [ nid ] = id
}
userNIDToCount , err := d . MembershipTable . SelectJoinedUsersSetForRooms ( ctx , nil , roomNIDs , userNIDs )
2020-09-03 17:27:02 +00:00
if err != nil {
return nil , err
}
stateKeyNIDs := make ( [ ] types . EventStateKeyNID , len ( userNIDToCount ) )
i := 0
for nid := range userNIDToCount {
stateKeyNIDs [ i ] = nid
i ++
}
if len ( nidToUserID ) != len ( userNIDToCount ) {
2022-02-22 13:40:08 +00:00
logrus . Warnf ( "SelectJoinedUsersSetForRooms found %d users but BulkSelectEventStateKey only returned state key NIDs for %d of them" , len ( userNIDToCount ) , len ( nidToUserID ) )
2020-09-03 17:27:02 +00:00
}
result := make ( map [ string ] int , len ( userNIDToCount ) )
for nid , count := range userNIDToCount {
result [ nidToUserID [ nid ] ] = count
}
return result , nil
2020-09-03 16:20:54 +00:00
}
2021-07-09 15:36:45 +00:00
// GetLocalServerInRoom returns true if we think we're in a given room or false otherwise.
func ( d * Database ) GetLocalServerInRoom ( ctx context . Context , roomNID types . RoomNID ) ( bool , error ) {
2022-02-04 10:39:34 +00:00
return d . MembershipTable . SelectLocalServerInRoom ( ctx , nil , roomNID )
2021-07-09 15:36:45 +00:00
}
2021-07-21 12:06:32 +00:00
// GetServerInRoom returns true if we think a server is in a given room or false otherwise.
func ( d * Database ) GetServerInRoom ( ctx context . Context , roomNID types . RoomNID , serverName gomatrixserverlib . ServerName ) ( bool , error ) {
2022-02-04 10:39:34 +00:00
return d . MembershipTable . SelectServerInRoom ( ctx , nil , roomNID , serverName )
2021-07-21 12:06:32 +00:00
}
2020-09-03 16:20:54 +00:00
// GetKnownUsers searches all users that userID knows about.
func ( d * Database ) GetKnownUsers ( ctx context . Context , userID , searchString string , limit int ) ( [ ] string , error ) {
2020-09-03 17:27:02 +00:00
stateKeyNID , err := d . EventStateKeysTable . SelectEventStateKeyNID ( ctx , nil , userID )
if err != nil {
return nil , err
}
2022-02-04 10:39:34 +00:00
return d . MembershipTable . SelectKnownUsers ( ctx , nil , stateKeyNID , searchString , limit )
2020-09-03 16:20:54 +00:00
}
// GetKnownRooms returns a list of all rooms we know about.
func ( d * Database ) GetKnownRooms ( ctx context . Context ) ( [ ] string , error ) {
2022-02-04 10:39:34 +00:00
return d . RoomsTable . SelectRoomIDs ( ctx , nil )
2020-09-03 16:20:54 +00:00
}
2020-09-03 17:27:02 +00:00
2020-11-05 10:19:23 +00:00
// ForgetRoom sets a users room to forgotten
func ( d * Database ) ForgetRoom ( ctx context . Context , userID , roomID string , forget bool ) error {
2022-02-04 10:39:34 +00:00
roomNIDs , err := d . RoomsTable . BulkSelectRoomNIDs ( ctx , nil , [ ] string { roomID } )
2020-11-05 10:19:23 +00:00
if err != nil {
return err
}
if len ( roomNIDs ) > 1 {
return fmt . Errorf ( "expected one room, got %d" , len ( roomNIDs ) )
}
stateKeyNID , err := d . EventStateKeysTable . SelectEventStateKeyNID ( ctx , nil , userID )
if err != nil {
return err
}
return d . Writer . Do ( d . DB , nil , func ( txn * sql . Tx ) error {
return d . MembershipTable . UpdateForgetMembership ( ctx , nil , roomNIDs [ 0 ] , stateKeyNID , forget )
} )
}
2020-09-03 17:27:02 +00:00
// FIXME TODO: Remove all this - horrible dupe with roomserver/state. Can't use the original impl because of circular loops
// it should live in this package!
func ( d * Database ) loadStateAtSnapshot (
ctx context . Context , stateNID types . StateSnapshotNID ,
) ( [ ] types . StateEntry , error ) {
stateBlockNIDLists , err := d . StateBlockNIDs ( ctx , [ ] types . StateSnapshotNID { stateNID } )
if err != nil {
return nil , err
}
// We've asked for exactly one snapshot from the db so we should have exactly one entry in the result.
stateBlockNIDList := stateBlockNIDLists [ 0 ]
stateEntryLists , err := d . StateEntries ( ctx , stateBlockNIDList . StateBlockNIDs )
if err != nil {
return nil , err
}
stateEntriesMap := stateEntryListMap ( stateEntryLists )
// Combine all the state entries for this snapshot.
// The order of state block NIDs in the list tells us the order to combine them in.
var fullState [ ] types . StateEntry
for _ , stateBlockNID := range stateBlockNIDList . StateBlockNIDs {
entries , ok := stateEntriesMap . lookup ( stateBlockNID )
if ! ok {
// This should only get hit if the database is corrupt.
// It should be impossible for an event to reference a NID that doesn't exist
2021-09-08 16:31:03 +00:00
panic ( fmt . Errorf ( "corrupt DB: Missing state block numeric ID %d" , stateBlockNID ) )
2020-09-03 17:27:02 +00:00
}
fullState = append ( fullState , entries ... )
}
// Stable sort so that the most recent entry for each state key stays
// remains later in the list than the older entries for the same state key.
sort . Stable ( stateEntryByStateKeySorter ( fullState ) )
// Unique returns the last entry and hence the most recent entry for each state key.
fullState = fullState [ : util . Unique ( stateEntryByStateKeySorter ( fullState ) ) ]
return fullState , nil
}
type stateEntryListMap [ ] types . StateEntryList
func ( m stateEntryListMap ) lookup ( stateBlockNID types . StateBlockNID ) ( stateEntries [ ] types . StateEntry , ok bool ) {
list := [ ] types . StateEntryList ( m )
i := sort . Search ( len ( list ) , func ( i int ) bool {
return list [ i ] . StateBlockNID >= stateBlockNID
} )
if i < len ( list ) && list [ i ] . StateBlockNID == stateBlockNID {
ok = true
stateEntries = list [ i ] . StateEntries
}
return
}
type stateEntryByStateKeySorter [ ] types . StateEntry
func ( s stateEntryByStateKeySorter ) Len ( ) int { return len ( s ) }
func ( s stateEntryByStateKeySorter ) Less ( i , j int ) bool {
return s [ i ] . StateKeyTuple . LessThan ( s [ j ] . StateKeyTuple )
}
func ( s stateEntryByStateKeySorter ) Swap ( i , j int ) { s [ i ] , s [ j ] = s [ j ] , s [ i ] }