Skip to content

Commit a80707d

Browse files
authored
Create and drop view (#6097)
* create view * add comment * fix test * check param and add comment
1 parent 391c961 commit a80707d

File tree

4 files changed

+71
-6
lines changed

4 files changed

+71
-6
lines changed

errors.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ var (
2323
ErrModelValueRequired = errors.New("model value required")
2424
// ErrModelAccessibleFieldsRequired model accessible fields required
2525
ErrModelAccessibleFieldsRequired = errors.New("model accessible fields required")
26+
// ErrSubQueryRequired sub query required
27+
ErrSubQueryRequired = errors.New("sub query required")
2628
// ErrInvalidData unsupported data
2729
ErrInvalidData = errors.New("unsupported data")
2830
// ErrUnsupportedDriver unsupported driver

migrator.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ func (db *DB) AutoMigrate(dst ...interface{}) error {
3030

3131
// ViewOption view option
3232
type ViewOption struct {
33-
Replace bool
34-
CheckOption string
35-
Query *DB
33+
Replace bool // If true, exec `CREATE`. If false, exec `CREATE OR REPLACE`
34+
CheckOption string // optional. e.g. `WITH [ CASCADED | LOCAL ] CHECK OPTION`
35+
Query *DB // required subquery.
3636
}
3737

3838
// ColumnType column type interface

migrator/migrator.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,14 +557,44 @@ func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
557557
return columnTypes, execErr
558558
}
559559

560-
// CreateView create view
560+
// CreateView create view from Query in gorm.ViewOption.
561+
// Query in gorm.ViewOption is a [subquery]
562+
//
563+
// // CREATE VIEW `user_view` AS SELECT * FROM `users` WHERE age > 20
564+
// q := DB.Model(&User{}).Where("age > ?", 20)
565+
// DB.Debug().Migrator().CreateView("user_view", gorm.ViewOption{Query: q})
566+
//
567+
// // CREATE OR REPLACE VIEW `users_view` AS SELECT * FROM `users` WITH CHECK OPTION
568+
// q := DB.Model(&User{})
569+
// DB.Debug().Migrator().CreateView("user_view", gorm.ViewOption{Query: q, Replace: true, CheckOption: "WITH CHECK OPTION"})
570+
//
571+
// [subquery]: https://gorm.io/docs/advanced_query.html#SubQuery
561572
func (m Migrator) CreateView(name string, option gorm.ViewOption) error {
562-
return gorm.ErrNotImplemented
573+
if option.Query == nil {
574+
return gorm.ErrSubQueryRequired
575+
}
576+
577+
sql := new(strings.Builder)
578+
sql.WriteString("CREATE ")
579+
if option.Replace {
580+
sql.WriteString("OR REPLACE ")
581+
}
582+
sql.WriteString("VIEW ")
583+
m.QuoteTo(sql, name)
584+
sql.WriteString(" AS ")
585+
586+
m.DB.Statement.AddVar(sql, option.Query)
587+
588+
if option.CheckOption != "" {
589+
sql.WriteString(" ")
590+
sql.WriteString(option.CheckOption)
591+
}
592+
return m.DB.Exec(m.Explain(sql.String(), m.DB.Statement.Vars...)).Error
563593
}
564594

565595
// DropView drop view
566596
func (m Migrator) DropView(name string) error {
567-
return gorm.ErrNotImplemented
597+
return m.DB.Exec("DROP VIEW IF EXISTS ?", clause.Table{Name: name}).Error
568598
}
569599

570600
func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) {

tests/migrate_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,3 +1509,36 @@ func TestMigrateIgnoreRelations(t *testing.T) {
15091509
t.Errorf("RelationModel2 should not be migrated")
15101510
}
15111511
}
1512+
1513+
func TestMigrateView(t *testing.T) {
1514+
DB.Save(GetUser("joins-args-db", Config{Pets: 2}))
1515+
1516+
if err := DB.Migrator().CreateView("invalid_users_pets", gorm.ViewOption{Query: nil}); err != gorm.ErrSubQueryRequired {
1517+
t.Fatalf("no view should be created, got %v", err)
1518+
}
1519+
1520+
query := DB.Model(&User{}).
1521+
Select("users.id as users_id, users.name as users_name, pets.id as pets_id, pets.name as pets_name").
1522+
Joins("inner join pets on pets.user_id = users.id")
1523+
1524+
if err := DB.Migrator().CreateView("users_pets", gorm.ViewOption{Query: query}); err != nil {
1525+
t.Fatalf("Failed to crate view, got %v", err)
1526+
}
1527+
1528+
var count int64
1529+
if err := DB.Table("users_pets").Count(&count).Error; err != nil {
1530+
t.Fatalf("should found created view")
1531+
}
1532+
1533+
if err := DB.Migrator().DropView("users_pets"); err != nil {
1534+
t.Fatalf("Failed to drop view, got %v", err)
1535+
}
1536+
1537+
query = DB.Model(&User{}).Where("age > ?", 20)
1538+
if err := DB.Migrator().CreateView("users_view", gorm.ViewOption{Query: query}); err != nil {
1539+
t.Fatalf("Failed to crate view, got %v", err)
1540+
}
1541+
if err := DB.Migrator().DropView("users_view"); err != nil {
1542+
t.Fatalf("Failed to drop view, got %v", err)
1543+
}
1544+
}

0 commit comments

Comments
 (0)