nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: ariga/atlas
Pull request #3155 (2024-09-25 14:32:22)

sql/postgres: ud types are case sensitive


Patch:
diff --git a/sql/postgres/convert.go b/sql/postgres/convert.go
index abac3708..5eb89e7c 100644
--- a/sql/postgres/convert.go
+++ b/sql/postgres/convert.go
@@ -172,7 +172,7 @@ func FormatType(t schema.Type) (string, error) {
 			return "", fmt.Errorf("postgres: unsupported text search type: %q", t.T)
 		}
 	case *UserDefinedType:
-		f = strings.ToLower(t.T)
+		f = t.T
 	case *XMLType:
 		f = strings.ToLower(t.T)
 	case *PseudoType:


Test patch:
diff --git a/sql/postgres/sqlspec_oss_test.go b/sql/postgres/sqlspec_oss_test.go
index 4cdcfe7f..d54dddcb 100644
--- a/sql/postgres/sqlspec_oss_test.go
+++ b/sql/postgres/sqlspec_oss_test.go
@@ -1976,6 +1976,10 @@ func TestTypes(t *testing.T) {
 			typeExpr: `sql("varchar(2) [ 2 ] [  ]")`,
 			expected: &ArrayType{Type: &schema.StringType{T: "varchar", Size: 2}, T: "varchar(2)[]"},
 		},
