Reorder migrations, skip errors if running migration again (#3160)

* Reorder migrations, skip errors if running migration again

* Rename migration file names to match migration version

* Add note about ingored error
This commit is contained in:
Lauris BH 2017-12-13 16:52:18 +02:00 committed by GitHub
parent d3c5911ffc
commit c06cc740de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 303 additions and 294 deletions

View file

@ -59,6 +59,10 @@ type Version struct {
Version int64 Version int64
} }
func emptyMigration(x *xorm.Engine) error {
return nil
}
// This is a sequence of migrations. Add new migrations to the bottom of the list. // This is a sequence of migrations. Add new migrations to the bottom of the list.
// If you want to "retire" a migration, remove it from the top of the list and // If you want to "retire" a migration, remove it from the top of the list and
// update minDBVersion accordingly // update minDBVersion accordingly
@ -127,17 +131,17 @@ var migrations = []Migration{
// v38 -> v39 // v38 -> v39
NewMigration("remove commits and settings unit types", removeCommitsUnitType), NewMigration("remove commits and settings unit types", removeCommitsUnitType),
// v39 -> v40 // v39 -> v40
NewMigration("adds time tracking and stopwatches", addTimetracking),
// v40 -> v41
NewMigration("migrate protected branch struct", migrateProtectedBranchStruct),
// v41 -> v42
NewMigration("add default value to user prohibit_login", addDefaultValueToUserProhibitLogin),
// v42 -> v43
NewMigration("add tags to releases and sync existing repositories", releaseAddColumnIsTagAndSyncTags), NewMigration("add tags to releases and sync existing repositories", releaseAddColumnIsTagAndSyncTags),
// v43 -> v44 // v40 -> v41
NewMigration("fix protected branch can push value to false", fixProtectedBranchCanPushValue), NewMigration("fix protected branch can push value to false", fixProtectedBranchCanPushValue),
// v44 -> v45 // v41 -> v42
NewMigration("remove duplicate unit types", removeDuplicateUnitTypes), NewMigration("remove duplicate unit types", removeDuplicateUnitTypes),
// v42 -> v43
NewMigration("empty step", emptyMigration),
// v43 -> v44
NewMigration("empty step", emptyMigration),
// v44 -> v45
NewMigration("empty step", emptyMigration),
// v45 -> v46 // v45 -> v46
NewMigration("remove index column from repo_unit table", removeIndexColumnFromRepoUnitTable), NewMigration("remove index column from repo_unit table", removeIndexColumnFromRepoUnitTable),
// v46 -> v47 // v46 -> v47
@ -147,8 +151,14 @@ var migrations = []Migration{
// v48 -> v49 // v48 -> v49
NewMigration("add repo indexer status", addRepoIndexerStatus), NewMigration("add repo indexer status", addRepoIndexerStatus),
// v49 -> v50 // v49 -> v50
NewMigration("add lfs lock table", addLFSLock), NewMigration("adds time tracking and stopwatches", addTimetracking),
// v50 -> v51 // v50 -> v51
NewMigration("migrate protected branch struct", migrateProtectedBranchStruct),
// v51 -> v52
NewMigration("add default value to user prohibit_login", addDefaultValueToUserProhibitLogin),
// v52 -> v53
NewMigration("add lfs lock table", addLFSLock),
// v53 -> v54
NewMigration("add reactions", addReactions), NewMigration("add reactions", addReactions),
} }

View file

