@ -5,13 +5,11 @@
package models
import (
"fmt"
"reflect"
"regexp"
"strings"
"testing"
"code.gitea.io/gitea/modules/setting "
"code.gitea.io/gitea/models/db "
"github.com/stretchr/testify/assert"
"xorm.io/builder"
)
@ -43,7 +41,7 @@ func CheckConsistencyFor(t *testing.T, beansToCheck ...interface{}) {
ptrToSliceValue := reflect . New ( sliceType )
ptrToSliceValue . Elem ( ) . Set ( sliceValue )
assert . NoError ( t , x . Table ( bean ) . Find ( ptrToSliceValue . Interface ( ) ) )
assert . NoError ( t , db . DefaultConte xt ( ) . Engine ( ) . Table ( bean ) . Find ( ptrToSliceValue . Interface ( ) ) )
sliceValue = ptrToSliceValue . Elem ( )
for i := 0 ; i < sliceValue . Len ( ) ; i ++ {
@ -60,7 +58,7 @@ func CheckConsistencyFor(t *testing.T, beansToCheck ...interface{}) {
}
// getCount get the count of database entries matching bean
func getCount ( t * testing . T , e Engine , bean interface { } ) int64 {
func getCount ( t * testing . T , e db . Engine , bean interface { } ) int64 {
count , err := e . Count ( bean )
assert . NoError ( t , err )
return count
@ -68,7 +66,7 @@ func getCount(t *testing.T, e Engine, bean interface{}) int64 {
// assertCount test the count of database entries matching bean
func assertCount ( t * testing . T , bean interface { } , expected int ) {
assert . EqualValues ( t , expected , getCount ( t , x , bean ) ,
assert . EqualValues ( t , expected , getCount ( t , db . DefaultConte xt ( ) . Engine ( ) , bean ) ,
"Failed consistency test, the counted bean (of type %T) was %+v" , bean , bean )
}
@ -91,46 +89,46 @@ func (repo *Repository) checkForConsistency(t *testing.T) {
assertCount ( t , & Milestone { RepoID : repo . ID } , repo . NumMilestones )
assertCount ( t , & Repository { ForkID : repo . ID } , repo . NumForks )
if repo . IsFork {
AssertExistsAndLoadBean ( t , & Repository { ID : repo . ForkID } )
db . AssertExistsAndLoadBean ( t , & Repository { ID : repo . ForkID } )
}
actual := getCount ( t , x . Where ( "Mode<>?" , RepoWatchModeDont ) , & Watch { RepoID : repo . ID } )
actual := getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . Where ( "Mode<>?" , RepoWatchModeDont ) , & Watch { RepoID : repo . ID } )
assert . EqualValues ( t , repo . NumWatches , actual ,
"Unexpected number of watches for repo %+v" , repo )
actual = getCount ( t , x . Where ( "is_pull=?" , false ) , & Issue { RepoID : repo . ID } )
actual = getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . Where ( "is_pull=?" , false ) , & Issue { RepoID : repo . ID } )
assert . EqualValues ( t , repo . NumIssues , actual ,
"Unexpected number of issues for repo %+v" , repo )
actual = getCount ( t , x . Where ( "is_pull=? AND is_closed=?" , false , true ) , & Issue { RepoID : repo . ID } )
actual = getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . Where ( "is_pull=? AND is_closed=?" , false , true ) , & Issue { RepoID : repo . ID } )
assert . EqualValues ( t , repo . NumClosedIssues , actual ,
"Unexpected number of closed issues for repo %+v" , repo )
actual = getCount ( t , x . Where ( "is_pull=?" , true ) , & Issue { RepoID : repo . ID } )
actual = getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . Where ( "is_pull=?" , true ) , & Issue { RepoID : repo . ID } )
assert . EqualValues ( t , repo . NumPulls , actual ,
"Unexpected number of pulls for repo %+v" , repo )
actual = getCount ( t , x . Where ( "is_pull=? AND is_closed=?" , true , true ) , & Issue { RepoID : repo . ID } )
actual = getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . Where ( "is_pull=? AND is_closed=?" , true , true ) , & Issue { RepoID : repo . ID } )
assert . EqualValues ( t , repo . NumClosedPulls , actual ,
"Unexpected number of closed pulls for repo %+v" , repo )
actual = getCount ( t , x . Where ( "is_closed=?" , true ) , & Milestone { RepoID : repo . ID } )
actual = getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . Where ( "is_closed=?" , true ) , & Milestone { RepoID : repo . ID } )
assert . EqualValues ( t , repo . NumClosedMilestones , actual ,
"Unexpected number of closed milestones for repo %+v" , repo )
}
func ( issue * Issue ) checkForConsistency ( t * testing . T ) {
actual := getCount ( t , x . Where ( "type=?" , CommentTypeComment ) , & Comment { IssueID : issue . ID } )
actual := getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . Where ( "type=?" , CommentTypeComment ) , & Comment { IssueID : issue . ID } )
assert . EqualValues ( t , issue . NumComments , actual ,
"Unexpected number of comments for issue %+v" , issue )
if issue . IsPull {
pr := AssertExistsAndLoadBean ( t , & PullRequest { IssueID : issue . ID } ) . ( * PullRequest )
pr := db . AssertExistsAndLoadBean ( t , & PullRequest { IssueID : issue . ID } ) . ( * PullRequest )
assert . EqualValues ( t , pr . Index , issue . Index )
}
}
func ( pr * PullRequest ) checkForConsistency ( t * testing . T ) {
issue := AssertExistsAndLoadBean ( t , & Issue { ID : pr . IssueID } ) . ( * Issue )
issue := db . AssertExistsAndLoadBean ( t , & Issue { ID : pr . IssueID } ) . ( * Issue )
assert . True ( t , issue . IsPull )
assert . EqualValues ( t , issue . Index , pr . Index )
}
@ -138,7 +136,7 @@ func (pr *PullRequest) checkForConsistency(t *testing.T) {
func ( milestone * Milestone ) checkForConsistency ( t * testing . T ) {
assertCount ( t , & Issue { MilestoneID : milestone . ID } , milestone . NumIssues )
actual := getCount ( t , x . Where ( "is_closed=?" , true ) , & Issue { MilestoneID : milestone . ID } )
actual := getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . Where ( "is_closed=?" , true ) , & Issue { MilestoneID : milestone . ID } )
assert . EqualValues ( t , milestone . NumClosedIssues , actual ,
"Unexpected number of closed issues for milestone %+v" , milestone )
@ -151,7 +149,7 @@ func (milestone *Milestone) checkForConsistency(t *testing.T) {
func ( label * Label ) checkForConsistency ( t * testing . T ) {
issueLabels := make ( [ ] * IssueLabel , 0 , 10 )
assert . NoError ( t , x . Find ( & issueLabels , & IssueLabel { LabelID : label . ID } ) )
assert . NoError ( t , db . DefaultConte xt ( ) . Engine ( ) . Find ( & issueLabels , & IssueLabel { LabelID : label . ID } ) )
assert . EqualValues ( t , label . NumIssues , len ( issueLabels ) ,
"Unexpected number of issue for label %+v" , label )
@ -162,7 +160,7 @@ func (label *Label) checkForConsistency(t *testing.T) {
expected := int64 ( 0 )
if len ( issueIDs ) > 0 {
expected = getCount ( t , x . In ( "id" , issueIDs ) . Where ( "is_closed=?" , true ) , & Issue { } )
expected = getCount ( t , db . DefaultConte xt ( ) . Engine ( ) . In ( "id" , issueIDs ) . Where ( "is_closed=?" , true ) , & Issue { } )
}
assert . EqualValues ( t , expected , label . NumClosedIssues ,
"Unexpected number of closed issues for label %+v" , label )
@ -174,18 +172,18 @@ func (team *Team) checkForConsistency(t *testing.T) {
}
func ( action * Action ) checkForConsistency ( t * testing . T ) {
repo := AssertExistsAndLoadBean ( t , & Repository { ID : action . RepoID } ) . ( * Repository )
repo := db . AssertExistsAndLoadBean ( t , & Repository { ID : action . RepoID } ) . ( * Repository )
assert . Equal ( t , repo . IsPrivate , action . IsPrivate , "action: %+v" , action )
}
// CountOrphanedLabels return count of labels witch are broken and not accessible via ui anymore
func CountOrphanedLabels ( ) ( int64 , error ) {
noref , err := x . Table ( "label" ) . Where ( "repo_id=? AND org_id=?" , 0 , 0 ) . Count ( "label.id" )
noref , err := db . DefaultConte xt ( ) . Engine ( ) . Table ( "label" ) . Where ( "repo_id=? AND org_id=?" , 0 , 0 ) . Count ( "label.id" )
if err != nil {
return 0 , err
}
norepo , err := x . Table ( "label" ) .
norepo , err := db . DefaultConte xt ( ) . Engine ( ) . Table ( "label" ) .
Where ( builder . And (
builder . Gt { "repo_id" : 0 } ,
builder . NotIn ( "repo_id" , builder . Select ( "id" ) . From ( "repository" ) ) ,
@ -195,7 +193,7 @@ func CountOrphanedLabels() (int64, error) {
return 0 , err
}
noorg , err := x . Table ( "label" ) .
noorg , err := db . DefaultConte xt ( ) . Engine ( ) . Table ( "label" ) .
Where ( builder . And (
builder . Gt { "org_id" : 0 } ,
builder . NotIn ( "org_id" , builder . Select ( "id" ) . From ( "user" ) ) ,
@ -211,12 +209,12 @@ func CountOrphanedLabels() (int64, error) {
// DeleteOrphanedLabels delete labels witch are broken and not accessible via ui anymore
func DeleteOrphanedLabels ( ) error {
// delete labels with no reference
if _ , err := x . Table ( "label" ) . Where ( "repo_id=? AND org_id=?" , 0 , 0 ) . Delete ( new ( Label ) ) ; err != nil {
if _ , err := db . DefaultConte xt ( ) . Engine ( ) . Table ( "label" ) . Where ( "repo_id=? AND org_id=?" , 0 , 0 ) . Delete ( new ( Label ) ) ; err != nil {
return err
}
// delete labels with none existing repos
if _ , err := x .
if _ , err := db . DefaultConte xt ( ) . Engine ( ) .
Where ( builder . And (
builder . Gt { "repo_id" : 0 } ,
builder . NotIn ( "repo_id" , builder . Select ( "id" ) . From ( "repository" ) ) ,
@ -226,7 +224,7 @@ func DeleteOrphanedLabels() error {
}
// delete labels with none existing orgs
if _ , err := x .
if _ , err := db . DefaultConte xt ( ) . Engine ( ) .
Where ( builder . And (
builder . Gt { "org_id" : 0 } ,
builder . NotIn ( "org_id" , builder . Select ( "id" ) . From ( "user" ) ) ,
@ -240,14 +238,14 @@ func DeleteOrphanedLabels() error {
// CountOrphanedIssueLabels return count of IssueLabels witch have no label behind anymore
func CountOrphanedIssueLabels ( ) ( int64 , error ) {
return x . Table ( "issue_label" ) .
return db . DefaultConte xt ( ) . Engine ( ) . Table ( "issue_label" ) .
NotIn ( "label_id" , builder . Select ( "id" ) . From ( "label" ) ) .
Count ( )
}
// DeleteOrphanedIssueLabels delete IssueLabels witch have no label behind anymore
func DeleteOrphanedIssueLabels ( ) error {
_ , err := x .
_ , err := db . DefaultConte xt ( ) . Engine ( ) .
NotIn ( "label_id" , builder . Select ( "id" ) . From ( "label" ) ) .
Delete ( IssueLabel { } )
@ -256,7 +254,7 @@ func DeleteOrphanedIssueLabels() error {
// CountOrphanedIssues count issues without a repo
func CountOrphanedIssues ( ) ( int64 , error ) {
return x . Table ( "issue" ) .
return db . DefaultConte xt ( ) . Engine ( ) . Table ( "issue" ) .
Join ( "LEFT" , "repository" , "issue.repo_id=repository.id" ) .
Where ( builder . IsNull { "repository.id" } ) .
Count ( "id" )
@ -264,15 +262,15 @@ func CountOrphanedIssues() (int64, error) {
// DeleteOrphanedIssues delete issues without a repo
func DeleteOrphanedIssues ( ) error {
sess := x . NewSession ( )
defer sess . Close ( )
if err := sess . Begin ( ) ; err != nil {
ctx , committer , err := db . TxContext ( )
if err != nil {
return err
}
defer committer . Close ( )
var ids [ ] int64
if err := s ess . Table ( "issue" ) . Distinct ( "issue.repo_id" ) .
if err := ctx . Engin e( ) . Table ( "issue" ) . Distinct ( "issue.repo_id" ) .
Join ( "LEFT" , "repository" , "issue.repo_id=repository.id" ) .
Where ( builder . IsNull { "repository.id" } ) . GroupBy ( "issue.repo_id" ) .
Find ( & ids ) ; err != nil {
@ -281,27 +279,28 @@ func DeleteOrphanedIssues() error {
var attachmentPaths [ ] string
for i := range ids {
paths , err := deleteIssuesByRepoID ( s ess , ids [ i ] )
paths , err := deleteIssuesByRepoID ( ctx . Engin e( ) , ids [ i ] )
if err != nil {
return err
}
attachmentPaths = append ( attachmentPaths , paths ... )
}
if err := sess . Commit ( ) ; err != nil {
if err := committer . Commit ( ) ; err != nil {
return err
}
committer . Close ( )
// Remove issue attachment files.
for i := range attachmentPaths {
removeAllWithNotice ( x , "Delete issue attachment" , attachmentPaths [ i ] )
removeAllWithNotice ( db . DefaultConte xt ( ) . Engine ( ) , "Delete issue attachment" , attachmentPaths [ i ] )
}
return nil
}
// CountOrphanedObjects count subjects with have no existing refobject anymore
func CountOrphanedObjects ( subject , refobject , joinCond string ) ( int64 , error ) {
return x . Table ( "`" + subject + "`" ) .
return db . DefaultConte xt ( ) . Engine ( ) . Table ( "`" + subject + "`" ) .
Join ( "LEFT" , refobject , joinCond ) .
Where ( builder . IsNull { "`" + refobject + "`.id" } ) .
Count ( "id" )
@ -317,45 +316,45 @@ func DeleteOrphanedObjects(subject, refobject, joinCond string) error {
if err != nil {
return err
}
_ , err = x . Exec ( append ( [ ] interface { } { sql } , args ... ) ... )
_ , err = db . DefaultConte xt ( ) . Engine ( ) . Exec ( append ( [ ] interface { } { sql } , args ... ) ... )
return err
}
// CountNullArchivedRepository counts the number of repositories with is_archived is null
func CountNullArchivedRepository ( ) ( int64 , error ) {
return x . Where ( builder . IsNull { "is_archived" } ) . Count ( new ( Repository ) )
return db . DefaultConte xt ( ) . Engine ( ) . Where ( builder . IsNull { "is_archived" } ) . Count ( new ( Repository ) )
}
// FixNullArchivedRepository sets is_archived to false where it is null
func FixNullArchivedRepository ( ) ( int64 , error ) {
return x . Where ( builder . IsNull { "is_archived" } ) . Cols ( "is_archived" ) . NoAutoTime ( ) . Update ( & Repository {
return db . DefaultConte xt ( ) . Engine ( ) . Where ( builder . IsNull { "is_archived" } ) . Cols ( "is_archived" ) . NoAutoTime ( ) . Update ( & Repository {
IsArchived : false ,
} )
}
// CountWrongUserType count OrgUser who have wrong type
func CountWrongUserType ( ) ( int64 , error ) {
return x . Where ( builder . Eq { "type" : 0 } . And ( builder . Neq { "num_teams" : 0 } ) ) . Count ( new ( User ) )
return db . DefaultConte xt ( ) . Engine ( ) . Where ( builder . Eq { "type" : 0 } . And ( builder . Neq { "num_teams" : 0 } ) ) . Count ( new ( User ) )
}
// FixWrongUserType fix OrgUser who have wrong type
func FixWrongUserType ( ) ( int64 , error ) {
return x . Where ( builder . Eq { "type" : 0 } . And ( builder . Neq { "num_teams" : 0 } ) ) . Cols ( "type" ) . NoAutoTime ( ) . Update ( & User { Type : 1 } )
return db . DefaultConte xt ( ) . Engine ( ) . Where ( builder . Eq { "type" : 0 } . And ( builder . Neq { "num_teams" : 0 } ) ) . Cols ( "type" ) . NoAutoTime ( ) . Update ( & User { Type : 1 } )
}
// CountCommentTypeLabelWithEmptyLabel count label comments with empty label
func CountCommentTypeLabelWithEmptyLabel ( ) ( int64 , error ) {
return x . Where ( builder . Eq { "type" : CommentTypeLabel , "label_id" : 0 } ) . Count ( new ( Comment ) )
return db . DefaultConte xt ( ) . Engine ( ) . Where ( builder . Eq { "type" : CommentTypeLabel , "label_id" : 0 } ) . Count ( new ( Comment ) )
}
// FixCommentTypeLabelWithEmptyLabel count label comments with empty label
func FixCommentTypeLabelWithEmptyLabel ( ) ( int64 , error ) {
return x . Where ( builder . Eq { "type" : CommentTypeLabel , "label_id" : 0 } ) . Delete ( new ( Comment ) )
return db . DefaultConte xt ( ) . Engine ( ) . Where ( builder . Eq { "type" : CommentTypeLabel , "label_id" : 0 } ) . Delete ( new ( Comment ) )
}
// CountCommentTypeLabelWithOutsideLabels count label comments with outside label
func CountCommentTypeLabelWithOutsideLabels ( ) ( int64 , error ) {
return x . Where ( "comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))" , CommentTypeLabel ) .
return db . DefaultConte xt ( ) . Engine ( ) . Where ( "comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))" , CommentTypeLabel ) .
Table ( "comment" ) .
Join ( "inner" , "label" , "label.id = comment.label_id" ) .
Join ( "inner" , "issue" , "issue.id = comment.issue_id " ) .
@ -365,7 +364,7 @@ func CountCommentTypeLabelWithOutsideLabels() (int64, error) {
// FixCommentTypeLabelWithOutsideLabels count label comments with outside label
func FixCommentTypeLabelWithOutsideLabels ( ) ( int64 , error ) {
res , err := x . Exec ( ` DELETE FROM comment WHERE comment . id IN (
res , err := db . DefaultConte xt ( ) . Engine ( ) . Exec ( ` DELETE FROM comment WHERE comment . id IN (
SELECT il_too . id FROM (
SELECT com . id
FROM comment AS com
@ -384,7 +383,7 @@ func FixCommentTypeLabelWithOutsideLabels() (int64, error) {
// CountIssueLabelWithOutsideLabels count label comments with outside label
func CountIssueLabelWithOutsideLabels ( ) ( int64 , error ) {
return x . Where ( builder . Expr ( "(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)" ) ) .
return db . DefaultConte xt ( ) . Engine ( ) . Where ( builder . Expr ( "(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)" ) ) .
Table ( "issue_label" ) .
Join ( "inner" , "label" , "issue_label.label_id = label.id " ) .
Join ( "inner" , "issue" , "issue.id = issue_label.issue_id " ) .
@ -394,7 +393,7 @@ func CountIssueLabelWithOutsideLabels() (int64, error) {
// FixIssueLabelWithOutsideLabels fix label comments with outside label
func FixIssueLabelWithOutsideLabels ( ) ( int64 , error ) {
res , err := x . Exec ( ` DELETE FROM issue_label WHERE issue_label . id IN (
res , err := db . DefaultConte xt ( ) . Engine ( ) . Exec ( ` DELETE FROM issue_label WHERE issue_label . id IN (
SELECT il_too . id FROM (
SELECT il_too_too . id
FROM issue_label AS il_too_too
@ -411,61 +410,3 @@ func FixIssueLabelWithOutsideLabels() (int64, error) {
return res . RowsAffected ( )
}
// CountBadSequences looks for broken sequences from recreate-table mistakes
func CountBadSequences ( ) ( int64 , error ) {
if ! setting . Database . UsePostgreSQL {
return 0 , nil
}
sess := x . NewSession ( )
defer sess . Close ( )
var sequences [ ] string
schema := sess . Engine ( ) . Dialect ( ) . URI ( ) . Schema
sess . Engine ( ) . SetSchema ( "" )
if err := sess . Table ( "information_schema.sequences" ) . Cols ( "sequence_name" ) . Where ( "sequence_name LIKE 'tmp_recreate__%_id_seq%' AND sequence_catalog = ?" , setting . Database . Name ) . Find ( & sequences ) ; err != nil {
return 0 , err
}
sess . Engine ( ) . SetSchema ( schema )
return int64 ( len ( sequences ) ) , nil
}
// FixBadSequences fixes for broken sequences from recreate-table mistakes
func FixBadSequences ( ) error {
if ! setting . Database . UsePostgreSQL {
return nil
}
sess := x . NewSession ( )
defer sess . Close ( )
if err := sess . Begin ( ) ; err != nil {
return err
}
var sequences [ ] string
schema := sess . Engine ( ) . Dialect ( ) . URI ( ) . Schema
sess . Engine ( ) . SetSchema ( "" )
if err := sess . Table ( "information_schema.sequences" ) . Cols ( "sequence_name" ) . Where ( "sequence_name LIKE 'tmp_recreate__%_id_seq%' AND sequence_catalog = ?" , setting . Database . Name ) . Find ( & sequences ) ; err != nil {
return err
}
sess . Engine ( ) . SetSchema ( schema )
sequenceRegexp := regexp . MustCompile ( ` tmp_recreate__(\w+)_id_seq.* ` )
for _ , sequence := range sequences {
tableName := sequenceRegexp . FindStringSubmatch ( sequence ) [ 1 ]
newSequenceName := tableName + "_id_seq"
if _ , err := sess . Exec ( fmt . Sprintf ( "ALTER SEQUENCE `%s` RENAME TO `%s`" , sequence , newSequenceName ) ) ; err != nil {
return err
}
if _ , err := sess . Exec ( fmt . Sprintf ( "SELECT setval('%s', COALESCE((SELECT MAX(id)+1 FROM `%s`), 1), false)" , newSequenceName , tableName ) ) ; err != nil {
return err
}
}
return sess . Commit ( )
}