Repository: ariga/atlas
Pull request #3401 (2025-02-24 15:37:55)

sql/sqlcheck: drop naming linter


Patch:
diff --git a/sql/mysql/mysqlcheck/mysqlcheck.go b/sql/mysql/mysqlcheck/mysqlcheck.go
index 4b9b97bb..d62423fa 100644
--- a/sql/mysql/mysqlcheck/mysqlcheck.go
+++ b/sql/mysql/mysqlcheck/mysqlcheck.go
@@ -20,7 +20,6 @@ import (
 	"ariga.io/atlas/sql/sqlcheck/datadepend"
 	"ariga.io/atlas/sql/sqlcheck/destructive"
 	"ariga.io/atlas/sql/sqlcheck/incompatible"
-	"ariga.io/atlas/sql/sqlcheck/naming"
 )
 
 var (
@@ -223,9 +222,5 @@ func analyzers(r *schemahcl.Resource) ([]sqlcheck.Analyzer, error) {
 	if err != nil {
 		return nil, err
 	}
-	nm, err := naming.New(r)
-	if err != nil {
-		return nil, err
-	}
-	return []sqlcheck.Analyzer{ds, dd, cd, bc, nm, sqlcheck.AnalyzerFunc(inlineRefs)}, nil
+	return []sqlcheck.Analyzer{ds, dd, cd, bc, sqlcheck.AnalyzerFunc(inlineRefs)}, nil
 }
diff --git a/sql/postgres/postgrescheck/postgrescheck.go b/sql/postgres/postgrescheck/postgrescheck.go
index 358b1597..e73f6ffb 100644
--- a/sql/postgres/postgrescheck/postgrescheck.go
+++ b/sql/postgres/postgrescheck/postgrescheck.go
@@ -14,7 +14,6 @@ import (
 	"ariga.io/atlas/sql/sqlcheck/datadepend"
 	"ariga.io/atlas/sql/sqlcheck/destructive"
 	"ariga.io/atlas/sql/sqlcheck/incompatible"
-	"ariga.io/atlas/sql/sqlcheck/naming"
 )
 
 func addNotNull(p *datadepend.ColumnPass) (diags []sqlcheck.Diagnostic, err error) {
@@ -52,9 +51,5 @@ func analyzers(r *schemahcl.Resource) ([]sqlcheck.Analyzer, error) {
 	if err != nil {
 		return nil, err
 	}
-	nm, err := naming.New(r)
-	if err != nil {
-		return nil, err
-	}
-	return []sqlcheck.Analyzer{ds, dd, cd, bc, nm}, nil
+	return []sqlcheck.Analyzer{ds, dd, cd, bc}, nil
 }
diff --git a/sql/sqlcheck/naming/naming.go b/sql/sqlcheck/naming/naming.go
deleted file mode 100644
index f3bf9004..00000000
--- a/sql/sqlcheck/naming/naming.go
+++ /dev/null
@@ -1,166 +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 naming
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"regexp"
-
-	"ariga.io/atlas/schemahcl"
-	"ariga.io/atlas/sql/internal/sqlx"
-	"ariga.io/atlas/sql/schema"
-	"ariga.io/atlas/sql/sqlcheck"
-)
-
-type (
-	// Analyzer checks for resource naming.
-	Analyzer struct {
-		sqlcheck.Options
-		// Global or resource-specific options.
-		All, Schema, Table, Column,
-		Index, ForeignKey, Check Options
-	}
-	// Options for resource naming.
-	Options struct {
-		re      *regexp.Regexp
-		Match   string `spec:"match"`
-		Message string `spec:"message"`
-	}
-)
-
-// New creates a new destructive changes Analyzer with the given options.
-func New(r *schemahcl.Resource) (*Analyzer, error) {
-	az := &Analyzer{}
-	if r, ok := r.Resource(az.Name()); ok {
-		if err := r.As(&az.Options); err != nil {
-			return nil, fmt.Errorf("sql/sqlcheck: parsing naming default options: %w", err)
-		}
-		if err := readOptions(r, &az.All); err != nil {
-			return nil, err
-		}
-		for _, f := range []struct {
-			string
-			*Options
-		}{
-			{"schema", &az.Schema},
-			{"table", &az.Table},
-			{"column", &az.Column},
-			{"index", &az.Index},
-			{"foreign_key", &az.ForeignKey},
-			{"check", &az.Check},
-		} {
-			if fr, ok := r.Resource(f.string); ok {
-				if err := readOptions(fr, f.Options); err != nil {
-					return nil, err
-				}
-			}
-		}
-	}
-	return az, nil
-}
-
-func readOptions(r *schemahcl.Resource, opts *Options) error {
-	if err := r.As(opts); err != nil {
-		return fmt.Errorf("sql/sqlcheck: parsing naming options: %w", err)
-	}
-	re, err := regexp.Compile(opts.Match)
-	if err != nil {
-		return fmt.Errorf("sql/sqlcheck: parsing naming regexp: %w", err)
-	}
-	opts.re = re
-	return nil
-}
-
-// List of codes.
-var (
-	codeNameS = sqlcheck.Code("NM101")
-	codeNameT = sqlcheck.Code("NM102")
-	codeNameC = sqlcheck.Code("NM103")
-	codeNameI = sqlcheck.Code("NM104")
-	codeNameF = sqlcheck.Code("NM105")
-	codeNameK = sqlcheck.Code("NM106")
-)
-
-// Name of the analyzer. Implements the sqlcheck.NamedAnalyzer interface.
-func (*Analyzer) Name() string {
-	return "naming"
-}
-
-// Analyze implements sqlcheck.Analyzer.
-func (a *Analyzer) Analyze(_ context.Context, p *sqlcheck.Pass) error {
-	var diags []sqlcheck.Diagnostic
-	for _, sc := range p.File.Changes {
-		for _, c := range sc.Changes {
-			switch c := c.(type) {
-			case *schema.AddSchema:
-				diags = append(diags, a.match(sc, codeNameS, c.S.Name, "Schema", a.Schema)...)
-			case *schema.AddTable:
-				diags = append(diags, a.match(sc, codeNameT, c.T.Name, "Table", a.Table)...)
-				for _, c := range c.T.Columns {
-					diags = append(diags, a.match(sc, codeNameC, c.Name, "Column", a.Column)...)
-				}
-				for _, i := range c.T.Indexes {
-					diags = append(diags, a.match(sc, codeNameI, i.Name, "Index", a.Index)...)
-				}
-				for _, f := range c.T.ForeignKeys {
-					diags = append(diags, a.match(sc, codeNameF, f.Symbol, "Foreign-key constraint", a.ForeignKey)...)
-				}
-				for _, at := range c.T.Attrs {
-					if k, ok := at.(*schema.Check); ok {
-						diags = append(diags, a.match(sc, codeNameK, k.Name, "Check constraint", a.Check)...)
-					}
-				}
-			case *schema.RenameTable:
-				diags = append(diags, a.match(sc, codeNameT, c.To.Name, "Table", a.Table)...)
-			case *schema.ModifyTable:
-				for i := range c.Changes {
-					switch mc := c.Changes[i].(type) {
-					case *schema.AddColumn:
-						diags = append(diags, a.match(sc, codeNameC, mc.C.Name, "Column", a.Column)...)
-					case *schema.RenameColumn:
-						diags = append(diags, a.match(sc, codeNameC, mc.To.Name, "Column", a.Column)...)
-					case *schema.AddIndex:
-						diags = append(diags, a.match(sc, codeNameI, mc.I.Name, "Index", a.Index)...)
-					case *schema.RenameIndex:
-						diags = append(diags, a.match(sc, codeNameI, mc.To.Name, "Index", a.Index)...)
-					case *schema.AddForeignKey:
-						diags = append(diags, a.match(sc, codeNameF, mc.F.Symbol, "Foreign-key constraint", a.ForeignKey)...)
-					case *schema.AddCheck:
-						diags = append(diags, a.match(sc, codeNameK, mc.C.Name, "Check constraint", a.Check)...)
-					}
-				}
-			}
-		}
-	}
-	if len(diags) > 0 {
-		const reportText = "naming violations detected"
-		p.Reporter.WriteReport(sqlcheck.Report{Text: reportText, Diagnostics: diags})
-		if sqlx.V(a.Error) {
-			return errors.New(reportText)
-		}
-	}
-	return nil
-}
-
-func (a *Analyzer) match(c *sqlcheck.Change, code, name, resource string, opts Options) []sqlcheck.Diagnostic {
-	re, msg := opts.re, opts.Message
-	if re == nil {
-		re, msg = a.All.re, a.All.Message
-	}
-	if re == nil || re.MatchString(name) {
-		return nil
-	}
-	d := sqlcheck.Diagnostic{
-		Pos:  c.Stmt.Pos,
-		Text: fmt.Sprintf("%s named %q violates the naming policy", resource, name),
-		Code: code,
-	}
-	if msg != "" {
-		d.Text += ": " + msg
-	}
-	return []sqlcheck.Diagnostic{d}
-}
diff --git a/sql/sqlite/sqlitecheck/sqlitecheck.go b/sql/sqlite/sqlitecheck/sqlitecheck.go
index 1055ebbe..d85e823e 100644
--- a/sql/sqlite/sqlitecheck/sqlitecheck.go
+++ b/sql/sqlite/sqlitecheck/sqlitecheck.go
@@ -17,7 +17,6 @@ import (
 	"ariga.io/atlas/sql/sqlcheck/datadepend"
 	"ariga.io/atlas/sql/sqlcheck/destructive"
 	"ariga.io/atlas/sql/sqlcheck/incompatible"
-	"ariga.io/atlas/sql/sqlcheck/naming"
 	"ariga.io/atlas/sql/sqlite"
 )
 
@@ -53,76 +52,70 @@ func modifyNotNull(p *datadepend.ColumnPass) (diags []sqlcheck.Diagnostic, err e
 	}, nil
 }
 
