Repository: ariga/atlas
Pull request #1875 (2023-07-18 11:40:45)

sql: share the same conn between schema interfaces


Patch:
diff --git a/sql/mysql/diff.go b/sql/mysql/diff.go
index 37068929..fcb30a0b 100644
--- a/sql/mysql/diff.go
+++ b/sql/mysql/diff.go
@@ -23,7 +23,7 @@ var DefaultDiff schema.Differ = &sqlx.Diff{DiffDriver: &diff{conn: noConn}}
 
 // A diff provides a MySQL implementation for sqlx.DiffDriver.
 type diff struct {
-	conn
+	*conn
 	// charset to collation mapping.
 	// See, internal directory.
 	ch2co, co2ch struct {
diff --git a/sql/mysql/driver.go b/sql/mysql/driver.go
index 46dbfba9..b96970eb 100644
--- a/sql/mysql/driver.go
+++ b/sql/mysql/driver.go
@@ -23,7 +23,7 @@ type (
 	// Driver represents a MySQL driver for introspecting database schemas,
 	// generating diff between schema elements and apply migrations changes.
 	Driver struct {
-		conn
+		*conn
 		schema.Differ
 		schema.Inspector
 		migrate.PlanApplier
@@ -58,7 +58,7 @@ func init() {
 
 // Open opens a new MySQL driver.
 func Open(db schema.ExecQuerier) (migrate.Driver, error) {
-	c := conn{ExecQuerier: db}
+	c := &conn{ExecQuerier: db}
 	rows, err := db.QueryContext(context.Background(), variablesQuery)
 	if err != nil {
 		return nil, fmt.Errorf("mysql: query system variables: %w", err)
diff --git a/sql/mysql/inspect.go b/sql/mysql/inspect.go
index 95aaf13c..b6cc9e92 100644
--- a/sql/mysql/inspect.go
+++ b/sql/mysql/inspect.go
@@ -17,7 +17,7 @@ import (
 )
 
 // A diff provides a MySQL implementation for schema.Inspector.
-type inspect struct{ conn }
+type inspect struct{ *conn }
 
 var _ schema.Inspector = (*inspect)(nil)
 
diff --git a/sql/mysql/migrate.go b/sql/mysql/migrate.go
index 7d791f87..9a7fb46d 100644
--- a/sql/mysql/migrate.go
+++ b/sql/mysql/migrate.go
@@ -17,7 +17,7 @@ import (
 )
 
 var (
-	noConn = conn{ExecQuerier: sqlx.NoRows, V: "8.0.31"}
+	noConn = &conn{ExecQuerier: sqlx.NoRows, V: "8.0.31"}
 	// DefaultPlan provides basic planning capabilities for MySQL dialects.
 	// Note, it is recommended to call Open, create a new Driver and use its
 	// migrate.PlanApplier when a database connection is available.
@@ -25,7 +25,7 @@ var (
 )
 
 // A planApply provides migration capabilities for schema elements.
-type planApply struct{ conn }
+type planApply struct{ *conn }
 
 // PlanChanges returns a migration plan for the given schema changes.
 func (p *planApply) PlanChanges(_ context.Context, name string, changes []schema.Change, opts ...migrate.PlanOption) (*migrate.Plan, error) {
@@ -61,7 +61,7 @@ func (p *planApply) ApplyChanges(ctx context.Context, changes []schema.Change, o
 // planApply so that multiple planning/applying can be called
 // in parallel.
 type state struct {
-	conn
+	*conn
 	migrate.Plan
 	migrate.PlanOptions
 }
diff --git a/sql/postgres/diff.go b/sql/postgres/diff.go
index c9b6a4fc..66da0ed0 100644
--- a/sql/postgres/diff.go
+++ b/sql/postgres/diff.go
@@ -24,7 +24,7 @@ import (
 var DefaultDiff schema.Differ = &sqlx.Diff{DiffDriver: &diff{}}
 
 // A diff provides a PostgreSQL implementation for sqlx.DiffDriver.
-type diff struct{ conn }
+type diff struct{ *conn }
 
 // SchemaAttrDiff returns a changeset for migrating schema attributes from one state to the other.
 func (*diff) SchemaAttrDiff(from, to *schema.Schema) []schema.Change {
diff --git a/sql/postgres/driver.go b/sql/postgres/driver.go
index 03a2c670..35f86984 100644
--- a/sql/postgres/driver.go
+++ b/sql/postgres/driver.go
@@ -24,7 +24,7 @@ type (
 	// Driver represents a PostgreSQL driver for introspecting database schemas,
 	// generating diff between schema elements and apply migrations changes.
 	Driver struct {
-		conn
+		*conn
 		schema.Differ
 		schema.Inspector
 		migrate.PlanApplier
@@ -86,7 +86,7 @@ func opener(_ context.Context, u *url.URL) (*sqlclient.Client, error) {
 
 // Open opens a new PostgreSQL driver.
 func Open(db schema.ExecQuerier) (migrate.Driver, error) {
-	c := conn{ExecQuerier: db}
+	c := &conn{ExecQuerier: db}
 	rows, err := db.QueryContext(context.Background(), paramsQuery)
 	if err != nil {
 		return nil, fmt.Errorf("postgres: scanning system variables: %w", err)
diff --git a/sql/postgres/inspect.go b/sql/postgres/inspect.go
index 35e4771e..ee7d9022 100644
--- a/sql/postgres/inspect.go
+++ b/sql/postgres/inspect.go
@@ -21,7 +21,7 @@ import (
 )
 
 // A diff provides a PostgreSQL implementation for schema.Inspector.
-type inspect struct{ conn }
+type inspect struct{ *conn }
 
 var _ schema.Inspector = (*inspect)(nil)
 
diff --git a/sql/postgres/migrate.go b/sql/postgres/migrate.go
index 1cac346a..29b16f64 100644
--- a/sql/postgres/migrate.go
+++ b/sql/postgres/migrate.go
@@ -20,10 +20,10 @@ import (
 // DefaultPlan provides basic planning capabilities for PostgreSQL dialects.
 // Note, it is recommended to call Open, create a new Driver and use its
 // migrate.PlanApplier when a database connection is available.
-var DefaultPlan migrate.PlanApplier = &planApply{conn: conn{ExecQuerier: sqlx.NoRows}}
+var DefaultPlan migrate.PlanApplier = &planApply{conn: &conn{ExecQuerier: sqlx.NoRows}}
 
 // A planApply provides migration capabilities for schema elements.
-type planApply struct{ conn }
+type planApply struct{ *conn }
 
 // PlanChanges returns a migration plan for the given schema changes.
 func (p *planApply) PlanChanges(_ context.Context, name string, changes []schema.Change, opts ...migrate.PlanOption) (*migrate.Plan, error) {
@@ -57,7 +57,7 @@ func (p *planApply) ApplyChanges(ctx context.Context, changes []schema.Change, o
 // planApply so that multiple planning/applying can be called
 // in parallel.
 type state struct {
-	conn
+	*conn
 	migrate.Plan
 	migrate.PlanOptions
 	droppedT []*schema.Table
diff --git a/sql/sqlite/driver.go b/sql/sqlite/driver.go
index 60cd5494..a4837377 100644
--- a/sql/sqlite/driver.go
+++ b/sql/sqlite/driver.go
@@ -26,7 +26,7 @@ type (
 	// Driver represents a SQLite driver for introspecting database schemas,
 	// generating diff between schema elements and apply migrations changes.
 	Driver struct {
-		conn
+		*conn
 		schema.Differ
 		schema.Inspector
 		migrate.PlanApplier
@@ -75,7 +75,7 @@ func init() {
 // Open opens a new SQLite driver.
 func Open(db schema.ExecQuerier) (migrate.Driver, error) {
 	var (
-		c   = conn{ExecQuerier: db}
+		c   = &conn{ExecQuerier: db}
 		ctx = context.Background()
 	)
 	rows, err := db.QueryContext(ctx, "SELECT sqlite_version()")
diff --git a/sql/sqlite/inspect.go b/sql/sqlite/inspect.go
index 650ebacb..6e800895 100644
--- a/sql/sqlite/inspect.go
+++ b/sql/sqlite/inspect.go
@@ -18,7 +18,7 @@ import (
 )
 
 // A diff provides an SQLite implementation for schema.Inspector.
-type inspect struct{ conn }
+type inspect struct{ *conn }
 
 var _ schema.Inspector = (*inspect)(nil)
 
diff --git a/sql/sqlite/migrate.go b/sql/sqlite/migrate.go
index b6ac8b9a..a670384c 100644
--- a/sql/sqlite/migrate.go
+++ b/sql/sqlite/migrate.go
@@ -17,10 +17,10 @@ import (
 // DefaultPlan provides basic planning capabilities for SQLite dialects.
 // Note, it is recommended to call Open, create a new Driver and use its
 // migrate.PlanApplier when a database connection is available.
-var DefaultPlan migrate.PlanApplier = &planApply{conn: conn{ExecQuerier: sqlx.NoRows}}
+var DefaultPlan migrate.PlanApplier = &planApply{conn: &conn{ExecQuerier: sqlx.NoRows}}
 
 // A planApply provides migration capabilities for schema elements.
-type planApply struct{ conn }
+type planApply struct{ *conn }
 
 // PlanChanges returns a migration plan for the given schema changes.
 func (p *planApply) PlanChanges(ctx context.Context, name string, changes []schema.Change, opts ...migrate.PlanOption) (*migrate.Plan, error) {
@@ -67,7 +67,7 @@ func (p *planApply) ApplyChanges(ctx context.Context, changes []schema.Change, o
 // planApply so that multiple planning/applying can be called
 // in parallel.
 type state struct {
-	conn
+	*conn
 	migrate.Plan
 	migrate.PlanOptions
 	skipFKs bool


Test patch:
diff --git a/sql/mysql/driver_test.go b/sql/mysql/driver_test.go
index bf01aa00..4e60f6f0 100644
--- a/sql/mysql/driver_test.go
+++ b/sql/mysql/driver_test.go
@@ -60,16 +60,16 @@ func TestDriver_LockAcquired(t *testing.T) {
 	}
 
 	t.Run("OnPool", func(t *testing.T) {
-		d := &Driver{}
+		d := &Driver{conn: &conn{}}
 		d.ExecQuerier = &mockOpener{DB: db}
 		lock(d)
 		require.EqualValues(t, 1, d.ExecQuerier.(*mockOpener).opened)
 	})
 
 	t.Run("OnConn", func(t *testing.T) {
+		d := &Driver{conn: &conn{}}
 		conn, err := db.Conn(context.Background())
 		require.NoError(t, err)
-		d := &Driver{}
 		d.ExecQuerier = conn
 		lock(d)
 	})
@@ -78,7 +78,7 @@ func TestDriver_LockAcquired(t *testing.T) {
 		m.ExpectBegin()
 		tx, err := db.Begin()
 		require.NoError(t, err)
-		d := &Driver{}
+		d := &Driver{conn: &conn{}}
 		d.ExecQuerier = tx
 		lock(d)
 	})
@@ -88,7 +88,7 @@ func TestDriver_LockAcquired(t *testing.T) {
 func TestDriver_LockError(t *testing.T) {
 	db, m, err := sqlmock.New()
 	require.NoError(t, err)
-	d := &Driver{}
+	d := &Driver{conn: &conn{}}
 	d.ExecQuerier = db
 
 	t.Run("Timeout", func(t *testing.T) {
@@ -117,8 +117,7 @@ func TestDriver_LockError(t *testing.T) {
 func TestDriver_UnlockError(t *testing.T) {
 	db, m, err := sqlmock.New()
 	require.NoError(t, err)
-	d := &Driver{}
-	d.ExecQuerier = db
+	d := &Driver{conn: &conn{ExecQuerier: db}}
 	acquired := func(name string, sec int) {
 		m.ExpectQuery(sqltest.Escape("SELECT GET_LOCK(?, ?)")).
 			WithArgs(name, sec).
diff --git a/sql/mysql/mysqlcheck/mysqlcheck_test.go b/sql/mysql/mysqlcheck/mysqlcheck_test.go
index d9e55e9c..e1628e20 100644
--- a/sql/mysql/mysqlcheck/mysqlcheck_test.go
+++ b/sql/mysql/mysqlcheck/mysqlcheck_test.go
@@ -5,7 +5,9 @@
 package mysqlcheck_test
 
 import (
+	"ariga.io/atlas/sql/internal/sqltest"
 	"context"
+	"github.com/DATA-DOG/go-sqlmock"
 	"testing"
 
 	"ariga.io/atlas/sql/migrate"
@@ -24,7 +26,7 @@ func TestDataDepend_MySQL_ImplicitUpdate(t *testing.T) {
 		pass   = &sqlcheck.Pass{
 			Dev: &sqlclient.Client{
 				Name:   "mysql",
-				Driver: &mysql.Driver{},
+				Driver: devDriver(t, "5.7.0"),
 			},
 			File: &sqlcheck.File{
 				File: testFile{name: "1.sql"},
@@ -73,14 +75,12 @@ func TestDataDepend_MySQL_ImplicitUpdate(t *testing.T) {
 }
 
 func TestDataDepend_MySQL8_ImplicitUpdate(t *testing.T) {
-	drv := &mysql.Driver{}
-	drv.V = "8.0.19"
 	var (
 		report *sqlcheck.Report
 		pass   = &sqlcheck.Pass{
 			Dev: &sqlclient.Client{
 				Name:   "mysql",
-				Driver: drv,
+				Driver: devDriver(t, "8.0.19"),
 			},
 			File: &sqlcheck.File{
 				File: testFile{name: "1.sql"},
@@ -126,7 +126,7 @@ func TestDataDepend_MySQL_MightFail(t *testing.T) {
 		pass   = &sqlcheck.Pass{
 			Dev: &sqlclient.Client{
 				Name:   "mysql",
-				Driver: &mysql.Driver{},
+				Driver: devDriver(t, "8.0.19"),
 			},
 			File: &sqlcheck.File{
 				File: testFile{name: "1.sql"},
@@ -169,14 +169,12 @@ func TestDataDepend_MySQL_MightFail(t *testing.T) {
 }
 
 func TestDataDepend_Maria_ImplicitUpdate(t *testing.T) {
-	drv := &mysql.Driver{}
-	drv.V = "10.7.1-MariaDB"
 	var (
 		report *sqlcheck.Report
 		pass   = &sqlcheck.Pass{
 			Dev: &sqlclient.Client{
 				Name:   "mysql",
-				Driver: drv,
+				Driver: devDriver(t, "10.7.1-MariaDB"),
 			},
 			File: &sqlcheck.File{
 				File: testFile{name: "1.sql"},
@@ -239,3 +237,19 @@ type testFile struct {
 func (t testFile) Name() string {
 	return t.name
 }
+
+func devDriver(t *testing.T, version string) migrate.Driver {
+	db, mk, err := sqlmock.New()
+	require.NoError(t, err)
+	mk.ExpectQuery("SELECT @@version, @@collation_server, @@character_set_server, @@lower_case_table_name").
+		WillReturnRows(sqltest.Rows(`
++-----------------+--------------------+------------------------+--------------------------+ 
+| @@version       | @@collation_server | @@character_set_server | @@lower_case_table_names | 
++-----------------+--------------------+------------------------+--------------------------+ 
+|` + version + `  | utf8_general_ci    | utf8                   | 0                        | 
++-----------------+--------------------+------------------------+--------------------------+ 
+`))
+	drv, err := mysql.Open(db)
+	require.NoError(t, err)
+	return drv
+}
diff --git a/sql/postgres/driver_test.go b/sql/postgres/driver_test.go
index 9c13f22f..9314a3c8 100644
--- a/sql/postgres/driver_test.go
+++ b/sql/postgres/driver_test.go
@@ -31,8 +31,7 @@ func TestDriver_LockAcquired(t *testing.T) {
 		WillReturnRows(sqlmock.NewRows([]string{"pg_advisory_unlock"}).AddRow(1)).
 		RowsWillBeClosed()
 
-	d := &Driver{}
-	d.ExecQuerier = db
+	d := &Driver{conn: &conn{ExecQuerier: db}}
 	unlock, err := d.Lock(context.Background(), name, 0)
 	require.NoError(t, err)
 	require.NoError(t, unlock())
@@ -42,8 +41,7 @@ func TestDriver_LockAcquired(t *testing.T) {
 func TestDriver_LockError(t *testing.T) {
 	db, m, err := sqlmock.New()
 	require.NoError(t, err)
-	d := &Driver{}
-	d.ExecQuerier = db
+	d := &Driver{conn: &conn{ExecQuerier: db}}
 	name, hash := "migrate", 979249972
 
 	t.Run("Timeout", func(t *testing.T) {
@@ -70,8 +68,7 @@ func TestDriver_LockError(t *testing.T) {
 func TestDriver_UnlockError(t *testing.T) {
 	db, m, err := sqlmock.New()
 	require.NoError(t, err)
-	d := &Driver{}
-	d.ExecQuerier = db
+	d := &Driver{conn: &conn{ExecQuerier: db}}
 	name, hash := "up", 1551306158
 	acquired := func() {
 		m.ExpectQuery(sqltest.Escape("SELECT pg_try_advisory_lock($1)")).
@@ -105,7 +102,7 @@ func TestDriver_UnlockError(t *testing.T) {
 
 func TestDriver_CheckClean(t *testing.T) {
 	s := schema.New("test")
-	drv := &Driver{Inspector: &mockInspector{schema: s}, conn: conn{schema: "test"}}
+	drv := &Driver{Inspector: &mockInspector{schema: s}, conn: &conn{schema: "test"}}
 	// Empty schema.
 	err := drv.CheckClean(context.Background(), nil)
 	require.NoError(t, err)