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

sql: multiple foreign key cascades bug fixes #42624

Merged
merged 1 commit into from Nov 26, 2019

Conversation

rohany
Copy link
Contributor

@rohany rohany commented Nov 20, 2019

This PR fixes multiple existing foreign key cascade bugs.

  • Fixes a panic on performing cascade updates on tables with
    multiple column families.
  • Fixes a bug where a self referential foreign key constraint with set default
    would not be maintained on a cascading update.
  • Fixes a bug where multiple self referential foreign key constraints
    would cause all the rows in the referenced constraint columns
    to be set to null or default on a cascading update.

The panic was caused by the updater having a mismatch of
fetched columns when only certain column families were
fetched by the get requset.

The first cascade bug was caused by usage of an invalid batch, causing updates
made by the updater to not be visible to reads made by the cascader.

The final bug was a logic error that was caused by rows being
enqueued in the cascader to be updatedeven when there wasn't a change
on the foreign key column that it was being checked for.
This caused rows to be updated when constrained values
weren't even changed, leading to cascades that could change large numbers
of unintended rows in the table.

Fixes #42120.

Release note (bug fix): This PR fixes multiple existing bugs:

  • Fixes a panic on performing cascade updates on tables with
    multiple column families.
  • Fixes a bug where a self referential foreign key constraint with set default
    would not be maintained on a cascading update.
  • Fixes a bug where multiple self referential foreign key constraints
    would cause all the rows in the referenced constraint columns
    to be set to null or default on a cascading update.

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@rohany
Copy link
Contributor Author

rohany commented Nov 20, 2019

I want to get a couple eyes on these changes, as some of the bugs were pretty subtle.
This probably should be backported to 19.2.2 right?
Also @BramGruneir, i think checking the datums when popping from the queue is the cleanest -- putting the updated information in a map and pulling it out later seems trickier / touches more code paths.

Copy link
Member

@RaduBerinde RaduBerinde left a comment

Choose a reason for hiding this comment

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

Thank you very much for working on this. It's some very complicated code :(

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @BramGruneir, @jordanlewis, @RaduBerinde, and @rohany)


pkg/sql/row/cascader.go, line 766 at r1 (raw file):

			origValues := values.originalValues.At(i)
			updatedValues := values.updatedValues.At(i)
			if len(origValues) == len(updatedValues) {

I don't have a good understanding of this code, but this seems sketchy. What if we need to stop the cascade when the lengths aren't different?

But.. I bet this is a net improvement to the status-quo :)

Copy link
Member

@RaduBerinde RaduBerinde left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @BramGruneir, @jordanlewis, @RaduBerinde, and @rohany)


pkg/sql/row/updater.go, line 305 at r1 (raw file):

	}

	// Prevent people from using b directly, as it does not get flushed.

This is strange, why don't we just name the argument batch?

Copy link
Contributor Author

@rohany rohany left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @BramGruneir, @jordanlewis, and @RaduBerinde)


pkg/sql/row/cascader.go, line 766 at r1 (raw file):

Previously, RaduBerinde wrote…

I don't have a good understanding of this code, but this seems sketchy. What if we need to stop the cascade when the lengths aren't different?

But.. I bet this is a net improvement to the status-quo :)

from what I can understand the lengths are different when either a delete happens (on which updatedValues is length 0), or on the original update that starts the cascade. In both cases this check isn't needed


pkg/sql/row/updater.go, line 305 at r1 (raw file):

Previously, RaduBerinde wrote…

This is strange, why don't we just name the argument batch?

oh thats really big brain -- i'll update that.

Copy link
Member

@RaduBerinde RaduBerinde left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @BramGruneir, @jordanlewis, @RaduBerinde, and @rohany)


pkg/sql/row/cascader.go, line 766 at r1 (raw file):

Previously, rohany (Rohan Yadav) wrote…

from what I can understand the lengths are different when either a delete happens (on which updatedValues is length 0), or on the original update that starts the cascade. In both cases this check isn't needed

Oh, that's great, please add that info to the comment!

@RaduBerinde
Copy link
Member


pkg/sql/row/cascader.go, line 766 at r1 (raw file):

Previously, RaduBerinde wrote…

Oh, that's great, please add that info to the comment!

Is the converse true? If it's a delete or an update that's not the original one, is it guaranteed that the lengths are different? If no, it seems strange than if the lengths happen to match we check something and otherwise we don't..

Copy link
Contributor Author

@rohany rohany left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @BramGruneir, @jordanlewis, and @RaduBerinde)


pkg/sql/row/cascader.go, line 766 at r1 (raw file):

Previously, RaduBerinde wrote…

Is the converse true? If it's a delete or an update that's not the original one, is it guaranteed that the lengths are different? If no, it seems strange than if the lengths happen to match we check something and otherwise we don't..

i believe so. The cascader always reads the full row from the table when performing updates, so if the cascader itself pushes something onto the queue here, then the original row and the updated row have the same length, equal to the number of columns in the table

Copy link
Contributor Author

@rohany rohany left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @BramGruneir, @jordanlewis, and @RaduBerinde)


pkg/sql/row/cascader.go, line 766 at r1 (raw file):

Previously, rohany (Rohan Yadav) wrote…

i believe so. The cascader always reads the full row from the table when performing updates, so if the cascader itself pushes something onto the queue here, then the original row and the updated row have the same length, equal to the number of columns in the table

Actually, let me specify this -- i think the lengths are only different in the case of deletions.

Copy link
Contributor Author

@rohany rohany left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @BramGruneir, @jordanlewis, and @RaduBerinde)


pkg/sql/row/cascader.go, line 766 at r1 (raw file):

Previously, rohany (Rohan Yadav) wrote…

Actually, let me specify this -- i think the lengths are only different in the case of deletions.

Thanks for the insightful question radu! I thought about it more and i think its true that the lengths are always going to be the same. Its just in the case of deletes that the updatedValues row container is nil. I removed the length check there.


pkg/sql/row/updater.go, line 305 at r1 (raw file):

Previously, rohany (Rohan Yadav) wrote…

oh thats really big brain -- i'll update that.

done

Copy link
Member

@RaduBerinde RaduBerinde left a comment

Choose a reason for hiding this comment

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

:lgtm:

Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @BramGruneir, @jordanlewis, and @RaduBerinde)

Copy link
Member

@BramGruneir BramGruneir left a comment

Choose a reason for hiding this comment

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

:lgtm:

With some very minor changes.

Reviewed 1 of 3 files at r1, 2 of 2 files at r2.
Reviewable status: :shipit: complete! 2 of 0 LGTMs obtained (waiting on @jordanlewis and @rohany)


pkg/sql/logictest/testdata/logic_test/cascade, line 3771 at r2 (raw file):

3  4  2  2
4  1  2  3
5  2  3  4

Please add some cleanup here (and the earlier subtest). I know it's not required but I tried to make sure that each test was self contained.


pkg/sql/row/cascader.go, line 762 at r2 (raw file):

		// We skip this check when values.updatedValues is nil. This happens when a cascade is started
		// because of a delete, upon which a row container is not created because there is not an
		// updated value when a row is deleted.

I still think it might be worthwhile to consider adding a mask might make this check significantly faster and not have to run compare so often. This whole check could be a logical and.

I wouldn't put that work in now, since the optimizer should be taken over this code.

Perhaps a todo just in case?

This PR fixes multiple existing foreign key cascade bugs.

* Fixes a panic on performing cascade updates on tables with
 multiple column families.
* Fixes a bug where a self referential foreign key constraint with set default
 would not be maintained on a cascading update.
* Fixes a bug where multiple self referential foreign key constraints
 would cause all the rows in the referenced constraint columns
 to be set to null or default on a cascading update.

The panic was caused by the updater having a mismatch of
fetched columns when only certain column families were
fetched by the get requset.

The first cascade bug was caused by usage of an invalid batch, causing updates
made by the updater to not be visible to reads made by the cascader.

The final bug was a logic error that was caused by rows being
enqueued in the cascader to be updatedeven when there wasn't a change
*on the foreign key column that it was being checked for*.
This caused rows to be updated when constrained values
weren't even changed, leading to cascades that could change large numbers
of unintended rows in the table.

Fixes cockroachdb#42120.

Release note (bug fix): This PR fixes multiple existing bugs:
* Fixes a panic on performing cascade updates on tables with
 multiple column families.
* Fixes a bug where a self referential foreign key constraint with set default
 would not be maintained on a cascading update.
* Fixes a bug where multiple self referential foreign key constraints
 would cause all the rows in the referenced constraint columns
 to be set to null or default on a cascading update.
@rohany
Copy link
Contributor Author

rohany commented Nov 25, 2019

TFTR! nits have been addressed.

Copy link
Member

@BramGruneir BramGruneir left a comment

Choose a reason for hiding this comment

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

Reviewed 2 of 2 files at r3.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (and 2 stale) (waiting on @jordanlewis and @rohany)

@rohany
Copy link
Contributor Author

rohany commented Nov 25, 2019

bors r+

@rohany
Copy link
Contributor Author

rohany commented Nov 25, 2019

@RaduBerinde, i think #42231 needs to be backported as well for me to backport this.

@craig
Copy link
Contributor

craig bot commented Nov 25, 2019

Build failed (retrying...)

@craig
Copy link
Contributor

craig bot commented Nov 25, 2019

Build failed (retrying...)

craig bot pushed a commit that referenced this pull request Nov 26, 2019
42624: sql: multiple foreign key cascades bug fixes r=rohany a=rohany

This PR fixes multiple existing foreign key cascade bugs.

* Fixes a panic on performing cascade updates on tables with
 multiple column families.
* Fixes a bug where a self referential foreign key constraint with set default
 would not be maintained on a cascading update.
* Fixes a bug where multiple self referential foreign key constraints
 would cause all the rows in the referenced constraint columns
 to be set to null or default on a cascading update.

The panic was caused by the updater having a mismatch of
fetched columns when only certain column families were
fetched by the get requset.

The first cascade bug was caused by usage of an invalid batch, causing updates
made by the updater to not be visible to reads made by the cascader.

The final bug was a logic error that was caused by rows being
enqueued in the cascader to be updatedeven when there wasn't a change
*on the foreign key column that it was being checked for*.
This caused rows to be updated when constrained values
weren't even changed, leading to cascades that could change large numbers
of unintended rows in the table.

Fixes #42120.

Release note (bug fix): This PR fixes multiple existing bugs:
* Fixes a panic on performing cascade updates on tables with
 multiple column families.
* Fixes a bug where a self referential foreign key constraint with set default
 would not be maintained on a cascading update.
* Fixes a bug where multiple self referential foreign key constraints
 would cause all the rows in the referenced constraint columns
 to be set to null or default on a cascading update.

Co-authored-by: Rohan Yadav <rohany@alumni.cmu.edu>
@craig
Copy link
Contributor

craig bot commented Nov 26, 2019

Build succeeded

@craig craig bot merged commit a31f5af into cockroachdb:master Nov 26, 2019
SQL Features (Deprecated - use SQL Experience board) automation moved this from Open PRs to Done Nov 26, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

sql: "invalid row length" in FK cascades code
4 participants