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

schemadiff: logically Apply() a diff to a CreateTable, CreateView or to a Schema #10183

Merged
merged 15 commits into from
May 2, 2022

Conversation

shlomi-noach
Copy link
Contributor

Description

In this PR schemadiff can Apply a change onto a declarative table, view or schema, via these new public functions:

func (c *CreateTableEntity) Apply(diff EntityDiff) (Entity, error)
func (c *CreateViewEntity) Apply(diff EntityDiff) (Entity, error)
func (s *Schema) Apply(diffs []EntityDiff) (*Schema, error)

as can be seen in the above, the things we can apply are:

  • An ALTER TABLE onto a CREATE TABLE entity
  • An ALTER VIEW onto a CREATE VIEW entity
  • A list of diffs on a Schema object. These diffs are CREATE/DROP/ALTER TABLE/SCHEMA statements

In each of the above functions:

  • we return an error if the operation is invalid (e.g. dropping a column when the column does not exist; adding a column when it already exists; etc)
  • we do not modify the receiver; instead, is successful, a new object is returns
  • we only support diffs that are generated by Diff()

So the idea is to be able to run purely-declarative schema changes, including ALTER TABLE statements.

The tests are trivially integrated into pre-existing tests: where we already validate a diff between object1 and object2, now attempt to apply the diff to object1 and expect the result to be identical to object2.

Related Issue(s)

This PR extends #10164 as it relies on some of its functionality

Checklist

  • "Backport me!" label has been added if this change should be backported
  • Tests were added or are not required
  • Documentation was added or is not required

cc @dbussink

Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
@shlomi-noach shlomi-noach added Type: Enhancement Logical improvement (somewhere between a bug and feature) Component: Query Serving release notes none labels May 1, 2022
@shlomi-noach
Copy link
Contributor Author

Working on final test failures. Moving to draft in the meantime.

@shlomi-noach shlomi-noach marked this pull request as draft May 1, 2022 12:44
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
@shlomi-noach
Copy link
Contributor Author

OK all good. I accidentally made a regression and pushed it before testing. Now fixed.

@shlomi-noach shlomi-noach marked this pull request as ready for review May 1, 2022 13:26
@@ -134,6 +135,8 @@ func getViewDependentTableNames(createView *sqlparser.CreateView) (names []strin
// normalize is called as part of Schema creation process. The user may only get a hold of normalized schema.
// It validates some cross-entity constraints, and orders entity based on dependencies (e.g. tables, views that read from tables, 2nd level views, etc.)
func (s *Schema) normalize() error {
s.named = map[string]Entity{}
s.sorted = []Entity{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these added for a specific reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are. At the end of apply() we call normalize, and we need to make sure that existing named values are erased. sorted happens to be unpopulated; but normalize really expects both to be empty.
We could clear both in apply, as right now this is the only path where the values get populated before normalize. But I feel normalize is the safer place to do that.

if !ok {
return ErrEntityTypeMismatch
}
s.tables[i] = toCreateTableEntity
Copy link
Contributor

Choose a reason for hiding this comment

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

Does s.named need to be updated here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does not, because it is implied that the view name already exists in named, and an update would just re-apply same name.

Copy link
Contributor

Choose a reason for hiding this comment

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

@shlomi-noach I'm probably missing something, but doesn't then calling for example Entity(name) return the old entity without the change applied? Or is this updated then somewhere else?

The reason I was asking is because this does get updated on create / drop so that's what caught my eye.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dbussink gotcha. named is only used in this function to determine existence/nonexistence of an entity. At the end of this function, we call normalize() which completely rebuilds named. We could update the entity value here, but it then gets deleted and recreated in normalize anyhow.

Copy link
Contributor

Choose a reason for hiding this comment

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

@shlomi-noach Ah ok, if that's the case I think there's an inconsistency though in the add / drop logic.

When a create is done, we don't add it to named so if the diffs slice has multiple creates for the same table, it would not error but have a last one wins.

On the other hand, if diffs contains multiple drops for the same table, it would error on the second one since it does delete from named.

I have no super strong opinion on what the behavior should be, but I think it should be consistent between create and drop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, there is an inconsistency. Fixing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please see updates to Schema.apply() and consistent use of s.named

if !ok {
return ErrEntityTypeMismatch
}
s.views[i] = toCreateViewEntity
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, does s.named need to be updated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

same as above, there is no need.

@@ -275,6 +277,46 @@ func (c *CreateTableEntity) diffTableCharset(
return ""
}

// isDefaultTableOptionValue sees if the value for a TableOption is also its default value
func isDefaultTableOptionValue(option *sqlparser.TableOption) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not for this PR, but should there be something similar for what the defaults are for column level options?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for our discussion later on.

}
}
if !afterColFound {
return errors.Wrapf(ErrApplyColumnNotFound, "%v", after.Name.String())
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be simplified to errors.Wrap(ErrApplyColumnNotFound, after.Name.String())? And for all the other further cases down in this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

return errors.Wrapf(ErrApplyColumnNotFound, "%v", after.Name.String())
}
default:
// no change in positiion
Copy link
Contributor

Choose a reason for hiding this comment

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

Double ii

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

// Remove partitioning
c.TableSpec.PartitionOption = nil
default:
return errors.Wrapf(ErrUnsupportedApplyOperation, "%v", sqlparser.String(spec))
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be errors.Wrap(ErrUnsupportedApplyOperation, sqlparser.String(spec))?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
@shlomi-noach shlomi-noach merged commit 92e0bed into vitessio:main May 2, 2022
@shlomi-noach shlomi-noach deleted the schemadiff-apply-diff branch May 2, 2022 10:35
@shlomi-noach
Copy link
Contributor Author

Tracking issue: #10203

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Query Serving Type: Enhancement Logical improvement (somewhere between a bug and feature)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants