Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pick new viable underlying index for foreign keys when current one is dropped #4847

Merged
merged 16 commits into from
Nov 28, 2022

Conversation

jycor
Copy link
Contributor

@jycor jycor commented Nov 22, 2022

When dropping underlying secondary indexes used by foreign key, another existing viable one should be used in its place, unless there isn't one.

TODO:

  • we should show duplicate indexes to primary key because MySQL does

Copy link
Member

@zachmu zachmu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start, needs a little work

return nil, nil, err
}
for _, fk := range fkc.AllKeys() {
newIdx, ok, err := findIndexWithPrefix(t.sch, oldIdx.ColumnNames())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite right: the old index being used might have had more column names than necessary for it to be used with this foreign key.

Consider parent (a, b, c), child (d, e, f). Parent table has an index on (a,b,c) and (a,b). Child declares a foreign key constraint on d references parent (a). We'll use the index (a,b,c) for it. When we drop index (a,b,c), we should fall back to (a,b). But this code doesn't do that, because it tries to use the old index columns, as opposed to the constraint definition.

Add a test.

newFk.ReferencedTableIndex = newIdx.Name()
} else {
// if a replacement index wasn't found; it matched on primary key, so use empty string
newFk.ReferencedTableIndex = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not good enough -- you can't make this assumption. If there is no index available to use after dropping this one, you have to raise an error.

Copy link
Contributor Author

@jycor jycor Nov 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can make this assumption, because we do in other places like here and here.

This is because GMS also checks if a valid prefix index exists, and errors when there isn't (I updated the tests to confirm this behavior). In the case of switching to a PK as the underlying index, GMS actually considers PKs as indexes and will correctly not error. However, on dolt side, we don't treat PK as an index, so findIndexWithPrefix wont be able to find it. A side effect of this is that if there is a viable PK and multiple viable secondary indexes, the PK will only be chosen if we drop all other viable secondary indexes first.

GMS Code for reference:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as there are tests then great

@@ -1186,6 +1186,90 @@ INSERT INTO child_non_unq VALUES ('1', 1), ('2', NULL), ('3', 3), ('4', 3), ('5'
}
}

func TestDropIndex(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better expressed as a Sql script test in ddl_queries.go

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there are any queries that show what underlying secondary index a foreign key is using, which is why I wrote the tests here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed the explicit checks into foreign keys here, my fault.

There are some decent functional tests in GMS for this, so I think you're covered there.

But you don't have coverage for a bunch of scenarios you should be testing. Specifically, there aren't multiple indexes to choose from when you drop the existing one here.

A unit test for findIndexWithPrefix could be more effective for that, your call

Copy link
Member

@zachmu zachmu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments

// setup
root, err = ExecuteSql(dEnv, root, "create table parent (i int);")
require.NoError(t, err)
root, err = ExecuteSql(dEnv, root, "alter table parent add index (i);")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name these indexes explicitly so that the names you use below don't break if we change the auto naming scheme

newFk.ReferencedTableIndex = newIdx.Name()
} else {
// if a replacement index wasn't found; it matched on primary key, so use empty string
newFk.ReferencedTableIndex = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as there are tests then great

@@ -1186,6 +1186,90 @@ INSERT INTO child_non_unq VALUES ('1', 1), ('2', NULL), ('3', 3), ('4', 3), ('5'
}
}

func TestDropIndex(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed the explicit checks into foreign keys here, my fault.

There are some decent functional tests in GMS for this, so I think you're covered there.

But you don't have coverage for a bunch of scenarios you should be testing. Specifically, there aren't multiple indexes to choose from when you drop the existing one here.

A unit test for findIndexWithPrefix could be more effective for that, your call

@@ -113,6 +113,16 @@ func (cmd LsCmd) Exec(ctx context.Context, commandStr string, args []string, dEn
}
}

// TODO: delete this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

require.NoError(t, err)

// dropping secondary index ij, should choose ijk
root, err = ExecuteSql(dEnv, root, "alter table parent drop index ij;")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this deterministic? Odd that it doesn't choose the primary key if suitable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a note that this isn't by design

require.NoError(t, err)

// setup
root, err = ExecuteSql(dEnv, root, "create table parent (i int);")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have two separate test cases in this method. Name them and run them in t.Run() blocks

@@ -1851,6 +1851,7 @@ func (t *AlterableDoltTable) CreateIndex(ctx *sql.Context, idx sql.IndexDef) err
if err != nil {
return err
}
// TODO: why do we need to do this?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

@@ -1851,6 +1851,7 @@ func (t *AlterableDoltTable) CreateIndex(ctx *sql.Context, idx sql.IndexDef) err
if err != nil {
return err
}
// TODO: why do we need to do this?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Member

@zachmu zachmu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just a couple small comments and some code that probably should be removed before checkin

@jycor jycor merged commit fe2eb95 into main Nov 28, 2022
@tbantle22 tbantle22 deleted the james/fks branch March 9, 2023 00:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants