Add Sync options to ignore constrains and indices (#2320)
needed for https://github.com/woodpecker-ci/woodpecker/pull/2117 Reviewed-on: https://gitea.com/xorm/xorm/pulls/2320 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: 6543 <6543@obermui.de> Co-committed-by: 6543 <6543@obermui.de>
This commit is contained in:
parent
ac84217e14
commit
db7c264062
|
@ -121,6 +121,7 @@ type EngineInterface interface {
|
||||||
ShowSQL(show ...bool)
|
ShowSQL(show ...bool)
|
||||||
Sync(...interface{}) error
|
Sync(...interface{}) error
|
||||||
Sync2(...interface{}) error
|
Sync2(...interface{}) error
|
||||||
|
SyncWithOptions(SyncOptions, ...interface{}) (*SyncResult, error)
|
||||||
StoreEngine(storeEngine string) *Session
|
StoreEngine(storeEngine string) *Session
|
||||||
TableInfo(bean interface{}) (*schemas.Table, error)
|
TableInfo(bean interface{}) (*schemas.Table, error)
|
||||||
TableName(interface{}, ...bool) string
|
TableName(interface{}, ...bool) string
|
||||||
|
|
54
sync.go
54
sync.go
|
@ -13,6 +13,10 @@ import (
|
||||||
|
|
||||||
type SyncOptions struct {
|
type SyncOptions struct {
|
||||||
WarnIfDatabaseColumnMissed bool
|
WarnIfDatabaseColumnMissed bool
|
||||||
|
// IgnoreConstrains will not add, delete or update unique constrains
|
||||||
|
IgnoreConstrains bool
|
||||||
|
// IgnoreIndices will not add or delete indices
|
||||||
|
IgnoreIndices bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type SyncResult struct{}
|
type SyncResult struct{}
|
||||||
|
@ -49,6 +53,8 @@ func (session *Session) Sync2(beans ...interface{}) error {
|
||||||
func (session *Session) Sync(beans ...interface{}) error {
|
func (session *Session) Sync(beans ...interface{}) error {
|
||||||
_, err := session.SyncWithOptions(SyncOptions{
|
_, err := session.SyncWithOptions(SyncOptions{
|
||||||
WarnIfDatabaseColumnMissed: false,
|
WarnIfDatabaseColumnMissed: false,
|
||||||
|
IgnoreConstrains: false,
|
||||||
|
IgnoreIndices: false,
|
||||||
}, beans...)
|
}, beans...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -103,15 +109,20 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...interface{})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = session.createUniques(bean)
|
if !opts.IgnoreConstrains {
|
||||||
if err != nil {
|
err = session.createUniques(bean)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = session.createIndexes(bean)
|
if !opts.IgnoreIndices {
|
||||||
if err != nil {
|
err = session.createIndexes(bean)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,9 +219,12 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// indices found in orig table
|
||||||
foundIndexNames := make(map[string]bool)
|
foundIndexNames := make(map[string]bool)
|
||||||
|
// indices to be added
|
||||||
addedNames := make(map[string]*schemas.Index)
|
addedNames := make(map[string]*schemas.Index)
|
||||||
|
|
||||||
|
// drop indices that exist in orig and new table schema but are not equal
|
||||||
for name, index := range table.Indexes {
|
for name, index := range table.Indexes {
|
||||||
var oriIndex *schemas.Index
|
var oriIndex *schemas.Index
|
||||||
for name2, index2 := range oriTable.Indexes {
|
for name2, index2 := range oriTable.Indexes {
|
||||||
|
@ -221,15 +235,13 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if oriIndex != nil {
|
if oriIndex != nil && oriIndex.Type != index.Type {
|
||||||
if oriIndex.Type != index.Type {
|
sql := engine.dialect.DropIndexSQL(tbNameWithSchema, oriIndex)
|
||||||
sql := engine.dialect.DropIndexSQL(tbNameWithSchema, oriIndex)
|
_, err = session.exec(sql)
|
||||||
_, err = session.exec(sql)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
oriIndex = nil
|
|
||||||
}
|
}
|
||||||
|
oriIndex = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if oriIndex == nil {
|
if oriIndex == nil {
|
||||||
|
@ -237,8 +249,17 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// drop all indices that do not exist in new schema or have changed
|
||||||
for name2, index2 := range oriTable.Indexes {
|
for name2, index2 := range oriTable.Indexes {
|
||||||
if _, ok := foundIndexNames[name2]; !ok {
|
if _, ok := foundIndexNames[name2]; !ok {
|
||||||
|
// ignore based on there type
|
||||||
|
if (index2.Type == schemas.IndexType && opts.IgnoreIndices) ||
|
||||||
|
(index2.Type == schemas.UniqueType && opts.IgnoreConstrains) {
|
||||||
|
// make sure we do not add a index with same name later
|
||||||
|
delete(addedNames, name2)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
sql := engine.dialect.DropIndexSQL(tbNameWithSchema, index2)
|
sql := engine.dialect.DropIndexSQL(tbNameWithSchema, index2)
|
||||||
_, err = session.exec(sql)
|
_, err = session.exec(sql)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -247,12 +268,13 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add new indices because either they did not exist before or were dropped to update them
|
||||||
for name, index := range addedNames {
|
for name, index := range addedNames {
|
||||||
if index.Type == schemas.UniqueType {
|
if index.Type == schemas.UniqueType && !opts.IgnoreConstrains {
|
||||||
session.statement.RefTable = table
|
session.statement.RefTable = table
|
||||||
session.statement.SetTableName(tbNameWithSchema)
|
session.statement.SetTableName(tbNameWithSchema)
|
||||||
err = session.addUnique(tbNameWithSchema, name)
|
err = session.addUnique(tbNameWithSchema, name)
|
||||||
} else if index.Type == schemas.IndexType {
|
} else if index.Type == schemas.IndexType && !opts.IgnoreIndices {
|
||||||
session.statement.RefTable = table
|
session.statement.RefTable = table
|
||||||
session.statement.SetTableName(tbNameWithSchema)
|
session.statement.SetTableName(tbNameWithSchema)
|
||||||
err = session.addIndex(tbNameWithSchema, name)
|
err = session.addIndex(tbNameWithSchema, name)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"xorm.io/xorm"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -645,3 +646,101 @@ func TestCollate(t *testing.T) {
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SyncWithOpts1 struct {
|
||||||
|
Id int64
|
||||||
|
Index int `xorm:"index"`
|
||||||
|
Unique int `xorm:"unique"`
|
||||||
|
Group1 int `xorm:"index(ttt)"`
|
||||||
|
Group2 int `xorm:"index(ttt)"`
|
||||||
|
UniGroup1 int `xorm:"unique(lll)"`
|
||||||
|
UniGroup2 int `xorm:"unique(lll)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SyncWithOpts1) TableName() string {
|
||||||
|
return "sync_with_opts"
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyncWithOpts2 struct {
|
||||||
|
Id int64
|
||||||
|
Index int `xorm:"index"`
|
||||||
|
Unique int `xorm:""`
|
||||||
|
Group1 int `xorm:"index(ttt)"`
|
||||||
|
Group2 int `xorm:"index(ttt)"`
|
||||||
|
UniGroup1 int `xorm:""`
|
||||||
|
UniGroup2 int `xorm:"unique(lll)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SyncWithOpts2) TableName() string {
|
||||||
|
return "sync_with_opts"
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyncWithOpts3 struct {
|
||||||
|
Id int64
|
||||||
|
Index int `xorm:""`
|
||||||
|
Unique int `xorm:"unique"`
|
||||||
|
Group1 int `xorm:""`
|
||||||
|
Group2 int `xorm:"index(ttt)"`
|
||||||
|
UniGroup1 int `xorm:"unique(lll)"`
|
||||||
|
UniGroup2 int `xorm:"unique(lll)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SyncWithOpts3) TableName() string {
|
||||||
|
return "sync_with_opts"
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncWithOptions(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareEngine())
|
||||||
|
|
||||||
|
// ignore indices and constrains
|
||||||
|
result, err := testEngine.SyncWithOptions(xorm.SyncOptions{IgnoreIndices: true, IgnoreConstrains: true}, &SyncWithOpts1{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, result)
|
||||||
|
assert.Len(t, getIndicesOfBeanFromDB(t, &SyncWithOpts1{}), 0)
|
||||||
|
|
||||||
|
// only ignore indices
|
||||||
|
result, err = testEngine.SyncWithOptions(xorm.SyncOptions{IgnoreConstrains: true}, &SyncWithOpts2{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, result)
|
||||||
|
indices := getIndicesOfBeanFromDB(t, &SyncWithOpts1{})
|
||||||
|
assert.Len(t, indices, 2)
|
||||||
|
assert.ElementsMatch(t, []string{"ttt", "index"}, getKeysFromMap(indices))
|
||||||
|
|
||||||
|
// only ignore constrains
|
||||||
|
result, err = testEngine.SyncWithOptions(xorm.SyncOptions{IgnoreIndices: true}, &SyncWithOpts3{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, result)
|
||||||
|
indices = getIndicesOfBeanFromDB(t, &SyncWithOpts1{})
|
||||||
|
assert.Len(t, indices, 4)
|
||||||
|
assert.ElementsMatch(t, []string{"ttt", "index", "unique", "lll"}, getKeysFromMap(indices))
|
||||||
|
|
||||||
|
tableInfoFromStruct, _ := testEngine.TableInfo(&SyncWithOpts1{})
|
||||||
|
assert.ElementsMatch(t, getKeysFromMap(tableInfoFromStruct.Indexes), getKeysFromMap(getIndicesOfBeanFromDB(t, &SyncWithOpts1{})))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIndicesOfBeanFromDB(t *testing.T, bean interface{}) map[string]*schemas.Index {
|
||||||
|
dbm, err := testEngine.DBMetas()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
tName := testEngine.TableName(bean)
|
||||||
|
var tSchema *schemas.Table
|
||||||
|
for _, t := range dbm {
|
||||||
|
if t.Name == tName {
|
||||||
|
tSchema = t
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !assert.NotNil(t, tSchema) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tSchema.Indexes
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKeysFromMap(m map[string]*schemas.Index) []string {
|
||||||
|
var ss []string
|
||||||
|
for k := range m {
|
||||||
|
ss = append(ss, k)
|
||||||
|
}
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue