2018-06-19 22:44:33 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
Migration fixes for gogs (0.11.66) to gitea (1.6.0) #5318 (#5341)
* Remove field from migration to support upgrades from older version
That will ensure the field does not get queried in the Select if it does
not exist yet:
```
[I] [SQL] SELECT "id", "repo_id", "index", "poster_id", "name", "content", "milestone_id", "priority", "assignee_id", "is_closed", "is_pull", "num_comments", "ref", "deadline_unix", "created_unix", "updated_unix
[...itea/routers/init.go:60 GlobalInit()] [E] Failed to initialize ORM engine: migrate: do migrate: pq: column "ref" does not exist
```
see #5318
* Skip remove stale watcher migration if not required
Otherwise the migration will fail if executed from a older database
version without multiple IssueWatch feature.
```
2018/11/11 23:51:14 [I] [SQL] SELECT DISTINCT "issue_watch"."user_id", "issue"."repo_id" FROM "issue_watch" INNER JOIN issue ON issue_watch.issue_id = issue.id WHERE (issue_watch.is_watching = $1) LIMIT 50 []int
[...itea/routers/init.go:60 GlobalInit()] [E] Failed to initialize ORM engine: migrate: do migrate: pq: relation "issue_watch" does not exist
```
see #5318
2018-11-18 21:25:32 +03:00
"fmt"
2018-06-19 22:44:33 +03:00
"code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm"
)
func removeStaleWatches ( x * xorm . Engine ) error {
type Watch struct {
ID int64
UserID int64
RepoID int64
}
type IssueWatch struct {
ID int64
UserID int64
RepoID int64
IsWatching bool
}
type Repository struct {
ID int64
IsPrivate bool
OwnerID int64
}
type Access struct {
UserID int64
RepoID int64
Mode int
}
const (
// AccessModeNone no access
AccessModeNone int = iota // 0
// AccessModeRead read access
AccessModeRead // 1
)
2019-01-26 17:50:36 +03:00
accessLevel := func ( e * xorm . Session , userID int64 , repo * Repository ) ( int , error ) {
2018-06-19 22:44:33 +03:00
mode := AccessModeNone
if ! repo . IsPrivate {
mode = AccessModeRead
}
if userID == 0 {
return mode , nil
}
if userID == repo . OwnerID {
return 4 , nil
}
a := & Access { UserID : userID , RepoID : repo . ID }
2019-01-26 17:50:36 +03:00
if has , err := e . Get ( a ) ; ! has || err != nil {
2018-06-19 22:44:33 +03:00
return mode , err
}
return a . Mode , nil
}
sess := x . NewSession ( )
defer sess . Close ( )
if err := sess . Begin ( ) ; err != nil {
return err
}
Migration fixes for gogs (0.11.66) to gitea (1.6.0) #5318 (#5341)
* Remove field from migration to support upgrades from older version
That will ensure the field does not get queried in the Select if it does
not exist yet:
```
[I] [SQL] SELECT "id", "repo_id", "index", "poster_id", "name", "content", "milestone_id", "priority", "assignee_id", "is_closed", "is_pull", "num_comments", "ref", "deadline_unix", "created_unix", "updated_unix
[...itea/routers/init.go:60 GlobalInit()] [E] Failed to initialize ORM engine: migrate: do migrate: pq: column "ref" does not exist
```
see #5318
* Skip remove stale watcher migration if not required
Otherwise the migration will fail if executed from a older database
version without multiple IssueWatch feature.
```
2018/11/11 23:51:14 [I] [SQL] SELECT DISTINCT "issue_watch"."user_id", "issue"."repo_id" FROM "issue_watch" INNER JOIN issue ON issue_watch.issue_id = issue.id WHERE (issue_watch.is_watching = $1) LIMIT 50 []int
[...itea/routers/init.go:60 GlobalInit()] [E] Failed to initialize ORM engine: migrate: do migrate: pq: relation "issue_watch" does not exist
```
see #5318
2018-11-18 21:25:32 +03:00
var issueWatch IssueWatch
if exist , err := sess . IsTableExist ( & issueWatch ) ; err != nil {
return fmt . Errorf ( "IsExist IssueWatch: %v" , err )
} else if ! exist {
return nil
}
2018-06-19 22:44:33 +03:00
repoCache := make ( map [ int64 ] * Repository )
2019-08-24 12:24:45 +03:00
err := sess . BufferSize ( setting . Database . IterateBufferSize ) . Iterate ( new ( Watch ) ,
2018-06-19 22:44:33 +03:00
func ( idx int , bean interface { } ) error {
watch := bean . ( * Watch )
repo := repoCache [ watch . RepoID ]
if repo == nil {
repo = & Repository {
ID : watch . RepoID ,
}
2019-01-26 17:50:36 +03:00
if _ , err := sess . Get ( repo ) ; err != nil {
2018-06-19 22:44:33 +03:00
return err
}
repoCache [ watch . RepoID ] = repo
}
// Remove watches from now unaccessible repositories
2019-01-26 17:50:36 +03:00
mode , err := accessLevel ( sess , watch . UserID , repo )
2018-06-19 22:44:33 +03:00
if err != nil {
return err
}
has := AccessModeRead <= mode
if has {
return nil
}
if _ , err = sess . Delete ( & Watch { 0 , watch . UserID , repo . ID } ) ; err != nil {
return err
}
_ , err = sess . Exec ( "UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?" , repo . ID )
return err
} )
if err != nil {
return err
}
repoCache = make ( map [ int64 ] * Repository )
2019-08-24 12:24:45 +03:00
err = sess . BufferSize ( setting . Database . IterateBufferSize ) .
2018-06-19 22:44:33 +03:00
Distinct ( "issue_watch.user_id" , "issue.repo_id" ) .
Join ( "INNER" , "issue" , "issue_watch.issue_id = issue.id" ) .
Where ( "issue_watch.is_watching = ?" , true ) .
Iterate ( new ( IssueWatch ) ,
func ( idx int , bean interface { } ) error {
watch := bean . ( * IssueWatch )
repo := repoCache [ watch . RepoID ]
if repo == nil {
repo = & Repository {
ID : watch . RepoID ,
}
2019-01-26 17:50:36 +03:00
if _ , err := sess . Get ( repo ) ; err != nil {
2018-06-19 22:44:33 +03:00
return err
}
repoCache [ watch . RepoID ] = repo
}
// Remove issue watches from now unaccssible repositories
2019-01-26 17:50:36 +03:00
mode , err := accessLevel ( sess , watch . UserID , repo )
2018-06-19 22:44:33 +03:00
if err != nil {
return err
}
has := AccessModeRead <= mode
if has {
return nil
}
iw := & IssueWatch {
IsWatching : false ,
}
_ , err = sess .
Join ( "INNER" , "issue" , "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?" , watch . RepoID ) .
Cols ( "is_watching" , "updated_unix" ) .
Where ( "`issue_watch`.user_id = ?" , watch . UserID ) .
Update ( iw )
return err
} )
if err != nil {
return err
}
return sess . Commit ( )
}