@ -6,69 +6,52 @@ package migrations
import ( import (
"fmt" "fmt"
"time"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
func addTimetracking(x *xorm.Engine) error { // ReleaseV39 describes the added field for Release
// RepoUnit describes all units of a repository type ReleaseV39 struct {
type RepoUnit struct { IsTag bool `xorm:"NOT NULL DEFAULT false"`
ID int64 }
RepoID int64 `xorm:"INDEX(s)"`
Type int `xorm:"INDEX(s)"`
Index int
Config map[string]interface{} `xorm:"JSON"`
CreatedUnix int64 `xorm:"INDEX CREATED"`
Created time.Time `xorm:"-"`
}
// Stopwatch see models/issue_stopwatch.go // TableName will be invoked by XORM to customrize the table name
type Stopwatch struct { func (*ReleaseV39) TableName() string {
ID int64 `xorm:"pk autoincr"` return "release"
IssueID int64 `xorm:"INDEX"` }
UserID int64 `xorm:"INDEX"`
Created time.Time `xorm:"-"`
CreatedUnix int64
}
// TrackedTime see models/issue_tracked_time.go func releaseAddColumnIsTagAndSyncTags(x *xorm.Engine) error {
type TrackedTime struct { if err := x.Sync2(new(ReleaseV39)); err != nil {
ID int64 `xorm:"pk autoincr" json:"id"`
IssueID int64 `xorm:"INDEX" json:"issue_id"`
UserID int64 `xorm:"INDEX" json:"user_id"`
Created time.Time `xorm:"-" json:"created"`
CreatedUnix int64 `json:"-"`
Time int64 `json:"time"`
}
if err := x.Sync2(new(Stopwatch)); err != nil {
return fmt.Errorf("Sync2: %v", err) return fmt.Errorf("Sync2: %v", err)
} }
if err := x.Sync2(new(TrackedTime)); err != nil {
return fmt.Errorf("Sync2: %v", err) // For the sake of SQLite3, we can't use x.Iterate here.
} offset := 0
//Updating existing issue units pageSize := 20
units := make([]*RepoUnit, 0, 100) for {
err := x.Where("`type` = ?", V16UnitTypeIssues).Find(&units) repos := make([]*models.Repository, 0, pageSize)
if err != nil { if err := x.Table("repository").Asc("id").Limit(pageSize, offset).Find(&repos); err != nil {
return fmt.Errorf("Query repo units: %v", err) return fmt.Errorf("select repos [offset: %d]: %v", offset, err)
}
for _, unit := range units {
if unit.Config == nil {
unit.Config = make(map[string]interface{})
} }
if _, ok := unit.Config["EnableTimetracker"]; !ok { for _, repo := range repos {
unit.Config["EnableTimetracker"] = setting.Service.DefaultEnableTimetracking gitRepo, err := git.OpenRepository(repo.RepoPath())
if err != nil {
log.Warn("OpenRepository: %v", err)
continue
}
if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
log.Warn("SyncReleasesWithTags: %v", err)
}
} }
if _, ok := unit.Config["AllowOnlyContributorsToTrackTime"]; !ok { if len(repos) < pageSize {
unit.Config["AllowOnlyContributorsToTrackTime"] = setting.Service.DefaultAllowOnlyContributorsToTrackTime break
}
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
return err
} }
offset += pageSize
} }
return nil return nil
} }

View file

