From 06cb8dfcca8ef42c02d2511c79c8bcf63b8c649c Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 23 Jul 2025 00:18:50 +0200 Subject: [PATCH] [v12.0/forgejo] fix: make the action feed resilient to database inconsistencies (#8618) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8617 This reverts commit 7380eac5a2a2c04e6e8948f74d1b71dee2ffb61e. Resolves forgejo/forgejo#8612 It is possible for the action feed to reference deleted repositories the `INNER JOIN` will make sure that these are filtered out. We cannot filter these out after the fact, because the value of `count` will still be incorrect. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8617): make the action feed resilient to database inconsistencies Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8618 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/activities/action.go | 5 ++++- models/activities/action_test.go | 18 ++++++++++++++++++ models/fixtures/action.yml | 8 ++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/models/activities/action.go b/models/activities/action.go index 8592f81414..f928ad6784 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -473,8 +473,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return nil, 0, err } + sess := db.GetEngine(ctx).Where(cond). + Select("`action`.*"). // this line will avoid select other joined table's columns + Join("INNER", "repository", "`repository`.id = `action`.repo_id") + opts.SetDefaultValues() - sess := db.GetEngine(ctx).Where(cond) sess = db.SetSessionPagination(sess, &opts) actions := make([]*Action, 0, opts.PageSize) diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 47dbd8ac2d..161d05bbfa 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -227,6 +227,24 @@ func TestNotifyWatchers(t *testing.T) { }) } +func TestGetFeedsCorrupted(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ + ID: 8, + RepoID: 1700, + }) + + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + RequestedUser: user, + Actor: user, + IncludePrivate: true, + }) + require.NoError(t, err) + assert.Empty(t, actions) + assert.Equal(t, int64(0), count) +} + func TestConsistencyUpdateAction(t *testing.T) { if !setting.Database.Type.IsSQLite3() { t.Skip("Test is only for SQLite database.") diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml index a97e94fbf4..f1592d4569 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -59,6 +59,14 @@ created_unix: 1603011540 # grouped with id:7 - id: 8 + user_id: 1 + op_type: 12 # close issue + act_user_id: 1 + repo_id: 1700 # dangling intentional + is_private: false + created_unix: 1603011541 + +- id: 9 user_id: 34 op_type: 12 # close issue act_user_id: 34