mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	[doctor] Add check/fix for bogus action rows (#19656)
Signed-off-by: Loïc Dachary <loic@dachary.org> Co-authored-by: Loïc Dachary <loic@dachary.org>
This commit is contained in:
		@@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"xorm.io/builder"
 | 
						"xorm.io/builder"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -246,3 +247,23 @@ func FixIssueLabelWithOutsideLabels() (int64, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return res.RowsAffected()
 | 
						return res.RowsAffected()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CountActionCreatedUnixString count actions where created_unix is an empty string
 | 
				
			||||||
 | 
					func CountActionCreatedUnixString() (int64, error) {
 | 
				
			||||||
 | 
						if setting.Database.UseSQLite3 {
 | 
				
			||||||
 | 
							return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FixActionCreatedUnixString set created_unix to zero if it is an empty string
 | 
				
			||||||
 | 
					func FixActionCreatedUnixString() (int64, error) {
 | 
				
			||||||
 | 
						if setting.Database.UseSQLite3 {
 | 
				
			||||||
 | 
							res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return 0, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return res.RowsAffected()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
	issues_model "code.gitea.io/gitea/models/issues"
 | 
						issues_model "code.gitea.io/gitea/models/issues"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
	"code.gitea.io/gitea/models/unittest"
 | 
						"code.gitea.io/gitea/models/unittest"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -103,3 +104,46 @@ func TestUpdateMilestoneCounters(t *testing.T) {
 | 
				
			|||||||
	assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID))
 | 
						assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID))
 | 
				
			||||||
	unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
 | 
						unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConsistencyUpdateAction(t *testing.T) {
 | 
				
			||||||
 | 
						if !setting.Database.UseSQLite3 {
 | 
				
			||||||
 | 
							t.Skip("Test is only for SQLite database.")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert.NoError(t, unittest.PrepareTestDatabase())
 | 
				
			||||||
 | 
						id := 8
 | 
				
			||||||
 | 
						unittest.AssertExistsAndLoadBean(t, &Action{
 | 
				
			||||||
 | 
							ID: int64(id),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						actions := make([]*Action, 0, 1)
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// XORM returns an error when created_unix is a string
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						err = db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions)
 | 
				
			||||||
 | 
						if assert.Error(t, err) {
 | 
				
			||||||
 | 
							assert.Contains(t, err.Error(), "type string to a int64: invalid syntax")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Get rid of incorrectly set created_unix
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						count, err := CountActionCreatedUnixString()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.EqualValues(t, 1, count)
 | 
				
			||||||
 | 
						count, err = FixActionCreatedUnixString()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.EqualValues(t, 1, count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						count, err = CountActionCreatedUnixString()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.EqualValues(t, 0, count)
 | 
				
			||||||
 | 
						count, err = FixActionCreatedUnixString()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.EqualValues(t, 0, count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// XORM must be happy now
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
 | 
				
			||||||
 | 
						unittest.CheckConsistencyFor(t, &Action{})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -142,6 +142,12 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
 | 
				
			|||||||
			Fixer:        models.FixIssueLabelWithOutsideLabels,
 | 
								Fixer:        models.FixIssueLabelWithOutsideLabels,
 | 
				
			||||||
			FixedMessage: "Removed",
 | 
								FixedMessage: "Removed",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name:         "Action with created_unix set as an empty string",
 | 
				
			||||||
 | 
								Counter:      models.CountActionCreatedUnixString,
 | 
				
			||||||
 | 
								Fixer:        models.FixActionCreatedUnixString,
 | 
				
			||||||
 | 
								FixedMessage: "Set to zero",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: function to recalc all counters
 | 
						// TODO: function to recalc all counters
 | 
				
			||||||
@@ -177,6 +183,9 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
 | 
				
			|||||||
		// find access without repository
 | 
							// find access without repository
 | 
				
			||||||
		genericOrphanCheck("Access entries without existing repository",
 | 
							genericOrphanCheck("Access entries without existing repository",
 | 
				
			||||||
			"access", "repository", "access.repo_id=repository.id"),
 | 
								"access", "repository", "access.repo_id=repository.id"),
 | 
				
			||||||
 | 
							// find action without repository
 | 
				
			||||||
 | 
							genericOrphanCheck("Action entries without existing repository",
 | 
				
			||||||
 | 
								"action", "repository", "action.repo_id=repository.id"),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, c := range consistencyChecks {
 | 
						for _, c := range consistencyChecks {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user