mirror of
https://github.com/glebarez/sqlite.git
synced 2025-10-05 07:36:58 +08:00
refactor: distinguish between Unique and UniqueIndex (#156)
* refactor: distinguish between Unique and UniqueIndex * take care of unique created by old version * update gorm to master latest (for https://github.com/go-gorm/gorm/pull/6386) * merge
This commit is contained in:
43
ddlmod.go
43
ddlmod.go
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
sqliteSeparator = "`|\"|'|\t"
|
sqliteSeparator = "`|\"|'|\t"
|
||||||
|
uniqueRegexp = regexp.MustCompile(fmt.Sprintf(`^CONSTRAINT [%v]?[\w-]+[%v]? UNIQUE (.*)$`, sqliteSeparator, sqliteSeparator))
|
||||||
indexRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)CREATE(?: UNIQUE)? INDEX [%v]?[\w\d-]+[%v]?(?s:.*?)ON (.*)$`, sqliteSeparator, sqliteSeparator))
|
indexRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)CREATE(?: UNIQUE)? INDEX [%v]?[\w\d-]+[%v]?(?s:.*?)ON (.*)$`, sqliteSeparator, sqliteSeparator))
|
||||||
tableRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)(CREATE TABLE [%v]?[\w\d-]+[%v]?)(?:\s*\((.*)\))?`, sqliteSeparator, sqliteSeparator))
|
tableRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)(CREATE TABLE [%v]?[\w\d-]+[%v]?)(?:\s*\((.*)\))?`, sqliteSeparator, sqliteSeparator))
|
||||||
separatorRegexp = regexp.MustCompile(fmt.Sprintf("[%v]", sqliteSeparator))
|
separatorRegexp = regexp.MustCompile(fmt.Sprintf("[%v]", sqliteSeparator))
|
||||||
@@ -103,11 +104,24 @@ func parseDDL(strs ...string) (*ddl, error) {
|
|||||||
|
|
||||||
for _, f := range result.fields {
|
for _, f := range result.fields {
|
||||||
fUpper := strings.ToUpper(f)
|
fUpper := strings.ToUpper(f)
|
||||||
if strings.HasPrefix(fUpper, "CHECK") ||
|
if strings.HasPrefix(fUpper, "CHECK") {
|
||||||
strings.HasPrefix(fUpper, "CONSTRAINT") {
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(fUpper, "CONSTRAINT") {
|
||||||
|
matches := uniqueRegexp.FindStringSubmatch(f)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
if columns := getAllColumns(matches[1]); len(columns) == 1 {
|
||||||
|
for idx, column := range result.columns {
|
||||||
|
if column.NameValue.String == columns[0] {
|
||||||
|
column.UniqueValue = sql.NullBool{Bool: true, Valid: true}
|
||||||
|
result.columns[idx] = column
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(fUpper, "PRIMARY KEY") {
|
if strings.HasPrefix(fUpper, "PRIMARY KEY") {
|
||||||
for _, name := range getAllColumns(f) {
|
for _, name := range getAllColumns(f) {
|
||||||
for idx, column := range result.columns {
|
for idx, column := range result.columns {
|
||||||
@@ -159,14 +173,7 @@ func parseDDL(strs ...string) (*ddl, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if matches := indexRegexp.FindStringSubmatch(str); len(matches) > 0 {
|
} else if matches := indexRegexp.FindStringSubmatch(str); len(matches) > 0 {
|
||||||
for _, column := range getAllColumns(matches[1]) {
|
// don't report Unique by UniqueIndex
|
||||||
for idx, c := range result.columns {
|
|
||||||
if c.NameValue.String == column {
|
|
||||||
c.UniqueValue = sql.NullBool{Bool: strings.ToUpper(strings.Fields(str)[1]) == "UNIQUE", Valid: true}
|
|
||||||
result.columns[idx] = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("invalid DDL")
|
return nil, errors.New("invalid DDL")
|
||||||
}
|
}
|
||||||
@@ -268,20 +275,6 @@ func (d *ddl) getColumns() []string {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ddl) alterColumn(name, sql string) bool {
|
|
||||||
reg := regexp.MustCompile("^(`|'|\"| )" + regexp.QuoteMeta(name) + "(`|'|\"| ) .*?$")
|
|
||||||
|
|
||||||
for i := 0; i < len(d.fields); i++ {
|
|
||||||
if reg.MatchString(d.fields[i]) {
|
|
||||||
d.fields[i] = sql
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.fields = append(d.fields, sql)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *ddl) removeColumn(name string) bool {
|
func (d *ddl) removeColumn(name string) bool {
|
||||||
reg := regexp.MustCompile("^(`|'|\"| )" + regexp.QuoteMeta(name) + "(`|'|\"| ) .*?$")
|
reg := regexp.MustCompile("^(`|'|\"| )" + regexp.QuoteMeta(name) + "(`|'|\"| ) .*?$")
|
||||||
|
|
||||||
|
@@ -16,11 +16,12 @@ func TestParseDDL(t *testing.T) {
|
|||||||
columns []migrator.ColumnType
|
columns []migrator.ColumnType
|
||||||
}{
|
}{
|
||||||
{"with_fk", []string{
|
{"with_fk", []string{
|
||||||
"CREATE TABLE `notes` (`id` integer NOT NULL,`text` varchar(500) DEFAULT \"hello\",`age` integer DEFAULT 18,`user_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))",
|
"CREATE TABLE `notes` (" +
|
||||||
|
"`id` integer NOT NULL,`text` varchar(500) DEFAULT \"hello\",`age` integer DEFAULT 18,`user_id` integer,PRIMARY KEY (`id`),CONSTRAINT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))",
|
||||||
"CREATE UNIQUE INDEX `idx_profiles_refer` ON `profiles`(`text`)",
|
"CREATE UNIQUE INDEX `idx_profiles_refer` ON `profiles`(`text`)",
|
||||||
}, 6, []migrator.ColumnType{
|
}, 6, []migrator.ColumnType{
|
||||||
{NameValue: sql.NullString{String: "id", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, PrimaryKeyValue: sql.NullBool{Bool: true, Valid: true}, NullableValue: sql.NullBool{Valid: true}, UniqueValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}},
|
{NameValue: sql.NullString{String: "id", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, PrimaryKeyValue: sql.NullBool{Bool: true, Valid: true}, NullableValue: sql.NullBool{Valid: true}, UniqueValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}},
|
||||||
{NameValue: sql.NullString{String: "text", Valid: true}, DataTypeValue: sql.NullString{String: "varchar", Valid: true}, LengthValue: sql.NullInt64{Int64: 500, Valid: true}, ColumnTypeValue: sql.NullString{String: "varchar(500)", Valid: true}, DefaultValueValue: sql.NullString{String: "hello", Valid: true}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
|
{NameValue: sql.NullString{String: "text", Valid: true}, DataTypeValue: sql.NullString{String: "varchar", Valid: true}, LengthValue: sql.NullInt64{Int64: 500, Valid: true}, ColumnTypeValue: sql.NullString{String: "varchar(500)", Valid: true}, DefaultValueValue: sql.NullString{String: "hello", Valid: true}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Bool: false, Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
|
||||||
{NameValue: sql.NullString{String: "age", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, DefaultValueValue: sql.NullString{String: "18", Valid: true}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
|
{NameValue: sql.NullString{String: "age", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, DefaultValueValue: sql.NullString{String: "18", Valid: true}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
|
||||||
{NameValue: sql.NullString{String: "user_id", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
|
{NameValue: sql.NullString{String: "user_id", Valid: true}, DataTypeValue: sql.NullString{String: "integer", Valid: true}, ColumnTypeValue: sql.NullString{String: "integer", Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, NullableValue: sql.NullBool{Bool: true, Valid: true}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
|
||||||
},
|
},
|
||||||
@@ -56,28 +57,54 @@ func TestParseDDL(t *testing.T) {
|
|||||||
ColumnTypeValue: sql.NullString{String: "int", Valid: true},
|
ColumnTypeValue: sql.NullString{String: "int", Valid: true},
|
||||||
NullableValue: sql.NullBool{Bool: false, Valid: true},
|
NullableValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
DefaultValueValue: sql.NullString{Valid: false},
|
DefaultValueValue: sql.NullString{Valid: false},
|
||||||
UniqueValue: sql.NullBool{Bool: true, Valid: true},
|
UniqueValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
PrimaryKeyValue: sql.NullBool{Valid: true},
|
PrimaryKeyValue: sql.NullBool{Valid: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
"unique index",
|
"unique index",
|
||||||
[]string{
|
[]string{
|
||||||
"CREATE TABLE `test-b` (`field` integer NOT NULL)",
|
"CREATE TABLE `test-b` (`field` integer NOT NULL)",
|
||||||
"CREATE UNIQUE INDEX `idx_uq` ON `test-b`(`field`) WHERE field = 0",
|
"CREATE UNIQUE INDEX `idx_uq` ON `test-b`(`field`) WHERE field = 0",
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
[]migrator.ColumnType{
|
[]migrator.ColumnType{{
|
||||||
{
|
|
||||||
NameValue: sql.NullString{String: "field", Valid: true},
|
NameValue: sql.NullString{String: "field", Valid: true},
|
||||||
DataTypeValue: sql.NullString{String: "integer", Valid: true},
|
DataTypeValue: sql.NullString{String: "integer", Valid: true},
|
||||||
ColumnTypeValue: sql.NullString{String: "integer", Valid: true},
|
ColumnTypeValue: sql.NullString{String: "integer", Valid: true},
|
||||||
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
|
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
UniqueValue: sql.NullBool{Bool: true, Valid: true},
|
UniqueValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
NullableValue: sql.NullBool{Bool: false, Valid: true},
|
NullableValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
"normal index",
|
||||||
|
[]string{
|
||||||
|
"CREATE TABLE `test-c` (`field` integer NOT NULL)",
|
||||||
|
"CREATE INDEX `idx_uq` ON `test-c`(`field`)",
|
||||||
},
|
},
|
||||||
|
1,
|
||||||
|
[]migrator.ColumnType{{
|
||||||
|
NameValue: sql.NullString{String: "field", Valid: true},
|
||||||
|
DataTypeValue: sql.NullString{String: "integer", Valid: true},
|
||||||
|
ColumnTypeValue: sql.NullString{String: "integer", Valid: true},
|
||||||
|
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
|
UniqueValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
|
NullableValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
|
}},
|
||||||
|
}, {
|
||||||
|
"unique constraint",
|
||||||
|
[]string{
|
||||||
|
"CREATE TABLE `unique_struct` (`name` text,CONSTRAINT `uni_unique_struct_name` UNIQUE (`name`))",
|
||||||
},
|
},
|
||||||
|
2,
|
||||||
|
[]migrator.ColumnType{{
|
||||||
|
NameValue: sql.NullString{String: "name", Valid: true},
|
||||||
|
DataTypeValue: sql.NullString{String: "text", Valid: true},
|
||||||
|
ColumnTypeValue: sql.NullString{String: "text", Valid: true},
|
||||||
|
PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true},
|
||||||
|
UniqueValue: sql.NullBool{Bool: true, Valid: true},
|
||||||
|
NullableValue: sql.NullBool{Bool: true, Valid: true},
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"non-unique index",
|
"non-unique index",
|
||||||
|
2
go.mod
2
go.mod
@@ -4,7 +4,7 @@ go 1.20
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.17
|
github.com/mattn/go-sqlite3 v1.14.17
|
||||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55
|
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
2
go.sum
2
go.sum
@@ -8,3 +8,5 @@ gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU=
|
|||||||
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 h1:sC1Xj4TYrLqg1n3AN10w871An7wJM0gzgcm8jkIkECQ=
|
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 h1:sC1Xj4TYrLqg1n3AN10w871An7wJM0gzgcm8jkIkECQ=
|
||||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
|
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde h1:9DShaph9qhkIYw7QF91I/ynrr4cOO2PZra2PFD7Mfeg=
|
||||||
|
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
96
migrator.go
96
migrator.go
@@ -79,14 +79,28 @@ func (m Migrator) AlterColumn(value interface{}, name string) error {
|
|||||||
return m.RunWithoutForeignKey(func() error {
|
return m.RunWithoutForeignKey(func() error {
|
||||||
return m.recreateTable(value, nil, func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) {
|
return m.recreateTable(value, nil, func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) {
|
||||||
if field := stmt.Schema.LookUpField(name); field != nil {
|
if field := stmt.Schema.LookUpField(name); field != nil {
|
||||||
if ddl.alterColumn(field.DBName, fmt.Sprintf("`%s` ?", field.DBName)) {
|
var sqlArgs []interface{}
|
||||||
return nil, nil, fmt.Errorf("field `%s` not found in origin ddl, ddl= '%s'", name, ddl.compile())
|
for i, f := range ddl.fields {
|
||||||
|
if matches := columnRegexp.FindStringSubmatch(f); len(matches) > 1 && matches[1] == field.DBName {
|
||||||
|
ddl.fields[i] = fmt.Sprintf("`%v` ?", field.DBName)
|
||||||
|
sqlArgs = []interface{}{m.FullDataTypeOf(field)}
|
||||||
|
// table created by old version might look like `CREATE TABLE ? (? varchar(10) UNIQUE)`.
|
||||||
|
// FullDataTypeOf doesn't contain UNIQUE, so we need to add unique constraint.
|
||||||
|
if strings.Contains(strings.ToUpper(matches[3]), " UNIQUE") {
|
||||||
|
uniName := m.DB.NamingStrategy.UniqueName(stmt.Table, field.DBName)
|
||||||
|
uni, _ := m.GuessConstraintInterfaceAndTable(stmt, uniName)
|
||||||
|
if uni != nil {
|
||||||
|
uniSQL, uniArgs := uni.Build()
|
||||||
|
ddl.addConstraint(uniName, uniSQL)
|
||||||
|
sqlArgs = append(sqlArgs, uniArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ddl, []interface{}{m.FullDataTypeOf(field)}, nil
|
|
||||||
}
|
}
|
||||||
|
break
|
||||||
return nil, nil, fmt.Errorf("failed to alter field with name `%s`", name)
|
}
|
||||||
|
}
|
||||||
|
return ddl, sqlArgs, nil
|
||||||
|
}
|
||||||
|
return nil, nil, fmt.Errorf("failed to alter field with name %v", name)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -153,7 +167,7 @@ func (m Migrator) DropColumn(value interface{}, name string) error {
|
|||||||
|
|
||||||
func (m Migrator) CreateConstraint(value interface{}, name string) error {
|
func (m Migrator) CreateConstraint(value interface{}, name string) error {
|
||||||
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||||
constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
|
constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
|
||||||
|
|
||||||
return m.recreateTable(value, &table,
|
return m.recreateTable(value, &table,
|
||||||
func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) {
|
func(ddl *ddl, stmt *gorm.Statement) (*ddl, []interface{}, error) {
|
||||||
@@ -164,12 +178,8 @@ func (m Migrator) CreateConstraint(value interface{}, name string) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if constraint != nil {
|
if constraint != nil {
|
||||||
constraintName = constraint.Name
|
constraintName = constraint.GetName()
|
||||||
constraintSql, constraintValues = buildConstraint(constraint)
|
constraintSql, constraintValues = constraint.Build()
|
||||||
} else if chk != nil {
|
|
||||||
constraintName = chk.Name
|
|
||||||
constraintSql = "CONSTRAINT ? CHECK (?)"
|
|
||||||
constraintValues = []interface{}{clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint}}
|
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
@@ -182,11 +192,9 @@ func (m Migrator) CreateConstraint(value interface{}, name string) error {
|
|||||||
|
|
||||||
func (m Migrator) DropConstraint(value interface{}, name string) error {
|
func (m Migrator) DropConstraint(value interface{}, name string) error {
|
||||||
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||||
constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
|
constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
|
||||||
if constraint != nil {
|
if constraint != nil {
|
||||||
name = constraint.Name
|
name = constraint.GetName()
|
||||||
} else if chk != nil {
|
|
||||||
name = chk.Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.recreateTable(value, &table,
|
return m.recreateTable(value, &table,
|
||||||
@@ -200,11 +208,9 @@ func (m Migrator) DropConstraint(value interface{}, name string) error {
|
|||||||
func (m Migrator) HasConstraint(value interface{}, name string) bool {
|
func (m Migrator) HasConstraint(value interface{}, name string) bool {
|
||||||
var count int64
|
var count int64
|
||||||
m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||||
constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
|
constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
|
||||||
if constraint != nil {
|
if constraint != nil {
|
||||||
name = constraint.Name
|
name = constraint.GetName()
|
||||||
} else if chk != nil {
|
|
||||||
name = chk.Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.DB.Raw(
|
m.DB.Raw(
|
||||||
@@ -317,26 +323,44 @@ func (m Migrator) DropIndex(value interface{}, name string) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) {
|
type Index struct {
|
||||||
sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??"
|
Seq int
|
||||||
if constraint.OnDelete != "" {
|
Name string
|
||||||
sql += " ON DELETE " + constraint.OnDelete
|
Unique bool
|
||||||
|
Origin string
|
||||||
|
Partial bool
|
||||||
}
|
}
|
||||||
|
|
||||||
if constraint.OnUpdate != "" {
|
// GetIndexes return Indexes []gorm.Index and execErr error,
|
||||||
sql += " ON UPDATE " + constraint.OnUpdate
|
// See the [doc]
|
||||||
|
//
|
||||||
|
// [doc]: https://www.sqlite.org/pragma.html#pragma_index_list
|
||||||
|
func (m Migrator) GetIndexes(value interface{}) ([]gorm.Index, error) {
|
||||||
|
indexes := make([]gorm.Index, 0)
|
||||||
|
err := m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||||
|
rst := make([]*Index, 0)
|
||||||
|
if err := m.DB.Debug().Raw(fmt.Sprintf("PRAGMA index_list(%q)", stmt.Table)).Scan(&rst).Error; err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
for _, index := range rst {
|
||||||
var foreignKeys, references []interface{}
|
if index.Origin == "u" { // skip the index was created by a UNIQUE constraint
|
||||||
for _, field := range constraint.ForeignKeys {
|
continue
|
||||||
foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName})
|
|
||||||
}
|
}
|
||||||
|
var columns []string
|
||||||
for _, field := range constraint.References {
|
if err := m.DB.Raw(fmt.Sprintf("SELECT name from PRAGMA_index_info(%q)", index.Name)).Scan(&columns).Error; err != nil { // alias `PRAGMA index_info(?)`
|
||||||
references = append(references, clause.Column{Name: field.DBName})
|
return err
|
||||||
}
|
}
|
||||||
results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)
|
indexes = append(indexes, &migrator.Index{
|
||||||
return
|
TableName: stmt.Table,
|
||||||
|
NameValue: index.Name,
|
||||||
|
ColumnList: columns,
|
||||||
|
PrimaryKeyValue: sql.NullBool{Bool: index.Origin == "pk", Valid: true}, // The exceptions are INTEGER PRIMARY KEY
|
||||||
|
UniqueValue: sql.NullBool{Bool: index.Unique, Valid: true},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return indexes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Migrator) getRawDDL(table string) (string, error) {
|
func (m Migrator) getRawDDL(table string) (string, error) {
|
||||||
|
Reference in New Issue
Block a user