-func init() {
-	sqlcheck.Register(sqlite.DriverName, func(r *schemahcl.Resource) ([]sqlcheck.Analyzer, error) {
-		ds, err := destructive.New(r)
-		if err != nil {
-			return nil, err
-		}
-		cd, err := condrop.New(r)
-		if err != nil {
-			return nil, err
-		}
-		dd, err := datadepend.New(r, datadepend.Handler{
-			AddNotNull:    addNotNull,
-			ModifyNotNull: modifyNotNull,
-		})
-		if err != nil {
-			return nil, err
-		}
-		bc, err := incompatible.New(r)
-		if err != nil {
-			return nil, err
-		}
-		nm, err := naming.New(r)
-		if err != nil {
-			return nil, err
-		}
-		return []sqlcheck.Analyzer{
-			sqlcheck.AnalyzerFunc(func(_ context.Context, p *sqlcheck.Pass) error {
-				var changes []*sqlcheck.Change
-				// Detect sequence of changes using temporary table and transform them to one ModifyTable change.
-				// See: https://www.sqlite.org/lang_altertable.html#making_other_kinds_of_table_schema_changes.
-				for i := 0; i < len(p.File.Changes); i++ {
-					if i+3 >= len(p.File.Changes) {
-						changes = append(changes, p.File.Changes[i])
-						continue
-					}
-					prevT, currT, ok := modifyUsingTemp(p.File.Changes[i], p.File.Changes[i+2], p.File.Changes[i+3])
-					if !ok {
-						changes = append(changes, p.File.Changes[i])
-						continue
-					}
-					diff, err := p.Dev.Driver.TableDiff(prevT, currT)
-					if err != nil {
-						return nil
-					}
-					changes = append(changes, &sqlcheck.Change{
-						Stmt: &migrate.Stmt{
-							// Use the position of the first statement.
-							Pos: p.File.Changes[i].Stmt.Pos,
-							// A combined statement.
-							Text: strings.Join([]string{
-								p.File.Changes[i].Stmt.Text,
-								p.File.Changes[i+2].Stmt.Text,
-								p.File.Changes[i+3].Stmt.Text,
-							}, "\n"),
-						},
-						Changes: schema.Changes{
-							&schema.ModifyTable{
-								T:       currT,
-								Changes: diff,
-							},
-						},
-					})
-					i += 3
-				}
-				p.File.Changes = changes
-				return nil
-			}),
-			ds, dd, cd, bc, nm,
-		}, nil
+func analyzers(r *schemahcl.Resource) ([]sqlcheck.Analyzer, error) {
+	ds, err := destructive.New(r)
+	if err != nil {
+		return nil, err
+	}
+	cd, err := condrop.New(r)
+	if err != nil {
+		return nil, err
+	}
+	dd, err := datadepend.New(r, datadepend.Handler{
+		AddNotNull:    addNotNull,
+		ModifyNotNull: modifyNotNull,
 	})
+	if err != nil {
+		return nil, err
+	}
+	bc, err := incompatible.New(r)
+	if err != nil {
+		return nil, err
+	}
+	return []sqlcheck.Analyzer{
+		sqlcheck.AnalyzerFunc(func(_ context.Context, p *sqlcheck.Pass) error {
+			var changes []*sqlcheck.Change
+			// Detect sequence of changes using temporary table and transform them to one ModifyTable change.
+			// See: https://www.sqlite.org/lang_altertable.html#making_other_kinds_of_table_schema_changes.
+			for i := 0; i < len(p.File.Changes); i++ {
+				if i+3 >= len(p.File.Changes) {
+					changes = append(changes, p.File.Changes[i])
+					continue
+				}
+				prevT, currT, ok := modifyUsingTemp(p.File.Changes[i], p.File.Changes[i+2], p.File.Changes[i+3])
+				if !ok {
+					changes = append(changes, p.File.Changes[i])
+					continue
+				}
+				diff, err := p.Dev.Driver.TableDiff(prevT, currT)
+				if err != nil {
+					return nil
+				}
+				changes = append(changes, &sqlcheck.Change{
+					Stmt: &migrate.Stmt{
+						// Use the position of the first statement.
+						Pos: p.File.Changes[i].Stmt.Pos,
+						// A combined statement.
+						Text: strings.Join([]string{
+							p.File.Changes[i].Stmt.Text,
+							p.File.Changes[i+2].Stmt.Text,
+							p.File.Changes[i+3].Stmt.Text,
+						}, "\n"),
+					},
+					Changes: schema.Changes{
+						&schema.ModifyTable{
+							T:       currT,
+							Changes: diff,
+						},
+					},
+				})
+				i += 3
+			}
+			p.File.Changes = changes
+			return nil
+		}),
+		ds, dd, cd, bc,
+	}, nil
 }
 
 // modifyUsingTemp indicates if the 3 changes represents a table modification using
diff --git a/sql/sqlite/sqlitecheck/sqlitecheck_oss.go b/sql/sqlite/sqlitecheck/sqlitecheck_oss.go
new file mode 100644
index 00000000..b7ecec62
--- /dev/null
+++ b/sql/sqlite/sqlitecheck/sqlitecheck_oss.go
@@ -0,0 +1,16 @@
+// 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.
+
+//go:build !ent
+
+package sqlitecheck
+
+import (
+	"ariga.io/atlas/sql/sqlcheck"
+	"ariga.io/atlas/sql/sqlite"
+)
+
+func init() {
+	sqlcheck.Register(sqlite.DriverName, analyzers)
+}


Test patch:
diff --git a/internal/integration/testdata/sqlite/cli-migrate-lint-naming.txtar b/internal/integration/testdata/sqlite/cli-migrate-lint-naming.txtar
deleted file mode 100644
index 1e6ea26c..00000000
--- a/internal/integration/testdata/sqlite/cli-migrate-lint-naming.txtar
+++ /dev/null
@@ -1,34 +0,0 @@
-atlas migrate lint --env local --latest=1
-stdout 'Analyzing changes until version 1 \(1 migration in total\):'
-stdout ''
-stdout '  -- analyzing version 1'
-stdout '    -- naming violations detected:'
-stdout '      -- L1: Table named "Users" violates the naming policy: must be lowercase'
-stdout '         https://atlasgo.io/lint/analyzers#NM102'
-stdout '      -- L2: Index named "users_name" violates the naming policy: must be lowercase and end with'
-stdout '         _idx https://atlasgo.io/lint/analyzers#NM104'
-stdout '  -- ok \(.+\)'
-stdout ''
-stdout '  -------------------------'
-stdout '  -- .+'
-stdout '  -- 1 version with warnings'
-stdout '  -- 2 schema changes'
-stdout '  -- 2 diagnostics'
-
--- atlas.hcl --
-env "local" {
-  dev = "URL"
-}
-lint {
-  naming {
-    match   = "^[a-z]+$"
-    message = "must be lowercase"
-    index {
-      match   = "^[a-z]+_idx$"
-      message = "must be lowercase and end with _idx"
-    }
-  }
-}
--- migrations/1.sql --
-CREATE TABLE Users (id int, name varchar(255));
-CREATE INDEX users_name ON Users (name);
\ No newline at end of file
diff --git a/sql/sqlcheck/naming/naming_test.go b/sql/sqlcheck/naming/naming_test.go
deleted file mode 100644
index 43cd790a..00000000
--- a/sql/sqlcheck/naming/naming_test.go
+++ /dev/null
@@ -1,78 +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 naming_test
-
-import (
-	"context"
-	"testing"
-
-	"ariga.io/atlas/schemahcl"
-	"ariga.io/atlas/sql/migrate"
-	"ariga.io/atlas/sql/schema"
-	"ariga.io/atlas/sql/sqlcheck"
-	"ariga.io/atlas/sql/sqlcheck/naming"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestOptions(t *testing.T) {
-	var cfg struct {
-		schemahcl.DefaultExtension
-	}
-	// language=hcl
-	err := schemahcl.New().EvalBytes([]byte(`
-naming {
-  match   = "^[a-z]+$"
-  message = "must be lowercase"
-  index {
-    match   = "^[a-z]+_idx$"
-    message = "must be lowercase and end with _idx"
-  } 
-}
-`), &cfg, nil)
-	require.NoError(t, err)
-	az, err := naming.New(cfg.Remain())
-	require.NoError(t, err)
-	var report *sqlcheck.Report
-	err = az.Analyze(context.Background(), &sqlcheck.Pass{
-		File: &sqlcheck.File{
-			Changes: []*sqlcheck.Change{
-				{
-					Changes: schema.Changes{
-						&schema.AddTable{T: schema.NewTable("Users")},
-					},
-					Stmt: &migrate.Stmt{
-						Pos:  0,
-						Text: "CREATE TABLE `Users`",
-					},
-				},
-				{
-					Changes: schema.Changes{
-						&schema.ModifyTable{
-							T: schema.NewTable("pets"),
-							Changes: schema.Changes{
-								&schema.AddIndex{
-									I: schema.NewIndex("pet_name"),
-								},
-							},
-						},
-					},
-					Stmt: &migrate.Stmt{
-						Pos:  20,
-						Text: "ALTER TABLE `pets` ADD INDEX `pet_name`",
-					},
-				},
-			},
-		},
-		Reporter: sqlcheck.ReportWriterFunc(func(r sqlcheck.Report) {
-			report = &r
-		}),
-	})
-	require.NoError(t, err)
-	require.NotNil(t, report)
-	require.Len(t, report.Diagnostics, 2)
-	require.Equal(t, `Table named "Users" violates the naming policy: must be lowercase`, report.Diagnostics[0].Text)
-	require.Equal(t, `Index named "pet_name" violates the naming policy: must be lowercase and end with _idx`, report.Diagnostics[1].Text)
-}
diff --git a/sql/sqlite/sqlitecheck/sqlitecheck_test.go b/sql/sqlite/sqlitecheck/sqlitecheck_test.go
index c966d4be..5899577c 100644
--- a/sql/sqlite/sqlitecheck/sqlitecheck_test.go
+++ b/sql/sqlite/sqlitecheck/sqlitecheck_test.go
@@ -125,7 +125,7 @@ func TestDetectModifyTable(t *testing.T) {
 	)
 	azs, err := sqlcheck.AnalyzerFor(sqlite.DriverName, nil)
 	require.NoError(t, err)
-	require.Len(t, azs, 6)
+	require.Len(t, azs, 5)
 	require.NoError(t, azs[0].Analyze(context.Background(), pass))
 	err = azs[1].Analyze(context.Background(), pass)
 	require.EqualError(t, err, "destructive changes detected")