@ -6,50 +6,21 @@ package migrations
import ( import (
"fmt" "fmt"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
func migrateProtectedBranchStruct(x *xorm.Engine) error { func fixProtectedBranchCanPushValue(x *xorm.Engine) error {
type ProtectedBranch struct { type ProtectedBranch struct {
ID int64 `xorm:"pk autoincr"` CanPush bool `xorm:"NOT NULL DEFAULT false"`
RepoID int64 `xorm:"UNIQUE(s)"`
BranchName string `xorm:"UNIQUE(s)"`
CanPush bool
Created time.Time `xorm:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
} }
var pbs []ProtectedBranch if err := x.Sync2(new(ProtectedBranch)); err != nil {
err := x.Find(&pbs) return fmt.Errorf("Sync2: %v", err)
if err != nil {
return err
} }
for _, pb := range pbs { _, err := x.Cols("can_push").Update(&ProtectedBranch{
if pb.CanPush { CanPush: false,
if _, err = x.ID(pb.ID).Delete(new(ProtectedBranch)); err != nil { })
return err return err
}
}
}
switch {
case setting.UseSQLite3:
log.Warn("Unable to drop columns in SQLite")
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB:
if _, err := x.Exec("ALTER TABLE protected_branch DROP COLUMN can_push"); err != nil {
return fmt.Errorf("DROP COLUMN can_push: %v", err)
}
default:
log.Fatal(4, "Unrecognized DB")
}
return nil
} }

View file

@ -7,36 +7,63 @@ package migrations
import ( import (
"fmt" "fmt"
"code.gitea.io/gitea/models"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
func addDefaultValueToUserProhibitLogin(x *xorm.Engine) (err error) { func removeDuplicateUnitTypes(x *xorm.Engine) error {
user := &models.User{ // RepoUnit describes all units of a repository
ProhibitLogin: false, type RepoUnit struct {
RepoID int64
Type int
} }
if _, err := x.Where("`prohibit_login` IS NULL").Cols("prohibit_login").Update(user); err != nil { // Enumerate all the unit types
const (
UnitTypeCode = iota + 1 // 1 code
UnitTypeIssues // 2 issues
UnitTypePullRequests // 3 PRs
UnitTypeReleases // 4 Releases
UnitTypeWiki // 5 Wiki
UnitTypeExternalWiki // 6 ExternalWiki
UnitTypeExternalTracker // 7 ExternalTracker
)
var externalIssueRepoUnits []RepoUnit
err := x.Where("type = ?", UnitTypeExternalTracker).Find(&externalIssueRepoUnits)
if err != nil {
return fmt.Errorf("Query repositories: %v", err)
}
var externalWikiRepoUnits []RepoUnit
err = x.Where("type = ?", UnitTypeExternalWiki).Find(&externalWikiRepoUnits)
if err != nil {
return fmt.Errorf("Query repositories: %v", err)
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err return err
} }
dialect := x.Dialect().DriverName() for _, repoUnit := range externalIssueRepoUnits {
if _, err = sess.Delete(&RepoUnit{
switch dialect { RepoID: repoUnit.RepoID,
case "mysql": Type: UnitTypeIssues,
_, err = x.Exec("ALTER TABLE user MODIFY `prohibit_login` tinyint(1) NOT NULL DEFAULT 0") }); err != nil {
case "postgres": return fmt.Errorf("Delete repo unit: %v", err)
_, err = x.Exec("ALTER TABLE \"user\" ALTER COLUMN `prohibit_login` SET NOT NULL, ALTER COLUMN `prohibit_login` SET DEFAULT false") }
case "mssql":
// xorm already set DEFAULT 0 for data type BIT in mssql
_, err = x.Exec(`ALTER TABLE [user] ALTER COLUMN "prohibit_login" BIT NOT NULL`)
case "sqlite3":
} }
if err != nil { for _, repoUnit := range externalWikiRepoUnits {
return fmt.Errorf("Error changing user prohibit_login column definition: %v", err) if _, err = sess.Delete(&RepoUnit{
RepoID: repoUnit.RepoID,
Type: UnitTypeWiki,
}); err != nil {
return fmt.Errorf("Delete repo unit: %v", err)
}
} }
return err return sess.Commit()
} }

View file

@ -1,57 +0,0 @@
// Copyright 2017 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 (
"fmt"
"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm"
)
// ReleaseV39 describes the added field for Release
type ReleaseV39 struct {
IsTag bool `xorm:"NOT NULL DEFAULT false"`
}
// TableName will be invoked by XORM to customrize the table name
func (*ReleaseV39) TableName() string {
return "release"
}
func releaseAddColumnIsTagAndSyncTags(x *xorm.Engine) error {
if err := x.Sync2(new(ReleaseV39)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
// For the sake of SQLite3, we can't use x.Iterate here.
offset := 0
pageSize := 20
for {
repos := make([]*models.Repository, 0, pageSize)
if err := x.Table("repository").Asc("id").Limit(pageSize, offset).Find(&repos); err != nil {
return fmt.Errorf("select repos [offset: %d]: %v", offset, err)
}
for _, repo := range repos {
gitRepo, err := git.OpenRepository(repo.RepoPath())
if err != nil {
log.Warn("OpenRepository: %v", err)
continue
}
if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
log.Warn("SyncReleasesWithTags: %v", err)
}
}
if len(repos) < pageSize {
break
}
offset += pageSize
}
return nil
}

View file

@ -1,26 +0,0 @@
// Copyright 2017 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 (
"fmt"
"github.com/go-xorm/xorm"
)
func fixProtectedBranchCanPushValue(x *xorm.Engine) error {
type ProtectedBranch struct {
CanPush bool `xorm:"NOT NULL DEFAULT false"`
}
if err := x.Sync2(new(ProtectedBranch)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
_, err := x.Cols("can_push").Update(&ProtectedBranch{
CanPush: false,
})
return err
}

View file

@ -1,69 +0,0 @@
// Copyright 2017 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 (
"fmt"
"github.com/go-xorm/xorm"
)
func removeDuplicateUnitTypes(x *xorm.Engine) error {
// RepoUnit describes all units of a repository
type RepoUnit struct {
RepoID int64
Type int
}
// Enumerate all the unit types
const (
UnitTypeCode = iota + 1 // 1 code
UnitTypeIssues // 2 issues
UnitTypePullRequests // 3 PRs
UnitTypeReleases // 4 Releases
UnitTypeWiki // 5 Wiki
UnitTypeExternalWiki // 6 ExternalWiki
UnitTypeExternalTracker // 7 ExternalTracker
)
var externalIssueRepoUnits []RepoUnit
err := x.Where("type = ?", UnitTypeExternalTracker).Find(&externalIssueRepoUnits)
if err != nil {
return fmt.Errorf("Query repositories: %v", err)
}
var externalWikiRepoUnits []RepoUnit
err = x.Where("type = ?", UnitTypeExternalWiki).Find(&externalWikiRepoUnits)
if err != nil {
return fmt.Errorf("Query repositories: %v", err)
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
for _, repoUnit := range externalIssueRepoUnits {
if _, err = sess.Delete(&RepoUnit{
RepoID: repoUnit.RepoID,
Type: UnitTypeIssues,
}); err != nil {
return fmt.Errorf("Delete repo unit: %v", err)
}
}
for _, repoUnit := range externalWikiRepoUnits {
if _, err = sess.Delete(&RepoUnit{
RepoID: repoUnit.RepoID,
Type: UnitTypeWiki,
}); err != nil {
return fmt.Errorf("Delete repo unit: %v", err)
}
}
return sess.Commit()
}

View file

@ -5,10 +5,9 @@
package migrations package migrations
import ( import (
"fmt"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
@ -18,7 +17,8 @@ func removeIndexColumnFromRepoUnitTable(x *xorm.Engine) (err error) {
log.Warn("Unable to drop columns in SQLite") log.Warn("Unable to drop columns in SQLite")
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB: case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB:
if _, err := x.Exec("ALTER TABLE repo_unit DROP COLUMN `index`"); err != nil { if _, err := x.Exec("ALTER TABLE repo_unit DROP COLUMN `index`"); err != nil {
return fmt.Errorf("DROP COLUMN index: %v", err) // Ignoring this error in case we run this migration second time (after migration reordering)
log.Warn("DROP COLUMN index: %v", err)
} }
default: default:
log.Fatal(4, "Unrecognized DB") log.Fatal(4, "Unrecognized DB")

View file

@ -8,24 +8,66 @@ import (
"fmt" "fmt"
"time" "time"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
func addLFSLock(x *xorm.Engine) error { func addTimetracking(x *xorm.Engine) error {
// LFSLock see models/lfs_lock.go // RepoUnit describes all units of a repository
type LFSLock struct { type RepoUnit struct {
ID int64 `xorm:"pk autoincr"` ID int64
RepoID int64 `xorm:"INDEX NOT NULL"` RepoID int64 `xorm:"INDEX(s)"`
Owner *models.User `xorm:"-"` Type int `xorm:"INDEX(s)"`
OwnerID int64 `xorm:"INDEX NOT NULL"` Config map[string]interface{} `xorm:"JSON"`
Path string `xorm:"TEXT"` CreatedUnix int64 `xorm:"INDEX CREATED"`
Created time.Time `xorm:"created"` Created time.Time `xorm:"-"`
} }
if err := x.Sync2(new(LFSLock)); err != nil { // Stopwatch see models/issue_stopwatch.go
type Stopwatch struct {
ID int64 `xorm:"pk autoincr"`
IssueID int64 `xorm:"INDEX"`
UserID int64 `xorm:"INDEX"`
Created time.Time `xorm:"-"`
CreatedUnix int64
}
// TrackedTime see models/issue_tracked_time.go
type TrackedTime struct {
ID int64 `xorm:"pk autoincr" json:"id"`
IssueID int64 `xorm:"INDEX" json:"issue_id"`
UserID int64 `xorm:"INDEX" json:"user_id"`
Created time.Time `xorm:"-" json:"created"`
CreatedUnix int64 `json:"-"`
Time int64 `json:"time"`
}
if err := x.Sync2(new(Stopwatch)); err != nil {
return fmt.Errorf("Sync2: %v", err) return fmt.Errorf("Sync2: %v", err)
} }
if err := x.Sync2(new(TrackedTime)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
//Updating existing issue units
units := make([]*RepoUnit, 0, 100)
err := x.Where("`type` = ?", V16UnitTypeIssues).Find(&units)
if err != nil {
return fmt.Errorf("Query repo units: %v", err)
}
for _, unit := range units {
if unit.Config == nil {
unit.Config = make(map[string]interface{})
}
if _, ok := unit.Config["EnableTimetracker"]; !ok {
unit.Config["EnableTimetracker"] = setting.Service.DefaultEnableTimetracking
}
if _, ok := unit.Config["AllowOnlyContributorsToTrackTime"]; !ok {
unit.Config["AllowOnlyContributorsToTrackTime"] = setting.Service.DefaultAllowOnlyContributorsToTrackTime
}
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
return err
}
}
return nil return nil
} }

View file

@ -5,24 +5,51 @@
package migrations package migrations
import ( import (
"fmt" "time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
) )
func addReactions(x *xorm.Engine) error { func migrateProtectedBranchStruct(x *xorm.Engine) error {
// Reaction see models/issue_reaction.go type ProtectedBranch struct {
type Reaction struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"` RepoID int64 `xorm:"UNIQUE(s)"`
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"` BranchName string `xorm:"UNIQUE(s)"`
CommentID int64 `xorm:"INDEX UNIQUE(s)"` CanPush bool
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"` Created time.Time `xorm:"-"`
CreatedUnix int64 `xorm:"INDEX created"` CreatedUnix int64
Updated time.Time `xorm:"-"`
UpdatedUnix int64
} }
if err := x.Sync2(new(Reaction)); err != nil { var pbs []ProtectedBranch
return fmt.Errorf("Sync2: %v", err) err := x.Find(&pbs)
if err != nil {
return err
} }
for _, pb := range pbs {
if pb.CanPush {
if _, err = x.ID(pb.ID).Delete(new(ProtectedBranch)); err != nil {
return err
}
}
}
switch {
case setting.UseSQLite3:
log.Warn("Unable to drop columns in SQLite")
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB:
if _, err := x.Exec("ALTER TABLE protected_branch DROP COLUMN can_push"); err != nil {
// Ignoring this error in case we run this migration second time (after migration reordering)
log.Warn("DROP COLUMN can_push (skipping): %v", err)
}
default:
log.Fatal(4, "Unrecognized DB")
}
return nil return nil
} }

42
models/migrations/v51.go Normal file
View file

@ -0,0 +1,42 @@
// Copyright 2017 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 (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"github.com/go-xorm/xorm"
)
func addDefaultValueToUserProhibitLogin(x *xorm.Engine) (err error) {
user := &models.User{
ProhibitLogin: false,
}
if _, err := x.Where("`prohibit_login` IS NULL").Cols("prohibit_login").Update(user); err != nil {
return err
}
dialect := x.Dialect().DriverName()
switch dialect {
case "mysql":
_, err = x.Exec("ALTER TABLE user MODIFY `prohibit_login` tinyint(1) NOT NULL DEFAULT 0")
case "postgres":
_, err = x.Exec("ALTER TABLE \"user\" ALTER COLUMN `prohibit_login` SET NOT NULL, ALTER COLUMN `prohibit_login` SET DEFAULT false")
case "mssql":
// xorm already set DEFAULT 0 for data type BIT in mssql
_, err = x.Exec(`ALTER TABLE [user] ALTER COLUMN "prohibit_login" BIT NOT NULL`)
case "sqlite3":
}
if err != nil {
// Ignoring this error in case we run this migration second time (after migration reordering)
log.Warn("Error changing user prohibit_login column definition (skipping): %v", err)
}
return nil
}

31
models/migrations/v52.go Normal file
View file

@ -0,0 +1,31 @@
// Copyright 2017 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 (
"fmt"
"time"
"code.gitea.io/gitea/models"
"github.com/go-xorm/xorm"
)
func addLFSLock(x *xorm.Engine) error {
// LFSLock see models/lfs_lock.go
type LFSLock struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX NOT NULL"`
Owner *models.User `xorm:"-"`
OwnerID int64 `xorm:"INDEX NOT NULL"`
Path string `xorm:"TEXT"`
Created time.Time `xorm:"created"`
}
if err := x.Sync2(new(LFSLock)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
return nil
}

28
models/migrations/v53.go Normal file
View file

@ -0,0 +1,28 @@
// Copyright 2017 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 (
"fmt"
"github.com/go-xorm/xorm"
)
func addReactions(x *xorm.Engine) error {
// Reaction see models/issue_reaction.go
type Reaction struct {
ID int64 `xorm:"pk autoincr"`
Type string `xorm:"INDEX UNIQUE(s) NOT NULL"`
IssueID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CommentID int64 `xorm:"INDEX UNIQUE(s)"`
UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"`
CreatedUnix int64 `xorm:"INDEX created"`
}
if err := x.Sync2(new(Reaction)); err != nil {
return fmt.Errorf("Sync2: %v", err)
}
return nil
}