2020-02-13 17:27:33 +00:00
// Copyright 2017-2018 New Vector Ltd
// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// 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 sqlite3
import (
"context"
"database/sql"
2022-09-09 12:14:52 +00:00
"errors"
2022-05-03 15:35:06 +00:00
"fmt"
2020-02-13 17:27:33 +00:00
2020-08-25 11:32:29 +00:00
"github.com/matrix-org/dendrite/internal/caching"
2020-04-16 09:06:55 +00:00
"github.com/matrix-org/dendrite/internal/sqlutil"
2020-05-26 14:42:42 +00:00
"github.com/matrix-org/dendrite/roomserver/storage/shared"
2020-11-05 10:19:23 +00:00
"github.com/matrix-org/dendrite/roomserver/storage/sqlite3/deltas"
2020-02-13 17:27:33 +00:00
"github.com/matrix-org/dendrite/roomserver/types"
2020-12-02 17:41:00 +00:00
"github.com/matrix-org/dendrite/setup/config"
2023-05-24 10:14:42 +00:00
"github.com/matrix-org/gomatrixserverlib"
2020-02-13 17:27:33 +00:00
)
// A Database is used to store room events and stream offsets.
type Database struct {
2020-05-26 14:42:42 +00:00
shared . Database
2020-02-13 17:27:33 +00:00
}
2020-05-01 12:34:53 +00:00
// Open a sqlite database.
2023-03-17 11:09:45 +00:00
func Open ( ctx context . Context , conMan sqlutil . Connections , dbProperties * config . DatabaseOptions , cache caching . RoomServerCaches ) ( * Database , error ) {
2020-02-13 17:27:33 +00:00
var d Database
2020-08-10 13:18:04 +00:00
var err error
2023-03-17 11:09:45 +00:00
db , writer , err := conMan . Connection ( dbProperties )
2022-05-03 15:35:06 +00:00
if err != nil {
return nil , fmt . Errorf ( "sqlutil.Open: %w" , err )
2020-02-13 17:27:33 +00:00
}
2020-11-05 10:19:23 +00:00
//db.Exec("PRAGMA journal_mode=WAL;")
//db.Exec("PRAGMA read_uncommitted = true;")
2020-02-20 09:28:03 +00:00
// FIXME: We are leaking connections somewhere. Setting this to 2 will eventually
// cause the roomserver to be unresponsive to new events because something will
// acquire the global mutex and never unlock it because it is waiting for a connection
// which it will never obtain.
2022-05-03 15:35:06 +00:00
// db.SetMaxOpenConns(20)
2020-05-27 10:03:47 +00:00
2021-04-26 12:25:57 +00:00
// Create the tables.
2022-07-25 09:39:22 +00:00
if err = d . create ( db ) ; err != nil {
2020-05-26 14:42:42 +00:00
return nil , err
}
2021-04-26 12:25:57 +00:00
2022-07-25 09:39:22 +00:00
// Special case, since this migration uses several tables, so it needs to
// be sure that all tables are created first.
2023-03-17 11:09:45 +00:00
if err = executeMigration ( ctx , db ) ; err != nil {
2022-09-09 12:14:52 +00:00
return nil , err
2020-05-26 14:42:42 +00:00
}
2021-04-26 12:25:57 +00:00
// Then prepare the statements. Now that the migrations have run, any columns referred
// to in the database code should now exist.
2022-08-08 08:18:57 +00:00
if err = d . prepare ( db , writer , cache ) ; err != nil {
2020-05-26 15:45:28 +00:00
return nil , err
}
2020-11-05 10:19:23 +00:00
return & d , nil
}
2022-09-09 12:14:52 +00:00
func executeMigration ( ctx context . Context , db * sql . DB ) error {
// TODO: Remove when we are sure we are not having goose artefacts in the db
// This forces an error, which indicates the migration is already applied, since the
// column event_nid was removed from the table
migrationName := "roomserver: state blocks refactor"
var cName string
err := db . QueryRowContext ( ctx , ` SELECT p.name FROM sqlite_master AS m JOIN pragma_table_info(m.name) AS p WHERE m.name = 'roomserver_state_block' AND p.name = 'event_nid' ` ) . Scan ( & cName )
if err != nil {
if errors . Is ( err , sql . ErrNoRows ) { // migration was already executed, as the column was removed
if err = sqlutil . InsertMigration ( ctx , db , migrationName ) ; err != nil {
2022-09-13 06:07:43 +00:00
return fmt . Errorf ( "unable to manually insert migration '%s': %w" , migrationName , err )
2022-09-09 12:14:52 +00:00
}
return nil
}
return err
}
m := sqlutil . NewMigrator ( db )
m . AddMigrations ( sqlutil . Migration {
Version : migrationName ,
Up : deltas . UpStateBlocksRefactor ,
} )
return m . Up ( ctx )
}
2021-04-26 12:25:57 +00:00
func ( d * Database ) create ( db * sql . DB ) error {
2022-05-09 13:30:32 +00:00
if err := CreateEventStateKeysTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-09 13:30:32 +00:00
if err := CreateEventTypesTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-09 13:30:32 +00:00
if err := CreateEventJSONTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-09 13:30:32 +00:00
if err := CreateEventsTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-16 17:33:16 +00:00
if err := CreateRoomsTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-16 17:33:16 +00:00
if err := CreateStateBlockTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-16 17:33:16 +00:00
if err := CreateStateSnapshotTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-10 12:41:12 +00:00
if err := CreatePrevEventsTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-16 17:33:16 +00:00
if err := CreateRoomAliasesTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-10 12:41:12 +00:00
if err := CreateInvitesTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-10 12:41:12 +00:00
if err := CreateMembershipTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-10 12:41:12 +00:00
if err := CreatePublishedTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
2022-05-10 12:41:12 +00:00
if err := CreateRedactionsTable ( db ) ; err != nil {
2021-04-26 12:25:57 +00:00
return err
}
return nil
}
2022-05-03 15:35:06 +00:00
func ( d * Database ) prepare ( db * sql . DB , writer sqlutil . Writer , cache caching . RoomServerCaches ) error {
2022-05-09 13:30:32 +00:00
eventStateKeys , err := PrepareEventStateKeysTable ( db )
2020-05-26 15:45:28 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-05-26 15:45:28 +00:00
}
2022-05-09 13:30:32 +00:00
eventTypes , err := PrepareEventTypesTable ( db )
2020-05-26 17:23:39 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-05-26 17:23:39 +00:00
}
2022-05-09 13:30:32 +00:00
eventJSON , err := PrepareEventJSONTable ( db )
2020-05-26 17:23:39 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-05-26 17:23:39 +00:00
}
2022-05-09 13:30:32 +00:00
events , err := PrepareEventsTable ( db )
2020-05-27 08:36:09 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-05-27 08:36:09 +00:00
}
2022-05-16 17:33:16 +00:00
rooms , err := PrepareRoomsTable ( db )
2020-05-27 08:36:09 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-05-27 08:36:09 +00:00
}
2022-05-16 17:33:16 +00:00
stateBlock , err := PrepareStateBlockTable ( db )
2020-05-27 08:36:09 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-05-27 08:36:09 +00:00
}
2022-05-16 17:33:16 +00:00
stateSnapshot , err := PrepareStateSnapshotTable ( db )
2020-05-27 10:03:47 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-05-27 10:03:47 +00:00
}
2022-05-10 12:41:12 +00:00
prevEvents , err := PreparePrevEventsTable ( db )
2020-05-27 10:03:47 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-05-27 10:03:47 +00:00
}
2022-05-16 17:33:16 +00:00
roomAliases , err := PrepareRoomAliasesTable ( db )
2020-07-02 14:41:18 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
2020-07-02 14:41:18 +00:00
}
2022-05-10 12:41:12 +00:00
invites , err := PrepareInvitesTable ( db )
2020-07-06 16:49:15 +00:00
if err != nil {
2020-11-05 10:19:23 +00:00
return err
}
2022-05-10 12:41:12 +00:00
membership , err := PrepareMembershipTable ( db )
2020-11-05 10:19:23 +00:00
if err != nil {
return err
}
2022-05-10 12:41:12 +00:00
published , err := PreparePublishedTable ( db )
2020-11-05 10:19:23 +00:00
if err != nil {
return err
}
2022-05-10 12:41:12 +00:00
redactions , err := PrepareRedactionsTable ( db )
2020-11-05 10:19:23 +00:00
if err != nil {
return err
2020-07-06 16:49:15 +00:00
}
2023-01-19 20:02:32 +00:00
purge , err := PreparePurgeStatements ( db , stateSnapshot )
if err != nil {
return err
}
2020-05-26 14:42:42 +00:00
d . Database = shared . Database {
2023-03-01 16:06:47 +00:00
DB : db ,
EventDatabase : shared . EventDatabase {
DB : db ,
Cache : cache ,
Writer : writer ,
EventsTable : events ,
EventTypesTable : eventTypes ,
EventStateKeysTable : eventStateKeys ,
EventJSONTable : eventJSON ,
PrevEventsTable : prevEvents ,
RedactionsTable : redactions ,
} ,
Cache : cache ,
Writer : writer ,
RoomsTable : rooms ,
StateBlockTable : stateBlock ,
StateSnapshotTable : stateSnapshot ,
RoomAliasesTable : roomAliases ,
InvitesTable : invites ,
MembershipTable : membership ,
PublishedTable : published ,
GetRoomUpdaterFn : d . GetRoomUpdater ,
Purge : purge ,
2020-05-26 14:42:42 +00:00
}
2020-11-05 10:19:23 +00:00
return nil
2020-02-13 17:27:33 +00:00
}
2020-08-20 15:24:33 +00:00
func ( d * Database ) SupportsConcurrentRoomInputs ( ) bool {
// This isn't supported in SQLite mode yet because of issues with
// database locks.
// TODO: Look at this again - the problem is probably to do with
// the membership updaters and latest events updaters.
return false
}
2022-02-04 10:39:34 +00:00
func ( d * Database ) GetRoomUpdater (
ctx context . Context , roomInfo * types . RoomInfo ,
) ( * shared . RoomUpdater , error ) {
2020-05-28 10:15:21 +00:00
// TODO: Do not use transactions. We should be holding open this transaction but we cannot have
// multiple write transactions on sqlite. The code will perform additional
// write transactions independent of this one which will consistently cause
// 'database is locked' errors. As sqlite doesn't support multi-process on the
// same DB anyway, and we only execute updates sequentially, the only worries
// are for rolling back when things go wrong. (atomicity)
2022-02-04 10:39:34 +00:00
return shared . NewRoomUpdater ( ctx , & d . Database , nil , roomInfo )
2020-04-03 13:29:06 +00:00
}
2020-02-13 17:27:33 +00:00
func ( d * Database ) MembershipUpdater (
ctx context . Context , roomID , targetUserID string ,
2020-05-20 17:03:06 +00:00
targetLocal bool , roomVersion gomatrixserverlib . RoomVersion ,
2020-08-19 12:24:54 +00:00
) ( * shared . MembershipUpdater , error ) {
2020-05-28 10:15:21 +00:00
// TODO: Do not use transactions. We should be holding open this transaction but we cannot have
// multiple write transactions on sqlite. The code will perform additional
// write transactions independent of this one which will consistently cause
// 'database is locked' errors. As sqlite doesn't support multi-process on the
// same DB anyway, and we only execute updates sequentially, the only worries
// are for rolling back when things go wrong. (atomicity)
2020-08-19 12:24:54 +00:00
return shared . NewMembershipUpdater ( ctx , & d . Database , nil , roomID , targetUserID , targetLocal , roomVersion )
2020-02-13 17:27:33 +00:00
}