From 33f03046a493f2840b8f4d3aa36522d08e860326 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 3 Nov 2020 15:22:57 +0000 Subject: [PATCH] Edges table instead of relationships --- internal/mscs/msc2836/msc2836.go | 23 +++- internal/mscs/msc2836/storage.go | 212 ++++++++++++++----------------- 2 files changed, 114 insertions(+), 121 deletions(-) diff --git a/internal/mscs/msc2836/msc2836.go b/internal/mscs/msc2836/msc2836.go index 36dc5cb68..cf57658da 100644 --- a/internal/mscs/msc2836/msc2836.go +++ b/internal/mscs/msc2836/msc2836.go @@ -176,7 +176,7 @@ func eventRelationshipHandler(db Database, rsAPI roomserver.RoomserverInternalAP // If include_parent: true and there is a valid m.relationship field in the event, // retrieve the referenced event. Apply history visibility check to that event and if it passes, add it to the response array. func includeParent(ctx context.Context, rsAPI roomserver.RoomserverInternalAPI, event *gomatrixserverlib.HeaderedEvent, userID string) (parent *gomatrixserverlib.HeaderedEvent) { - parentID, _ := parentChildEventIDs(event) + parentID, _, _ := parentChildEventIDs(event) if parentID == "" { return nil } @@ -187,7 +187,7 @@ func includeParent(ctx context.Context, rsAPI roomserver.RoomserverInternalAPI, // Apply history visibility checks to all these events and add the ones which pass into the response array, // honouring the recent_first flag and the limit. func includeChildren(ctx context.Context, rsAPI roomserver.RoomserverInternalAPI, db Database, parentID string, limit int, recentFirst bool, userID string) ([]*gomatrixserverlib.HeaderedEvent, *util.JSONResponse) { - children, err := db.ChildrenForParent(ctx, parentID) + children, err := db.ChildrenForParent(ctx, parentID, "m.reference") if err != nil { util.GetLogger(ctx).WithError(err).Error("failed to get ChildrenForParent") resErr := jsonerror.InternalServerError() @@ -219,6 +219,10 @@ func includeChildren(ctx context.Context, rsAPI roomserver.RoomserverInternalAPI func walkThread( ctx context.Context, db Database, rsAPI roomserver.RoomserverInternalAPI, userID string, req *EventRelationshipRequest, included map[string]int, limit int, ) ([]*gomatrixserverlib.HeaderedEvent, bool) { + if req.Direction != "down" { + util.GetLogger(ctx).Error("not implemented: direction=up") + return nil, false + } var result []*gomatrixserverlib.HeaderedEvent eventsToWalk := newWalker(req) parent, siblingNum, current := eventsToWalk.Next() @@ -295,22 +299,33 @@ func getEventIfVisible(ctx context.Context, rsAPI roomserver.RoomserverInternalA return &event } +// walker walks the thread DAG type walker interface { + // Next returns the next event. `current` is the event ID being walked. + // `parent` is the parent of `current`. `siblingNum` is the sibling number of `current`, starting + // from one. Next() (parent string, siblingNum int, current string) } func newWalker(req *EventRelationshipRequest) walker { if *req.DepthFirst { - return &depthWalker{req} + return &depthWalker{ + req: req, + current: req.EventID, + } } return &breadthWalker{req} } type depthWalker struct { - req *EventRelationshipRequest + req *EventRelationshipRequest + db Database + current string } func (w *depthWalker) Next() (parent string, siblingNum int, current string) { + //var events []string + //children, err := w.db.ChildrenForParent(w.ctx, w.current) return "", 0, "" } diff --git a/internal/mscs/msc2836/storage.go b/internal/mscs/msc2836/storage.go index 1b68252ed..4fd7a614b 100644 --- a/internal/mscs/msc2836/storage.go +++ b/internal/mscs/msc2836/storage.go @@ -13,140 +13,99 @@ import ( type Database interface { // StoreRelation stores the parent->child and child->parent relationship for later querying. StoreRelation(ctx context.Context, ev *gomatrixserverlib.HeaderedEvent) error - ChildrenForParent(ctx context.Context, eventID string) ([]string, error) + ChildrenForParent(ctx context.Context, eventID, relType string) ([]string, error) } -type Postgres struct { +type DB struct { db *sql.DB - insertRelationStmt *sql.Stmt - selectChildrenForParentStmt *sql.Stmt -} - -func NewPostgresDatabase(dbOpts *config.DatabaseOptions) (Database, error) { - var p Postgres - var err error - if p.db, err = sqlutil.Open(dbOpts); err != nil { - return nil, err - } - _, err = p.db.Exec(` - CREATE TABLE IF NOT EXISTS msc2836_relationships ( - parent_event_id TEXT NOT NULL, - child_event_id TEXT NOT NULL, - parent_room_id TEXT NOT NULL, - parent_origin_server_ts BIGINT NOT NULL, - CONSTRAINT msc2836_relationships_unique UNIQUE (parent_event_id, child_event_id) - ); - `) - if err != nil { - return nil, err - } - if p.insertRelationStmt, err = p.db.Prepare(` - INSERT INTO msc2836_relationships(parent_event_id, child_event_id, parent_room_id, parent_origin_server_ts) VALUES($1, $2, $3, $4) ON CONFLICT DO NOTHING - `); err != nil { - return nil, err - } - if p.selectChildrenForParentStmt, err = p.db.Prepare(` - SELECT child_event_id FROM msc2836_relationships WHERE parent_event_id = $1 - `); err != nil { - return nil, err - } - return &p, err -} - -func (p *Postgres) StoreRelation(ctx context.Context, ev *gomatrixserverlib.HeaderedEvent) error { - parent, child := parentChildEventIDs(ev) - if parent == "" || child == "" { - return nil - } - _, err := p.insertRelationStmt.ExecContext(ctx, parent, child, ev.RoomID(), ev.OriginServerTS()) - return err -} - -func (p *Postgres) ChildrenForParent(ctx context.Context, eventID string) ([]string, error) { - return childrenForParent(ctx, eventID, p.selectChildrenForParentStmt) -} - -type SQLite struct { - db *sql.DB - insertRelationStmt *sql.Stmt - selectChildrenForParentStmt *sql.Stmt writer sqlutil.Writer -} - -func NewSQLiteDatabase(dbOpts *config.DatabaseOptions) (Database, error) { - var s SQLite - var err error - if s.db, err = sqlutil.Open(dbOpts); err != nil { - return nil, err - } - s.writer = sqlutil.NewExclusiveWriter() - _, err = s.db.Exec(` - CREATE TABLE IF NOT EXISTS msc2836_relationships ( - parent_event_id TEXT NOT NULL, - child_event_id TEXT NOT NULL, - parent_room_id TEXT NOT NULL, - parent_origin_server_ts BIGINT NOT NULL, - UNIQUE (parent_event_id, child_event_id) - ); - `) - if err != nil { - return nil, err - } - if s.insertRelationStmt, err = s.db.Prepare(` - INSERT INTO msc2836_relationships(parent_event_id, child_event_id, parent_room_id, parent_origin_server_ts) VALUES($1, $2, $3, $4) ON CONFLICT (parent_event_id, child_event_id) DO NOTHING - `); err != nil { - return nil, err - } - if s.selectChildrenForParentStmt, err = s.db.Prepare(` - SELECT child_event_id FROM msc2836_relationships WHERE parent_event_id = $1 - `); err != nil { - return nil, err - } - return &s, nil -} - -func (s *SQLite) StoreRelation(ctx context.Context, ev *gomatrixserverlib.HeaderedEvent) error { - parent, child := parentChildEventIDs(ev) - if parent == "" || child == "" { - return nil - } - _, err := s.insertRelationStmt.ExecContext(ctx, parent, child, ev.RoomID(), ev.OriginServerTS()) - return err -} - -func (s *SQLite) ChildrenForParent(ctx context.Context, eventID string) ([]string, error) { - return childrenForParent(ctx, eventID, s.selectChildrenForParentStmt) + insertRelationStmt *sql.Stmt + selectChildrenForParentStmt *sql.Stmt } // NewDatabase loads the database for msc2836 func NewDatabase(dbOpts *config.DatabaseOptions) (Database, error) { if dbOpts.ConnectionString.IsPostgres() { - return NewPostgresDatabase(dbOpts) + return newPostgresDatabase(dbOpts) } - return NewSQLiteDatabase(dbOpts) + return newSQLiteDatabase(dbOpts) } -func parentChildEventIDs(ev *gomatrixserverlib.HeaderedEvent) (parent string, child string) { - if ev == nil { - return +func newPostgresDatabase(dbOpts *config.DatabaseOptions) (Database, error) { + d := DB{ + writer: sqlutil.NewDummyWriter(), } - body := struct { - Relationship struct { - RelType string `json:"rel_type"` - EventID string `json:"event_id"` - } `json:"m.relationship"` - }{} - if err := json.Unmarshal(ev.Content(), &body); err != nil { - return + var err error + if d.db, err = sqlutil.Open(dbOpts); err != nil { + return nil, err } - if body.Relationship.RelType == "m.reference" && body.Relationship.EventID != "" { - return body.Relationship.EventID, ev.EventID() + _, err = d.db.Exec(` + CREATE TABLE IF NOT EXISTS msc2836_edges ( + parent_event_id TEXT NOT NULL, + child_event_id TEXT NOT NULL, + rel_type TEXT NOT NULL, + CONSTRAINT msc2836_edges UNIQUE (parent_event_id, child_event_id, rel_type) + ); + `) + if err != nil { + return nil, err } - return + if d.insertRelationStmt, err = d.db.Prepare(` + INSERT INTO msc2836_edges(parent_event_id, child_event_id, rel_type) VALUES($1, $2, $3) ON CONFLICT DO NOTHING + `); err != nil { + return nil, err + } + if d.selectChildrenForParentStmt, err = d.db.Prepare(` + SELECT child_event_id FROM msc2836_edges WHERE parent_event_id = $1 AND rel_type = $2 + `); err != nil { + return nil, err + } + return &d, err } -func childrenForParent(ctx context.Context, eventID string, stmt *sql.Stmt) ([]string, error) { - rows, err := stmt.QueryContext(ctx, eventID) +func newSQLiteDatabase(dbOpts *config.DatabaseOptions) (Database, error) { + d := DB{ + writer: sqlutil.NewExclusiveWriter(), + } + var err error + if d.db, err = sqlutil.Open(dbOpts); err != nil { + return nil, err + } + _, err = d.db.Exec(` + CREATE TABLE IF NOT EXISTS msc2836_edges ( + parent_event_id TEXT NOT NULL, + child_event_id TEXT NOT NULL, + rel_type TEXT NOT NULL, + UNIQUE (parent_event_id, child_event_id, rel_type) + ); + `) + if err != nil { + return nil, err + } + if d.insertRelationStmt, err = d.db.Prepare(` + INSERT INTO msc2836_edges(parent_event_id, child_event_id, rel_type) VALUES($1, $2, $3) ON CONFLICT (parent_event_id, child_event_id, rel_type) DO NOTHING + `); err != nil { + return nil, err + } + if d.selectChildrenForParentStmt, err = d.db.Prepare(` + SELECT child_event_id FROM msc2836_edges WHERE parent_event_id = $1 AND rel_type = $2 + `); err != nil { + return nil, err + } + return &d, nil +} + +func (p *DB) StoreRelation(ctx context.Context, ev *gomatrixserverlib.HeaderedEvent) error { + parent, child, relType := parentChildEventIDs(ev) + if parent == "" || child == "" { + return nil + } + _, err := p.insertRelationStmt.ExecContext(ctx, parent, child, relType) + return err +} + +func (p *DB) ChildrenForParent(ctx context.Context, eventID, relType string) ([]string, error) { + rows, err := p.selectChildrenForParentStmt.QueryContext(ctx, eventID, relType) if err != nil { return nil, err } @@ -161,3 +120,22 @@ func childrenForParent(ctx context.Context, eventID string, stmt *sql.Stmt) ([]s } return children, nil } + +func parentChildEventIDs(ev *gomatrixserverlib.HeaderedEvent) (parent, child, relType string) { + if ev == nil { + return + } + body := struct { + Relationship struct { + RelType string `json:"rel_type"` + EventID string `json:"event_id"` + } `json:"m.relationship"` + }{} + if err := json.Unmarshal(ev.Content(), &body); err != nil { + return + } + if body.Relationship.EventID == "" || body.Relationship.RelType == "" { + return + } + return body.Relationship.EventID, ev.EventID(), body.Relationship.RelType +}