diff --git a/front/follows.go b/front/follows.go index 8d08a9ea..88e4b794 100644 --- a/front/follows.go +++ b/front/follows.go @@ -51,7 +51,7 @@ func (h *Handler) follows(w text.Writer, r *request, args ...string) { persons.actor->>'$.type' = 'Group' and notes.object->'$.inReplyTo' is null and notes.inserted >= unixepoch() - 7*24*60*60 - union + union all select persons.actor, notes.inserted as ninserted, follows.inserted as finserted from follows join persons @@ -64,7 +64,7 @@ func (h *Handler) follows(w text.Writer, r *request, args ...string) { follows.follower = $1 and persons.actor->>'$.type' != 'Group' and notes.inserted >= unixepoch() - 7*24*60*60 - union + union all select persons.actor, shares.inserted as ninserted, follows.inserted as finserted from follows join shares @@ -80,7 +80,7 @@ func (h *Handler) follows(w text.Writer, r *request, args ...string) { shares.inserted >= unixepoch() - 7*24*60*60 and notes.public = 1 and follows.follower = $1 - union + union all select persons.actor, null as ninserted, follows.inserted as finserted from follows join persons diff --git a/front/fts.go b/front/fts.go index 82c6734e..1fdae1f7 100644 --- a/front/fts.go +++ b/front/fts.go @@ -89,7 +89,7 @@ func (h *Handler) fts(w text.Writer, r *request, args ...string) { where notes.public = 1 and notesfts.content match $1 - union + union all select notes.id, notes.object, notes.author, notes.inserted, rank, 1 as aud from follows join @@ -109,7 +109,7 @@ func (h *Handler) fts(w text.Writer, r *request, args ...string) { where follows.follower = $2 and notesfts.content match $1 - union + union all select notes.id, notes.object, notes.author, notes.inserted, rank, 0 as aud from notesfts join notes on diff --git a/front/mentions.go b/front/mentions.go index ae2836f6..2de75bf5 100644 --- a/front/mentions.go +++ b/front/mentions.go @@ -55,7 +55,7 @@ func (h *Handler) mentions(w text.Writer, r *request, args ...string) { where follows.follower = $1 and notes.inserted >= $2 - union + union all select notes.id, notes.object, authors.actor, shares.inserted, sharers.actor as sharer from follows join @@ -83,10 +83,8 @@ func (h *Handler) mentions(w text.Writer, r *request, args ...string) { follows.follower = $1 and shares.inserted >= $2 ) - group by - id order by - max(inserted) desc + inserted desc limit $3 offset $4`, r.User.ID, diff --git a/front/outbox.go b/front/outbox.go index 289a9c13..fe9e5edf 100644 --- a/front/outbox.go +++ b/front/outbox.go @@ -95,7 +95,7 @@ func (h *Handler) userOutbox(w text.Writer, r *request, args ...string) { select notes.id, persons.actor, notes.object, notes.inserted, null as sharer from notes join persons on persons.id = $1 where notes.author = $1 and notes.public = 1 - union + union all select notes.id, authors.actor, notes.object, shares.inserted, sharers.actor as by from shares join notes on notes.id = shares.note @@ -116,7 +116,7 @@ func (h *Handler) userOutbox(w text.Writer, r *request, args ...string) { select notes.id, persons.actor, notes.object, notes.inserted, null as sharer from notes join persons on persons.id = notes.author where notes.author = $1 - union + union all select notes.id, authors.actor, notes.object, shares.inserted, sharers.actor as by from shares join notes on notes.id = shares.note join persons authors on authors.id = notes.author @@ -157,7 +157,7 @@ func (h *Handler) userOutbox(w text.Writer, r *request, args ...string) { notes.author = $1 and persons.id = $1 and exists (select 1 from follows where follower = $2 and followed = $1 and accepted = 1) - union + union all select notes.id, authors.actor, notes.object, shares.inserted, sharers.actor as by from shares join notes on notes.id = shares.note diff --git a/front/print.go b/front/print.go index 28b59ebd..caccca81 100644 --- a/front/print.go +++ b/front/print.go @@ -324,11 +324,11 @@ func (r *request) PrintNote(w text.Writer, note *ap.Object, author *ap.Actor, sh join notes on notes.id = shares.note join persons on persons.id = shares.by and persons.id = notes.object->>'$.audience' where shares.note = $1 - union + union all select persons.id, persons.actor->>'$.preferredUsername' as username, shares.inserted, 2 as rank from shares join persons on persons.id = shares.by where shares.note = $1 and persons.host = $2 - union + union all select persons.id, persons.actor->>'$.preferredUsername' as username, shares.inserted, 3 as rank from shares join persons on persons.id = shares.by where shares.note = $1 and persons.host != $2 @@ -347,16 +347,16 @@ func (r *request) PrintNote(w text.Writer, note *ap.Object, author *ap.Actor, sh join notes on notes.id = shares.note join persons on persons.id = shares.by and persons.id = notes.object->>'$.audience' where shares.note = $1 - union + union all select persons.id, persons.actor->>'$.preferredUsername' as username, shares.inserted, 2 as rank from shares join follows on follows.followed = shares.by join persons on persons.id = follows.followed where shares.note = $1 and follows.follower = $2 - union + union all select persons.id, persons.actor->>'$.preferredUsername' as username, shares.inserted, 3 as rank from shares join persons on persons.id = shares.by where shares.note = $1 and persons.host = $3 - union + union all select persons.id, persons.actor->>'$.preferredUsername' as username, shares.inserted, 4 as rank from shares join persons on persons.id = shares.by where shares.note = $1 and persons.host != $3 diff --git a/front/thread.go b/front/thread.go index b19d7be6..fe1ee565 100644 --- a/front/thread.go +++ b/front/thread.go @@ -43,7 +43,7 @@ func (h *Handler) thread(w text.Writer, r *request, args ...string) { r.Log.Info("Viewing thread", "post", postID) var threadHead sql.NullString - if err := r.QueryRow(`with recursive thread(id, parent) as (select notes.id, notes.object->>'$.inReplyTo' as parent from notes where id = ? union select notes.id, notes.object->>'$.inReplyTo' as parent from thread t join notes on notes.id = t.parent) select thread.id from thread where thread.parent is null limit 1`, postID).Scan(&threadHead); err != nil && !errors.Is(err, sql.ErrNoRows) { + if err := r.QueryRow(`with recursive thread(id, parent) as (select notes.id, notes.object->>'$.inReplyTo' as parent from notes where id = ? union all select notes.id, notes.object->>'$.inReplyTo' as parent from thread t join notes on notes.id = t.parent) select thread.id from thread where thread.parent is null limit 1`, postID).Scan(&threadHead); err != nil && !errors.Is(err, sql.ErrNoRows) { r.Log.Warn("Failed to fetch thread head", "error", err) w.Error() return @@ -57,7 +57,7 @@ func (h *Handler) thread(w text.Writer, r *request, args ...string) { return } - rows, err := r.Query(`select thread.depth, thread.id, strftime('%Y-%m-%d', datetime(thread.inserted, 'unixepoch')), persons.actor->>'$.preferredUsername' from (with recursive thread(id, author, inserted, parent, depth, path) as (select notes.id, notes.author, notes.inserted, object->>'$.inReplyTo' as parent, 0 as depth, notes.inserted || notes.id as path from notes where id = $1 union select notes.id, notes.author, notes.inserted, notes.object->>'$.inReplyTo', t.depth + 1, t.path || notes.inserted || notes.id from thread t join notes on notes.object->>'$.inReplyTo' = t.id) select thread.depth, thread.id, thread.author, thread.inserted, thread.path from thread order by thread.path limit $2 offset $3) thread join persons on persons.id = thread.author order by thread.path`, postID, h.Config.PostsPerPage, offset) + rows, err := r.Query(`select thread.depth, thread.id, strftime('%Y-%m-%d', datetime(thread.inserted, 'unixepoch')), persons.actor->>'$.preferredUsername' from (with recursive thread(id, author, inserted, parent, depth, path) as (select notes.id, notes.author, notes.inserted, object->>'$.inReplyTo' as parent, 0 as depth, notes.inserted || notes.id as path from notes where id = $1 union all select notes.id, notes.author, notes.inserted, notes.object->>'$.inReplyTo', t.depth + 1, t.path || notes.inserted || notes.id from thread t join notes on notes.object->>'$.inReplyTo' = t.id) select thread.depth, thread.id, thread.author, thread.inserted, thread.path from thread order by thread.path limit $2 offset $3) thread join persons on persons.id = thread.author order by thread.path`, postID, h.Config.PostsPerPage, offset) if err != nil { r.Log.Info("Failed to fetch thread", "post", postID, "error", err) w.Status(40, "Post not found") diff --git a/front/users.go b/front/users.go index 8a8eec93..838faa21 100644 --- a/front/users.go +++ b/front/users.go @@ -35,29 +35,47 @@ func (h *Handler) users(w text.Writer, r *request, args ...string) { "📻 My Radio", func(offset int) (*sql.Rows, error) { return r.Query(` - select object, actor, sharer, max(inserted) from + select object, actor, sharer, inserted from ( - select notes.id, notes.object, persons.actor, notes.inserted, null as sharer from - follows - join - persons - on - persons.id = follows.followed - join - notes - on - notes.author = follows.followed and - ( - notes.public = 1 or - persons.actor->>'$.followers' in (notes.cc0, notes.to0, notes.cc1, notes.to1, notes.cc2, notes.to2) or - $1 in (notes.cc0, notes.to0, notes.cc1, notes.to1, notes.cc2, notes.to2) or - (notes.to2 is not null and exists (select 1 from json_each(notes.object->'$.to') where value = persons.actor->>'$.followers' or value = $1)) or - (notes.cc2 is not null and exists (select 1 from json_each(notes.object->'$.cc') where value = persons.actor->>'$.followers' or value = $1)) - ) - where - follows.follower = $1 and - notes.inserted >= $2 - union + select id, object, actor, inserted, null as sharer from + ( + select notes.id, notes.object, persons.actor, notes.inserted from + follows + join + persons + on + persons.id = follows.followed + join + notes + on + notes.author = follows.followed and + ( + notes.public = 1 or + persons.actor->>'$.followers' in (notes.cc0, notes.to0, notes.cc1, notes.to1, notes.cc2, notes.to2) or + $1 in (notes.cc0, notes.to0, notes.cc1, notes.to1, notes.cc2, notes.to2) or + (notes.to2 is not null and exists (select 1 from json_each(notes.object->'$.to') where value = persons.actor->>'$.followers' or value = $1)) or + (notes.cc2 is not null and exists (select 1 from json_each(notes.object->'$.cc') where value = persons.actor->>'$.followers' or value = $1)) + ) + where + follows.follower = $1 and + notes.inserted >= $2 + union + select notes.id, notes.object, authors.actor, notes.inserted from + notes myposts + join + notes + on + notes.object->>'$.inReplyTo' = myposts.id + join + persons authors + on + authors.id = notes.author + where + myposts.author = $1 and + notes.author != $1 and + notes.inserted >= $2 + ) + union all select notes.id, notes.object, authors.actor, shares.inserted, sharers.actor as sharer from follows join @@ -80,26 +98,9 @@ func (h *Handler) users(w text.Writer, r *request, args ...string) { follows.follower = $1 and shares.inserted >= $2 and notes.public = 1 - union - select notes.id, notes.object, authors.actor, notes.inserted, null as sharer from - notes myposts - join - notes - on - notes.object->>'$.inReplyTo' = myposts.id - join - persons authors - on - authors.id = notes.author - where - myposts.author = $1 and - notes.author != $1 and - notes.inserted >= $2 ) - group by - id order by - max(inserted) desc + inserted desc limit $3 offset $4`, r.User.ID, diff --git a/front/view.go b/front/view.go index 478bb89e..14d473c5 100644 --- a/front/view.go +++ b/front/view.go @@ -171,7 +171,7 @@ func (h *Handler) view(w text.Writer, r *request, args ...string) { r.Log.Warn("Failed to check if parent post exists", "error", err) } - if err := r.QueryRow(`with recursive thread(id, parent, depth) as (select notes.id, notes.object->>'$.inReplyTo' as parent, 1 as depth from notes where id = ? union select notes.id, notes.object->>'$.inReplyTo' as parent, t.depth + 1 from thread t join notes on notes.id = t.parent) select id from thread order by depth desc limit 1`, note.InReplyTo).Scan(&threadHead); err != nil && errors.Is(err, sql.ErrNoRows) { + if err := r.QueryRow(`with recursive thread(id, parent, depth) as (select notes.id, notes.object->>'$.inReplyTo' as parent, 1 as depth from notes where id = ? union all select notes.id, notes.object->>'$.inReplyTo' as parent, t.depth + 1 from thread t join notes on notes.id = t.parent) select id from thread order by depth desc limit 1`, note.InReplyTo).Scan(&threadHead); err != nil && errors.Is(err, sql.ErrNoRows) { r.Log.Debug("First post in thread is missing") } else if err != nil { r.Log.Warn("Failed to fetch first post in thread", "error", err) @@ -179,7 +179,7 @@ func (h *Handler) view(w text.Writer, r *request, args ...string) { } var threadDepth int - if err := r.QueryRow(`with recursive thread(id, depth) as (select notes.id, 0 as depth from notes where id = ? union select notes.id, t.depth + 1 from thread t join notes on notes.object->>'$.inReplyTo' = t.id where t.depth <= 3) select max(thread.depth) from thread`, note.ID).Scan(&threadDepth); err != nil { + if err := r.QueryRow(`with recursive thread(id, depth) as (select notes.id, 0 as depth from notes where id = ? union all select notes.id, t.depth + 1 from thread t join notes on notes.object->>'$.inReplyTo' = t.id where t.depth <= 3) select max(thread.depth) from thread`, note.ID).Scan(&threadDepth); err != nil { r.Log.Warn("Failed to query thread depth", "error", err) } diff --git a/outbox/forward.go b/outbox/forward.go index f9750944..3c28ad1f 100644 --- a/outbox/forward.go +++ b/outbox/forward.go @@ -42,7 +42,7 @@ func ForwardActivity(ctx context.Context, domain string, cfg *cfg.Config, log *s var firstPostID, threadStarterID string var depth int - if err := tx.QueryRowContext(ctx, `with recursive thread(id, author, parent, depth) as (select notes.id, notes.author, notes.object->>'$.inReplyTo' as parent, 1 as depth from notes where id = $1 union select notes.id, notes.author, notes.object->>'$.inReplyTo' as parent, t.depth + 1 from thread t join notes on notes.id = t.parent where t.depth <= $2) select id, author, depth from thread order by depth desc limit 1`, note.ID, cfg.MaxForwardingDepth+1).Scan(&firstPostID, &threadStarterID, &depth); err != nil && errors.Is(err, sql.ErrNoRows) { + if err := tx.QueryRowContext(ctx, `with recursive thread(id, author, parent, depth) as (select notes.id, notes.author, notes.object->>'$.inReplyTo' as parent, 1 as depth from notes where id = $1 union all select notes.id, notes.author, notes.object->>'$.inReplyTo' as parent, t.depth + 1 from thread t join notes on notes.id = t.parent where t.depth <= $2) select id, author, depth from thread order by depth desc limit 1`, note.ID, cfg.MaxForwardingDepth+1).Scan(&firstPostID, &threadStarterID, &depth); err != nil && errors.Is(err, sql.ErrNoRows) { log.Debug("Failed to find thread for post", "note", note.ID) return nil } else if err != nil { diff --git a/outbox/move.go b/outbox/move.go index 8cdfc0f4..145d18d1 100644 --- a/outbox/move.go +++ b/outbox/move.go @@ -36,7 +36,7 @@ type Mover struct { } func (m *Mover) updatedMoveTargets(ctx context.Context, prefix string) error { - rows, err := m.DB.QueryContext(ctx, `select oldid, newid from (select old.id as oldid, new.id as newid, old.updated as oldupdated from persons old join persons new on old.actor->>'$.movedTo' = new.id and not exists (select 1 from json_each(new.actor->'$.alsoKnownAs') where value = old.id) and old.updated > new.updated where old.actor->>'$.movedTo' is not null union select old.id, old.actor->>'$.movedTo', old.updated from persons old where old.actor->>'$.movedTo' is not null and not exists (select 1 from persons new where new.id = old.actor->>'$.movedTo')) where exists (select 1 from follows where followed = oldid and follower like ? and inserted < oldupdated)`, prefix) + rows, err := m.DB.QueryContext(ctx, `select oldid, newid from (select old.id as oldid, new.id as newid, old.updated as oldupdated from persons old join persons new on old.actor->>'$.movedTo' = new.id and not exists (select 1 from json_each(new.actor->'$.alsoKnownAs') where value = old.id) and old.updated > new.updated where old.actor->>'$.movedTo' is not null union all select old.id, old.actor->>'$.movedTo', old.updated from persons old where old.actor->>'$.movedTo' is not null and not exists (select 1 from persons new where new.id = old.actor->>'$.movedTo')) where exists (select 1 from follows where followed = oldid and follower like ? and inserted < oldupdated)`, prefix) if err != nil { return fmt.Errorf("failed to moved actors: %w", err) }