Repository: ariga/atlas
Pull request #1276 (2022-11-08 13:09:55)

sql: expose default diffing capabilities for mysql and sqlite


Patch:
diff --git a/sql/mysql/diff.go b/sql/mysql/diff.go
index 758c23bd..92c44346 100644
--- a/sql/mysql/diff.go
+++ b/sql/mysql/diff.go
@@ -16,6 +16,11 @@ import (
 	"ariga.io/atlas/sql/schema"
 )
 
+// DefaultDiff provides basic diffing capabilities for MySQL dialects.
+// Note, it is recommended to call Open, create a new Driver and use its
+// Differ when a database connection is available.
+var DefaultDiff schema.Differ = &sqlx.Diff{DiffDriver: &diff{}}
+
 // A diff provides a MySQL implementation for sqlx.DiffDriver.
 type diff struct {
 	conn
diff --git a/sql/schema/generate.go b/sql/schema/generate.go
deleted file mode 100644
index a9eb306f..00000000
--- a/sql/schema/generate.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2021-present The Atlas Authors. All rights reserved.
-// This source code is licensed under the Apache 2.0 license found
-// in the LICENSE file in the root directory of this source tree.
-
-package schema
diff --git a/sql/sqlite/diff.go b/sql/sqlite/diff.go
index 17072064..5d55bb37 100644
--- a/sql/sqlite/diff.go
+++ b/sql/sqlite/diff.go
@@ -14,17 +14,22 @@ import (
 	"ariga.io/atlas/sql/schema"
 )
 
-// A Diff provides a SQLite implementation for sqlx.DiffDriver.
-type Diff struct{}
+// DefaultDiff provides basic diffing capabilities for MySQL dialects.
+// Note, it is recommended to call Open, create a new Driver and use its
+// Differ when a database connection is available.
+var DefaultDiff schema.Differ = &sqlx.Diff{DiffDriver: &diff{}}
+
+// A diff provides a SQLite implementation for sqlx.DiffDriver.
+type diff struct{}
 
 // SchemaAttrDiff returns a changeset for migrating schema attributes from one state to the other.
-func (d *Diff) SchemaAttrDiff(_, _ *schema.Schema) []schema.Change {
+func (d *diff) SchemaAttrDiff(_, _ *schema.Schema) []schema.Change {
 	// No special schema attribute diffing for SQLite.
 	return nil
 }
 
 // TableAttrDiff returns a changeset for migrating table attributes from one state to the other.
-func (d *Diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
+func (d *diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
 	var changes []schema.Change
 	switch {
 	case sqlx.Has(from.Attrs, &WithoutRowID{}) && !sqlx.Has(to.Attrs, &WithoutRowID{}):
@@ -40,7 +45,7 @@ func (d *Diff) TableAttrDiff(from, to *schema.Table) ([]schema.Change, error) {
 }
 
 // ColumnChange returns the schema changes (if any) for migrating one column to the other.
-func (d *Diff) ColumnChange(_ *schema.Table, from, to *schema.Column) (schema.ChangeKind, error) {
+func (d *diff) ColumnChange(_ *schema.Table, from, to *schema.Column) (schema.ChangeKind, error) {
 	change := sqlx.CommentChange(from.Attrs, to.Attrs)
 	if from.Type.Null != to.Type.Null {
 		change |= schema.ChangeNull
@@ -62,7 +67,7 @@ func (d *Diff) ColumnChange(_ *schema.Table, from, to *schema.Column) (schema.Ch
 }
 
 // typeChanged reports if the column type was changed.
-func (d *Diff) typeChanged(from, to *schema.Column) (bool, error) {
+func (d *diff) typeChanged(from, to *schema.Column) (bool, error) {
 	fromT, toT := from.Type.Type, to.Type.Type
 	if fromT == nil || toT == nil {
 		return false, fmt.Errorf("sqlite: missing type information for column %q", from.Name)
@@ -72,7 +77,7 @@ func (d *Diff) typeChanged(from, to *schema.Column) (bool, error) {
 }
 
 // defaultChanged reports if the default value of a column was changed.
-func (d *Diff) defaultChanged(from, to *schema.Column) bool {
+func (d *diff) defaultChanged(from, to *schema.Column) bool {
 	d1, ok1 := sqlx.DefaultValue(from)
 	d2, ok2 := sqlx.DefaultValue(to)
 	if ok1 != ok2 {
@@ -87,7 +92,7 @@ func (d *Diff) defaultChanged(from, to *schema.Column) bool {
 }
 
 // generatedChanged reports if the generated expression of a column was changed.
-func (*Diff) generatedChanged(from, to *schema.Column) bool {
+func (*diff) generatedChanged(from, to *schema.Column) bool {
 	var (
 		fromX, toX     schema.GeneratedExpr
 		fromHas, toHas = sqlx.Has(from.Attrs, &fromX), sqlx.Has(to.Attrs, &toX)
@@ -97,7 +102,7 @@ func (*Diff) generatedChanged(from, to *schema.Column) bool {
 
 // IsGeneratedIndexName reports if the index name was generated by the database.
 // See: https://github.com/sqlite/sqlite/blob/e937df8/src/build.c#L3583.
-func (d *Diff) IsGeneratedIndexName(t *schema.Table, idx *schema.Index) bool {
+func (d *diff) IsGeneratedIndexName(t *schema.Table, idx *schema.Index) bool {
 	p := fmt.Sprintf("sqlite_autoindex_%s_", t.Name)
 	if !strings.HasPrefix(idx.Name, p) {
 		return false
@@ -107,18 +112,18 @@ func (d *Diff) IsGeneratedIndexName(t *schema.Table, idx *schema.Index) bool {
 }
 
 // IndexAttrChanged reports if the index attributes were changed.
-func (*Diff) IndexAttrChanged(from, to []schema.Attr) bool {
+func (*diff) IndexAttrChanged(from, to []schema.Attr) bool {
 	var p1, p2 IndexPredicate
 	return sqlx.Has(from, &p1) != sqlx.Has(to, &p2) || (p1.P != p2.P && p1.P != sqlx.MayWrap(p2.P))
 }
 
 // IndexPartAttrChanged reports if the index-part attributes were changed.
-func (*Diff) IndexPartAttrChanged(_, _ *schema.Index, _ int) bool {
+func (*diff) IndexPartAttrChanged(_, _ *schema.Index, _ int) bool {
 	return false
 }
 
 // ReferenceChanged reports if the foreign key referential action was changed.
-func (*Diff) ReferenceChanged(from, to schema.ReferenceOption) bool {
+func (*diff) ReferenceChanged(from, to schema.ReferenceOption) bool {
 	// According to SQLite, if an action is not explicitly
 	// specified, it defaults to "NO ACTION".
 	if from == "" {
@@ -131,7 +136,7 @@ func (*Diff) ReferenceChanged(from, to schema.ReferenceOption) bool {
 }
 
 // Normalize implements the sqlx.Normalizer interface.
-func (d *Diff) Normalize(from, to *schema.Table) error {
+func (d *diff) Normalize(from, to *schema.Table) error {
 	used := make([]bool, len(to.ForeignKeys))
 	// In SQLite, there is no easy way to get the foreign-key constraint
 	// name, except for parsing the CREATE statement. Therefore, we check
diff --git a/sql/sqlite/driver.go b/sql/sqlite/driver.go
index cbbb2bb6..5d800645 100644
--- a/sql/sqlite/driver.go
+++ b/sql/sqlite/driver.go
@@ -83,7 +83,7 @@ func Open(db schema.ExecQuerier) (migrate.Driver, error) {
 	}
 	return &Driver{
 		conn:        c,
-		Differ:      &sqlx.Diff{DiffDriver: &Diff{}},
+		Differ:      &sqlx.Diff{DiffDriver: &diff{}},
 		Inspector:   &inspect{c},
 		PlanApplier: &planApply{c},
 	}, nil


Test patch:
diff --git a/sql/mysql/diff_test.go b/sql/mysql/diff_test.go
index c279a37a..14e6e50a 100644
--- a/sql/mysql/diff_test.go
+++ b/sql/mysql/diff_test.go
@@ -623,3 +623,16 @@ func TestDiff_RealmDiff(t *testing.T) {
 		&schema.AddTable{T: to.Schemas[1].Tables[0]},
 	}, changes)
 }
+
+func TestDefaultDiff(t *testing.T) {
+	changes, err := DefaultDiff.SchemaDiff(
+		schema.New("public").
+			AddTables(
+				schema.NewTable("users").AddColumns(schema.NewIntColumn("id", "int")),
+			),
+		schema.New("public"),
+	)
+	require.NoError(t, err)
+	require.Len(t, changes, 1)
+	require.IsType(t, &schema.DropTable{}, changes[0])
+}
diff --git a/sql/sqlite/diff_test.go b/sql/sqlite/diff_test.go
index aa326fab..dc8e8462 100644
--- a/sql/sqlite/diff_test.go
+++ b/sql/sqlite/diff_test.go
@@ -329,3 +329,16 @@ func TestDiff_SchemaDiff(t *testing.T) {
 		&schema.AddTable{T: to.Tables[1]},
 	}, changes)
 }
+
+func TestDefaultDiff(t *testing.T) {
+	changes, err := DefaultDiff.SchemaDiff(
+		schema.New("main").
+			AddTables(
+				schema.NewTable("users").AddColumns(schema.NewIntColumn("id", "int")),
+			),
+		schema.New("main"),
+	)
+	require.NoError(t, err)
+	require.Len(t, changes, 1)
+	require.IsType(t, &schema.DropTable{}, changes[0])
+}
diff --git a/sql/sqlite/sqlitecheck/sqlitecheck_test.go b/sql/sqlite/sqlitecheck/sqlitecheck_test.go
index b7fd50f5..e2591e5e 100644
--- a/sql/sqlite/sqlitecheck/sqlitecheck_test.go
+++ b/sql/sqlite/sqlitecheck/sqlitecheck_test.go
@@ -8,7 +8,6 @@ import (
 	"context"
 	"testing"
 
-	"ariga.io/atlas/sql/internal/sqlx"
 	"ariga.io/atlas/sql/migrate"
 	"ariga.io/atlas/sql/schema"
 	"ariga.io/atlas/sql/sqlcheck"
@@ -26,7 +25,7 @@ func TestDetectModifyTable(t *testing.T) {
 			Dev: &sqlclient.Client{
 				Driver: func() migrate.Driver {
 					drv := &sqlite.Driver{}
-					drv.Differ = &sqlx.Diff{DiffDriver: &sqlite.Diff{}}
+					drv.Differ = sqlite.DefaultDiff
 					return drv
 				}(),
 			},