+		{
+			typeExpr: `sql("CaseSensitive")`,
+			expected: &UserDefinedType{T: "CaseSensitive"},
+		},
 		{
 			typeExpr: "oid",
 			expected: &OIDType{T: typeOID},
Repository: ariga/atlas
Pull request #3177 (2024-10-05 09:15:23)

schemahcl: handle null values as omission


Patch:
diff --git a/schemahcl/schemahcl.go b/schemahcl/schemahcl.go
index c75ef282..c39289ac 100644
--- a/schemahcl/schemahcl.go
+++ b/schemahcl/schemahcl.go
@@ -587,6 +587,10 @@ func (s *State) toAttrs(ctx *hcl.EvalContext, vr SchemaValidator, hclAttrs hclsy
 		if diag.HasErrors() {
 			return nil, s.typeError(diag, scope)
 		}
+		// Setting an attribute as null means omission.
+		if value.IsNull() {
+			continue
+		}
 		if err := vr.ValidateAttribute(ctx, hclAttr, value); err != nil {
 			return nil, err
 		}


Test patch:
diff --git a/schemahcl/schemahcl_test.go b/schemahcl/schemahcl_test.go
index 431581a8..f2b58ae1 100644
--- a/schemahcl/schemahcl_test.go
+++ b/schemahcl/schemahcl_test.go
@@ -57,6 +57,18 @@ vars = {
 	marshal, err := Marshal(&test)
 	require.NoError(t, err)
 	require.EqualValues(t, f, string(marshal))
+
+	var v struct {
+		NullV string  `spec:"null_v"`
+		NullP *string `spec:"null_p"`
+	}
+	err = New().EvalBytes([]byte(`
+null_v = null
+null_p = null
+`), &v, nil)
+	require.NoError(t, err)
+	require.Empty(t, v.NullV)
+	require.Nil(t, v.NullP)
 }
 
 func TestResource(t *testing.T) {
Repository: ariga/atlas
Pull request #3195 (2024-10-19 19:56:23)

sql/mysql: unix connection to a specific database/schema
 Closed https://github.com/ariga/atlas/pull/2847


Patch:
diff --git a/sql/mysql/driver.go b/sql/mysql/driver.go
index e9de037d..c05f715d 100644
--- a/sql/mysql/driver.go
+++ b/sql/mysql/driver.go
@@ -326,7 +326,11 @@ func (parser) ParseURL(u *url.URL) *sqlclient.URL {
 	v := u.Query()
 	v.Set("parseTime", "true")
 	u.RawQuery = v.Encode()
-	return &sqlclient.URL{URL: u, DSN: dsn(u), Schema: strings.TrimPrefix(u.Path, "/")}
+	cu := &sqlclient.URL{URL: u, DSN: dsn(u), Schema: strings.TrimPrefix(u.Path, "/")}
+	if strings.HasSuffix(u.Scheme, "+unix") {
+		cu.Schema = v.Get("database")
+	}
+	return cu
 }
 
 // ChangeSchema implements the sqlclient.SchemaChanger interface.


Test patch:
diff --git a/sql/mysql/driver_test.go b/sql/mysql/driver_test.go
index 4e60f6f0..cc49d1de 100644
--- a/sql/mysql/driver_test.go
+++ b/sql/mysql/driver_test.go
@@ -39,6 +39,20 @@ func TestParser_ParseURL(t *testing.T) {
 			require.Equal(t, d, p.DSN)
 		}
 	})
+	t.Run("Schema", func(t *testing.T) {
+		for u, d := range map[string]string{
+			"mysql://user:pass@localhost:3306/my_db?foo=bar":    "my_db",
+			"mysql://user:pass@localhost:3306?foo=bar":          "",
+			"mysql+unix:///path/to/socket":                      "",
+			"mysql+unix://user:pass@/path/to/socket":            "",
+			"mysql+unix://user@/path/to/socket?database=dbname": "dbname",
+		} {
+			u1, err := url.Parse(u)
+			require.NoError(t, err)
+			p := parser{}.ParseURL(u1)
+			require.Equal(t, d, p.Schema)
+		}
+	})
 }
 
 func TestDriver_LockAcquired(t *testing.T) {
Repository: ariga/atlas
Pull request #3229 (2024-11-23 04:11:13)

fix: an error that occurs when collation contains hyphen or dot
I noticed this error while using the ent.
The collation of the migration file generated using `migrate.NamedDiff` in ent will be output without double quotation marks. The following error occurs when migrating the generated file.

```sh
ERROR:  syntax error at or near "-"
LINE 255: ...ALTER COLUMN "name" TYPE text COLLATE ja-x-icu;
```

The same error occurs if the collation contains a dot in the collation will also cause an error.

e.g. `en_US.UTF-8`

The version in which I found this error is a bit old, but I believe it reproduces in the latest version.

- ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43
- entgo.io/ent v0.13.1

The fix for the cause of this error is L820. The other two were mechanically corrected because they were using COLLATE.


Patch:
diff --git a/sql/postgres/migrate_oss.go b/sql/postgres/migrate_oss.go
index 94de05ec..0d93d11d 100644
--- a/sql/postgres/migrate_oss.go
+++ b/sql/postgres/migrate_oss.go
@@ -817,7 +817,7 @@ func (s *state) alterType(b *sqlx.Builder, alter *changeGroup, t *schema.Table,
 		b.P("TYPE", f)
 	}
 	if collate := (schema.Collation{}); sqlx.Has(c.To.Attrs, &collate) {
-		b.P("COLLATE", collate.V)
+		b.P("COLLATE").Ident(collate.V)
 	}
 	if using := (ConvertUsing{}); sqlx.Has(c.Extra, &using) {
 		b.P("USING", using.X)


Test patch:
diff --git a/sql/postgres/migrate_oss_test.go b/sql/postgres/migrate_oss_test.go
index f2f9c7cf..d3bb1287 100644
--- a/sql/postgres/migrate_oss_test.go
+++ b/sql/postgres/migrate_oss_test.go
@@ -1822,6 +1822,31 @@ func TestPlanChanges(t *testing.T) {
 			},
 			wantErr: true,
 		},
+		{
+			changes: []schema.Change{
+				&schema.ModifyTable{
+					T: schema.NewTable("users").
+						AddColumns(schema.NewColumn("name").SetType(&schema.StringType{T: "text"}).AddAttrs(&schema.Collation{V: "ja-x-icu"})),
+					Changes: []schema.Change{
+						&schema.ModifyColumn{
+							From:   schema.NewColumn("name").SetType(&schema.StringType{T: "text"}),
+							To:     schema.NewColumn("name").SetType(&schema.StringType{T: "text"}).AddAttrs(&schema.Collation{V: "ja-x-icu"}),
+							Change: schema.ChangeType,
+						},
+					},
+				},
+			},
+			wantPlan: &migrate.Plan{
+				Reversible:    true,
+				Transactional: true,
+				Changes: []*migrate.Change{
+					{
+						Cmd:     `ALTER TABLE "users" ALTER COLUMN "name" TYPE text COLLATE "ja-x-icu"`,
+						Reverse: `ALTER TABLE "users" ALTER COLUMN "name" TYPE text`,
+					},
+				},
+			},
+		},
 	}
 	for i, tt := range tests {
 		t.Run(strconv.Itoa(i), func(t *testing.T) {
Repository: ariga/atlas
Pull request #3238 (2024-11-29 18:52:01)

sql/migrate: support custom Delimiter in Plan


Patch:
diff --git a/sql/migrate/dir.go b/sql/migrate/dir.go
index 38b04540..9b3cba4b 100644
--- a/sql/migrate/dir.go
+++ b/sql/migrate/dir.go
@@ -528,11 +528,22 @@ func CheckVersion(v string) error {
 	return nil
 }
 
+// delim returns a directive with the given delimiter.
+func delim(s string) string {
+	if s == "" {
+		return ""
+	}
+	// Escape delimiters. e.g. "\n" => "\\n".
+	s = strings.NewReplacer("\n", `\n`, "\r", `\r`, "\t", `\t`).Replace(s)
+	return fmt.Sprintf("-- atlas:%s %s", directiveDelimiter, s)
+}
+
 var (
 	// templateFunc contains the template.FuncMap for the DefaultFormatter.
 	templateFuncs = template.FuncMap{
 		"upper": strings.ToUpper,
 		"now":   NewVersion,
+		"delim": delim,
 	}
 	// DefaultFormatter is a default implementation for Formatter.
 	DefaultFormatter = TemplateFormatter{
@@ -541,7 +552,7 @@ var (
 				"{{ with .Version }}{{ . }}{{ else }}{{ now }}{{ end }}{{ with .Name }}_{{ . }}{{ end }}.sql",
 			)),
 			C: template.Must(template.New("").Funcs(templateFuncs).Parse(
-				`{{ range .Changes }}{{ with .Comment }}{{ printf "-- %s%s\n" (slice . 0 1 | upper ) (slice . 1) }}{{ end }}{{ printf "%s;\n" .Cmd }}{{ end }}`,
+				`{{ with .Delimiter }}{{ delim . | printf "%s\n\n" }}{{ end }}{{ range .Changes }}{{ with .Comment }}{{ printf "-- %s%s\n" (slice . 0 1 | upper ) (slice . 1) }}{{ end }}{{ printf "%s%s\n" .Cmd (or $.Delimiter ";") }}{{ end }}`,
 			)),
 		},
 	}
diff --git a/sql/migrate/migrate.go b/sql/migrate/migrate.go
index 2d8ffd89..a93d075f 100644
--- a/sql/migrate/migrate.go
+++ b/sql/migrate/migrate.go
@@ -34,6 +34,9 @@ type (
 
 		// Changes defines the list of changeset in the plan.
 		Changes []*Change
+
+		// Delimiter to use for separating statements.
+		Delimiter string
 	}
 
 	// A Change of migration.


Test patch:
diff --git a/sql/migrate/migrate_test.go b/sql/migrate/migrate_test.go
index 00e18dd5..ee2f503b 100644
--- a/sql/migrate/migrate_test.go
+++ b/sql/migrate/migrate_test.go
@@ -74,6 +74,15 @@ func TestPlanner_WritePlan(t *testing.T) {
 	require.Equal(t, countFiles(t, d), 3)
 	requireFileEqual(t, d, "add_t1_and_t2.up.sql", "CREATE TABLE t1(c int)\nCREATE TABLE t2(c int)\n")
 	requireFileEqual(t, d, "add_t1_and_t2.down.sql", "DROP TABLE t1 IF EXISTS\nDROP TABLE t2\n")
+
+	// With custom delimiter.
+	plan.Delimiter = "\nGO"
+	pl = migrate.NewPlanner(nil, d, migrate.PlanWithChecksum(false))
+	require.NotNil(t, pl)
+	require.NoError(t, pl.WritePlan(plan))
+	v = time.Now().UTC().Format("20060102150405")
+	require.Equal(t, countFiles(t, d), 3)
+	requireFileEqual(t, d, v+"_add_t1_and_t2.sql", "-- atlas:delimiter \\nGO\n\nCREATE TABLE t1(c int)\nGO\nCREATE TABLE t2(c int)\nGO\n")
 }
 
 func TestPlanner_WriteCheckpoint(t *testing.T) {
Repository: ariga/atlas
Pull request #3310 (2025-01-14 11:29:28)

sql/migrate: clean up error from revisions table when retry pass
Closed https://github.com/ariga/atlas/issues/3308


Patch:
diff --git a/sql/migrate/migrate.go b/sql/migrate/migrate.go
index 9595aa0e..7385e2fa 100644
--- a/sql/migrate/migrate.go
+++ b/sql/migrate/migrate.go
@@ -875,6 +875,12 @@ func (e *Executor) Execute(ctx context.Context, m File) (err error) {
 		}
 		r.PartialHashes = append(r.PartialHashes, "h1:"+sums[r.Applied])
 		r.Applied++
+		// In case retry attempts succeeded,
+		// clean up the error from the table.
+		if r.Error != "" {
+			r.Error = ""
+			r.ErrorStmt = ""
+		}
 		if err = e.writeRevision(ctx, r); err != nil {
 			e.log.Log(LogError{Error: err})
 			return err


Test patch:
diff --git a/sql/migrate/migrate_test.go b/sql/migrate/migrate_test.go
index d4890152..7a307252 100644
--- a/sql/migrate/migrate_test.go
+++ b/sql/migrate/migrate_test.go
@@ -753,6 +753,20 @@ func TestExecutor(t *testing.T) {
 	e1 = (*log)[4].(migrate.LogError)
 	require.EqualError(t, e1.Error, `sql/migrate: write revision: done error`)
 	require.EqualError(t, errors.Unwrap(e1.Error), `done error`)
+
+	// Successful retry should reset the error.
+	mem := &migrate.MemDir{}
+	require.NoError(t, mem.WriteFile("1.sql", []byte("CREATE TABLE t(c int);")))
+	sum, err := mem.Checksum()
+	require.NoError(t, err)
+	require.NoError(t, migrate.WriteSumFile(mem, sum))
+	*rrw = []*migrate.Revision{{Version: "1", Error: "error", ErrorStmt: ";CREATE TABLE t(c int);", Applied: 0, Total: 1}}
+	ex, err = migrate.NewExecutor(&mockDriver{}, mem, rrw, migrate.WithLogger(log))
+	require.NoError(t, err)
+	err = ex.ExecuteTo(context.Background(), "1")
+	require.NoError(t, err)
+	require.Empty(t, (*rrw)[0].Error)
+	require.Empty(t, (*rrw)[0].ErrorStmt)
 }
 
 func TestExecutor_Baseline(t *testing.T) {
Repository: ariga/atlas
Pull request #3351 (2025-01-29 10:51:01)

sql/sqlspec: allow labeling primary keys
Approved internally. 


Patch:
diff --git a/sql/internal/specutil/convert.go b/sql/internal/specutil/convert.go
index da7f6a83..15c25ae9 100644
--- a/sql/internal/specutil/convert.go
+++ b/sql/internal/specutil/convert.go
@@ -522,6 +522,7 @@ func PrimaryKey(spec *sqlspec.PrimaryKey, parent *schema.Table) (*schema.Index,
 		})
 	}
 	pk := &schema.Index{
+		Name:  spec.Name,
 		Table: parent,
 		Parts: parts,
 	}
diff --git a/sql/sqlspec/sqlspec.go b/sql/sqlspec/sqlspec.go
index 09a90080..47a75cf1 100644
--- a/sql/sqlspec/sqlspec.go
+++ b/sql/sqlspec/sqlspec.go
@@ -63,6 +63,7 @@ type (
 
 	// PrimaryKey holds a specification for the primary key of a table.
 	PrimaryKey struct {
+		Name    string           `spec:",name"` // Optional name.
 		Parts   []*IndexPart     `spec:"on"`
 		Columns []*schemahcl.Ref `spec:"columns"`
 		schemahcl.DefaultExtension


Test patch:
diff --git a/sql/sqlspec/sqlspec_test.go b/sql/sqlspec/sqlspec_test.go
index a8425236..dd4be85f 100644
--- a/sql/sqlspec/sqlspec_test.go
+++ b/sql/sqlspec/sqlspec_test.go
@@ -7,6 +7,8 @@ package sqlspec
 import (
 	"testing"
 
+	"ariga.io/atlas/schemahcl"
+
 	"github.com/stretchr/testify/require"
 )
 
@@ -55,3 +57,44 @@ SELECT
 		require.Equal(t, tt.expected, MightHeredoc(tt.input))
 	}
 }
+
+func TestMarshalPrimaryKey(t *testing.T) {
+	spec := &Table{
+		Name: "users",
+		Columns: []*Column{
+			{Name: "id", Type: &schemahcl.Type{T: "text"}},
+		},
+		PrimaryKey: &PrimaryKey{
+			Columns: []*schemahcl.Ref{
+				{V: "$column.id"},
+			},
+		},
+	}
+	buf, err := schemahcl.Marshal.MarshalSpec(spec)
+	require.NoError(t, err)
+	require.Equal(t, `table "users" {
+  column "id" {
+    null = false
+    type = sql("text")
+  }
+  primary_key {
+    columns = [column.id]
+  }
+}
+`, string(buf))
+
+	// Include primary key on marshaling.
+	spec.PrimaryKey.Name = "custom_name"
+	buf, err = schemahcl.Marshal.MarshalSpec(spec)
+	require.NoError(t, err)
+	require.Equal(t, `table "users" {
+  column "id" {
+    null = false
+    type = sql("text")
+  }
+  primary_key "custom_name" {
+    columns = [column.id]
+  }
+}
+`, string(buf))
+}
Repository: ariga/atlas
Pull request #3373 (2025-02-11 08:40:03)

sql/sqlx: ensure drop table with FK first


Patch:
diff --git a/sql/internal/sqlx/plan.go b/sql/internal/sqlx/plan.go
index a225d13a..651ba017 100644
--- a/sql/internal/sqlx/plan.go
+++ b/sql/internal/sqlx/plan.go
@@ -504,7 +504,13 @@ func depOfDrop(o schema.Object, c schema.Change) bool {
 	case *schema.DropTable:
 		deps = c.T.Deps
 		for _, t := range c.T.Triggers {
-			deps = append(deps, t.Deps...)
+			for _, d := range t.Deps {
+				// If the trigger depends on the table that has FK to its parent,
+				// this dependency should be ignored as the FK need be dropped first.
+				if t, ok := d.(*schema.Table); !ok || !refTo(t.ForeignKeys, c.T) {
+					deps = append(deps, d)
+				}
+			}
 		}
 	case *schema.DropView:
 		deps = c.V.Deps


Test patch:
diff --git a/sql/internal/sqlx/plan_test.go b/sql/internal/sqlx/plan_test.go
index f2aff28a..078ba0e4 100644
--- a/sql/internal/sqlx/plan_test.go
+++ b/sql/internal/sqlx/plan_test.go
@@ -179,3 +179,21 @@ func TestSameView(t *testing.T) {
 	require.False(t, SameView(v1, schema.NewView("v1", "")))
 	require.False(t, SameView(v1, schema.NewView("v1", "").SetSchema(schema.New("private"))))
 }
+
+func TestSortDropTables_WithFK(t *testing.T) {
+	t1 := schema.NewTable("t1").AddColumns(schema.NewColumn("c1"))
+	t2 := schema.NewTable("t2").AddColumns(schema.NewColumn("c1"), schema.NewColumn("c2"))
+	t2.AddForeignKeys(
+		schema.NewForeignKey("FK_t2_t1").
+			AddColumns(t2.Columns[1]).
+			AddRefColumns(t1.Columns[0]).
+			SetRefTable(t1),
+	)
+	t1.Triggers = []*schema.Trigger{
+		{Table: t1, Deps: []schema.Object{t2}},
+	}
+	changes := []schema.Change{&schema.DropTable{T: t2}, &schema.DropTable{T: t1}}
+	require.Equal(t, []schema.Change{changes[0], changes[1]}, SortChanges(changes, nil))
+	changes = []schema.Change{&schema.DropTable{T: t1}, &schema.DropTable{T: t2}}
+	require.Equal(t, []schema.Change{changes[1], changes[0]}, SortChanges(changes, nil))
+}
Repository: ariga/atlas
Pull request #3400 (2025-02-23 11:40:33)

sql/postgres limited support in OSS version


Patch:
diff --git a/sql/postgres/migrate_oss.go b/sql/postgres/migrate_oss.go
index c8b2bbc7..2b3756b1 100644
--- a/sql/postgres/migrate_oss.go
+++ b/sql/postgres/migrate_oss.go
@@ -93,46 +93,14 @@ func (s *state) plan(changes []schema.Change) error {
 			err = s.modifyTable(c)
 		case *schema.RenameTable:
 			s.renameTable(c)
-		case *schema.AddFunc:
-			err = s.addFunc(c)
-		case *schema.AddProc:
-			err = s.addProc(c)
-		case *schema.ModifyFunc:
-			err = s.modifyFunc(c)
-		case *schema.ModifyProc:
-			err = s.modifyProc(c)
-		case *schema.RenameFunc:
-			err = s.renameFunc(c)
-		case *schema.RenameProc:
-			err = s.renameProc(c)
-		case *schema.AddView:
-			err = s.addView(c)
-		case *schema.DropView:
-			err = s.dropView(c)
-		case *schema.ModifyView:
-			err = s.modifyView(c)
-		case *schema.RenameView:
-			s.renameView(c)
 		case *schema.DropTable:
 			err = s.dropTable(c)
-		case *schema.DropFunc:
-			err = s.dropFunc(c)
-		case *schema.DropProc:
-			err = s.dropProc(c)
 		case *schema.AddObject:
 			err = s.addObject(c)
 		case *schema.ModifyObject:
 			err = s.modifyObject(c)
 		case *schema.DropObject:
 			err = s.dropObject(c)
-		case *schema.AddTrigger:
-			err = s.addTrigger(c)
-		case *schema.DropTrigger:
-			err = s.dropTrigger(c)
-		case *schema.RenameTrigger:
-			err = s.renameTrigger(c)
-		case *schema.ModifyTrigger:
-			err = s.modifyTrigger(c)
 		default:
 			err = fmt.Errorf("unsupported change %T", c)
 		}


Test patch:
diff --git a/sql/postgres/migrate_oss_test.go b/sql/postgres/migrate_oss_test.go
index d3bb1287..2339334c 100644
--- a/sql/postgres/migrate_oss_test.go
+++ b/sql/postgres/migrate_oss_test.go
@@ -1847,6 +1847,15 @@ func TestPlanChanges(t *testing.T) {
 				},
 			},
 		},
+		// Adding view is not supported in OSS.
+		{
+			changes: []schema.Change{
+				&schema.AddView{
+					V: schema.NewView("v1", "SELECT * FROM users"),
+				},
+			},
+			wantErr: true,
+		},
 	}
 	for i, tt := range tests {
 		t.Run(strconv.Itoa(i), func(t *testing.T) {
Repository: ariga/atlas
Pull request #3456 (2025-04-09 16:39:22)

sql/sqltool: remove call to parent `Checksum()`
The parent can be local directory, that takes all files - including `.down.sql` for golang-migrate format.

Ref: https://github.com/ariga/atlas/pull/1642, https://github.com/ariga/atlas/pull/1652


Patch:
diff --git a/sql/sqltool/tool.go b/sql/sqltool/tool.go
index 19fe7069..3c423c3c 100644
--- a/sql/sqltool/tool.go
+++ b/sql/sqltool/tool.go
@@ -120,9 +120,6 @@ func (d *GolangMigrateDir) Files() ([]migrate.File, error) {
 
 // Checksum implements Dir.Checksum. By default, it calls Files() and creates a checksum from them.
 func (d *GolangMigrateDir) Checksum() (migrate.HashFile, error) {
-	if d, ok := d.FS.(migrate.Dir); ok {
-		return d.Checksum()
-	}
 	files, err := d.Files()
 	if err != nil {
 		return nil, err
@@ -396,9 +393,6 @@ func (d *FlywayDir) Files() ([]migrate.File, error) {
 
 // Checksum implements Dir.Checksum. By default, it calls Files() and creates a checksum from them.
 func (d *FlywayDir) Checksum() (migrate.HashFile, error) {
-	if d, ok := d.FS.(migrate.Dir); ok {
-		return d.Checksum()
-	}
 	files, err := d.Files()
 	if err != nil {
 		return nil, err


Test patch:
diff --git a/sql/sqltool/tool_test.go b/sql/sqltool/tool_test.go
index 2a4ce7f1..cdc9da37 100644
--- a/sql/sqltool/tool_test.go
+++ b/sql/sqltool/tool_test.go
@@ -288,9 +288,7 @@ func TestChecksum(t *testing.T) {
 				return d
 			}(),
 			files: []string{
-				"1_initial.down.sql",
 				"1_initial.up.sql",
-				"2_second_migration.down.sql",
 				"2_second_migration.up.sql",
 			},
 		},
@@ -331,11 +329,9 @@ func TestChecksum(t *testing.T) {
 			}(),
 			files: []string{
 				"B2__baseline.sql",
-				"R__views.sql",
-				"U1__initial.sql",
-				"V1__initial.sql",
-				"V2__second_migration.sql",
 				"V3__third_migration.sql",
+				"v3/V3_1__fourth_migration.sql",
+				"R__views.sql",
 			},
 		},
 		{
Repository: NetApp/harvest
Pull request #2191 (2023-07-07 14:15:18)

fix: s3 dashboard fails to import in Grafana 8.5.15


Patch:
diff --git a/grafana/dashboards/cmode/s3ObjectStorage.json b/grafana/dashboards/cmode/s3ObjectStorage.json
index fbae51a4..c0d86d4f 100644
--- a/grafana/dashboards/cmode/s3ObjectStorage.json
+++ b/grafana/dashboards/cmode/s3ObjectStorage.json
@@ -486,10 +486,7 @@
       "pluginVersion": "8.1.8",
       "targets": [
         {
-          "datasource": {
-            "type": "prometheus",
-            "uid": "EO6UabnVz"
-          },
+          "datasource": "${DS_PROMETHEUS}",
           "exemplar": false,
           "expr": "ontaps3_logical_used_size{datacenter=~\"$Datacenter\",cluster=~\"$Cluster\",svm=~\"$SVM\",bucket=~\"$Bucket\"}",
           "hide": false,
@@ -607,10 +604,7 @@
       "pluginVersion": "8.1.8",
       "targets": [
         {
-          "datasource": {
-            "type": "prometheus",
-            "uid": "EO6UabnVz"
-          },
+          "datasource": "${DS_PROMETHEUS}",
           "exemplar": false,
           "expr": "ontaps3_used_percent{datacenter=~\"$Datacenter\",cluster=~\"$Cluster\",svm=~\"$SVM\",bucket=~\"$Bucket\"}",
           "hide": false,


Test patch:
diff --git a/cmd/tools/grafana/dashboard_test.go b/cmd/tools/grafana/dashboard_test.go
index 7c2390c3..b48c2511 100644
--- a/cmd/tools/grafana/dashboard_test.go
+++ b/cmd/tools/grafana/dashboard_test.go
@@ -120,7 +120,7 @@ func TestDatasource(t *testing.T) {
 
 func checkDashboardForDatasource(t *testing.T, path string, data []byte) {
 	path = shortPath(path)
-	// visit all panel for datasource test
+	// visit all panels for datasource test
 	visitAllPanels(data, func(p string, key, value gjson.Result) {
 		dsResult := value.Get("datasource")
 		panelTitle := value.Get("title").String()
@@ -139,6 +139,27 @@ func checkDashboardForDatasource(t *testing.T, path string, data []byte) {
 		if dsResult.String() != "${DS_PROMETHEUS}" {
 			t.Errorf("dashboard=%s panel=%s has %s datasource should be ${DS_PROMETHEUS}", path, panelTitle, dsResult.String())
 		}
+
+		// Later versions of Grafana introduced a different datasource shape which causes errors
+		// when used in older versions. Detect that here
+		// GOOD "datasource": "${DS_PROMETHEUS}",
+		// BAD  "datasource": {
+		//            "type": "prometheus",
+		//            "uid": "EO6UabnVz"
+		//          },
+		dses := value.Get("targets.#.datasource").Array()
+		for i, ds := range dses {
+			if ds.String() != "${DS_PROMETHEUS}" {
+				targetPath := fmt.Sprintf("%s.target[%d].datasource", p, i)
+				t.Errorf(
+					"dashboard=%s path=%s panel=%s has %s datasource shape that breaks older versions of Grafana",
+					path,
+					targetPath,
+					panelTitle,
+					dsResult.String(),
+				)
+			}
+		}
 	})
 
 	// Check that the variable DS_PROMETHEUS exist