diff --git a/go/vt/schemadiff/errors.go b/go/vt/schemadiff/errors.go index 74d1c231a19..cb015c42f52 100644 --- a/go/vt/schemadiff/errors.go +++ b/go/vt/schemadiff/errors.go @@ -389,11 +389,20 @@ func (e *IndexNeededByForeignKeyError) Error() string { } type ViewDependencyUnresolvedError struct { - View string + View string + MissingReferencedEntities []string } func (e *ViewDependencyUnresolvedError) Error() string { - return fmt.Sprintf("view %s has unresolved/loop dependencies", sqlescape.EscapeID(e.View)) + var b strings.Builder + fmt.Fprintf(&b, "view %s has unresolved/loop dependencies: ", sqlescape.EscapeID(e.View)) + for i, entity := range e.MissingReferencedEntities { + if i > 0 { + b.WriteString(", ") + } + sqlescape.WriteEscapeID(&b, entity) + } + return b.String() } type InvalidColumnReferencedInViewError struct { diff --git a/go/vt/schemadiff/schema.go b/go/vt/schemadiff/schema.go index c251779061c..658fdab37c5 100644 --- a/go/vt/schemadiff/schema.go +++ b/go/vt/schemadiff/schema.go @@ -341,7 +341,14 @@ func (s *Schema) normalize(hints *DiffHints) error { if _, ok := dependencyLevels[v.Name()]; !ok { // We _know_ that in this iteration, at least one view is found unassigned a dependency level. // We gather all the errors. - errs = errors.Join(errs, &ViewDependencyUnresolvedError{View: v.ViewName.Name.String()}) + dependentNames := getViewDependentTableNames(v.CreateView) + missingReferencedEntities := []string{} + for _, name := range dependentNames { + if _, ok := dependencyLevels[name]; !ok { + missingReferencedEntities = append(missingReferencedEntities, name) + } + } + errs = errors.Join(errs, &ViewDependencyUnresolvedError{View: v.ViewName.Name.String(), MissingReferencedEntities: missingReferencedEntities}) // We still add it so it shows up in the output if that is used for anything. s.sorted = append(s.sorted, v) } diff --git a/go/vt/schemadiff/schema_test.go b/go/vt/schemadiff/schema_test.go index 43cfcf78bdc..1c98e71011b 100644 --- a/go/vt/schemadiff/schema_test.go +++ b/go/vt/schemadiff/schema_test.go @@ -119,12 +119,26 @@ func TestNewSchemaFromQueriesUnresolved(t *testing.T) { ) schema, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.Error(t, err) - assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) + assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7", MissingReferencedEntities: []string{"v8"}}).Error()) v := schema.sorted[len(schema.sorted)-1] assert.IsType(t, &CreateViewEntity{}, v) assert.Equal(t, "CREATE VIEW `v7` AS SELECT * FROM `v8`, `t2`", v.Create().CanonicalStatementString()) } +func TestNewSchemaFromQueriesUnresolvedMulti(t *testing.T) { + // v8 does not exist + queries := append(schemaTestCreateQueries, + "create view v7 as select * from v8, t2, t20, v21", + ) + schema, err := NewSchemaFromQueries(NewTestEnv(), queries) + assert.Error(t, err) + assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7", MissingReferencedEntities: []string{"v8", "t20", "v21"}}).Error()) + assert.Equal(t, "view `v7` has unresolved/loop dependencies: `v8`, `t20`, `v21`", err.Error()) + v := schema.sorted[len(schema.sorted)-1] + assert.IsType(t, &CreateViewEntity{}, v) + assert.Equal(t, "CREATE VIEW `v7` AS SELECT * FROM `v8`, `t2`, `t20`, `v21`", v.Create().CanonicalStatementString()) +} + func TestNewSchemaFromQueriesWithSQLKeyword(t *testing.T) { queries := []string{ "create table `order` (id int primary key, info int not null)", @@ -141,7 +155,7 @@ func TestNewSchemaFromQueriesUnresolvedAlias(t *testing.T) { ) _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.Error(t, err) - assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) + assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7", MissingReferencedEntities: []string{"something_else"}}).Error()) } func TestNewSchemaFromQueriesViewFromDual(t *testing.T) { @@ -171,7 +185,7 @@ func TestNewSchemaFromQueriesLoop(t *testing.T) { _, err := NewSchemaFromQueries(NewTestEnv(), queries) require.Error(t, err) err = vterrors.UnwrapFirst(err) - assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) + assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7", MissingReferencedEntities: []string{"v8"}}).Error()) } func TestToSQL(t *testing.T) { @@ -582,7 +596,7 @@ SELECT ) AS ranking FROM users AS u JOIN earnings AS e ON e.user_id = u.id; `, - expectErr: &ViewDependencyUnresolvedError{View: "user_earnings_ranking"}, + expectErr: &ViewDependencyUnresolvedError{View: "user_earnings_ranking", MissingReferencedEntities: []string{"earnings"}}, }, } for _, ts := range tt {