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

Update INNER JOIN Alpha #583

Merged
merged 33 commits into from Oct 18, 2021
Merged

Update INNER JOIN Alpha #583

merged 33 commits into from Oct 18, 2021

Conversation

VinaiRachakonda
Copy link
Contributor

No description provided.

enginetest/update_queries.go Show resolved Hide resolved
"github.com/dolthub/go-mysql-server/sql/plan"
)

var ErrNonUpdateInnerJoinNotSupports = errors.NewKind("error: Only INNER JOINs are support for Update currently")
Copy link
Member

Choose a reason for hiding this comment

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

Just use sql.ErrUnsupportedFeature

func getResolvedTableFromJoin(node plan.JoinNode) map[string]*plan.ResolvedTable {
toProcess := []sql.Node{node}
ret := make(map[string]*plan.ResolvedTable)
for len(toProcess) > 0 {
Copy link
Member

Choose a reason for hiding this comment

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

You want plan.Inspect here, don't roll your own

Also generalize this to any node (not just a join node), call it getTablesByName, and move it into tables.go

}

// getRowUpdaterMap maps a set of tables to their RowUpdater objects.
func getRowUpdaterMap(ctx *sql.Context, node sql.Node, ij plan.JoinNode) map[string]sql.RowUpdater {
Copy link
Member

Choose a reason for hiding this comment

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

rowUpdatersByTable

}

// getTablesToBeUpdated takes a node and looks for the tables to modified by a SetField.
func getTablesToBeUpdated(node sql.Node) map[string]interface{} {
Copy link
Member

Choose a reason for hiding this comment

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

Return type should be map[string]struct{}{}


// Resolved implements the sql.Node interface.
func (u *UpdateJoin) Resolved() bool {
return true
Copy link
Member

Choose a reason for hiding this comment

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

Don't override this, leave it to UnaryNode


// Children implements the sql.Node interface.
func (u *UpdateJoin) Children() []sql.Node {
return []sql.Node{u.Child}
Copy link
Member

Choose a reason for hiding this comment

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

Don't override


// Schema implements the sql.Node interface.
func (u *UpdateJoin) Schema() sql.Schema {
return u.Child.Schema()
Copy link
Member

Choose a reason for hiding this comment

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

Don't override


// Check if the row has already been updated
cache := u.getOrCreateCache(ctx, tableName)
hash, err := sql.HashOf(oldRow)
Copy link
Member

Choose a reason for hiding this comment

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

This only works if the table has primary keys. Otherwise the table could contain duplicate rows. We need some deeper integration into the table iterator on a second pass. For now, you need to error out in the analyzer if the table is keyless.


// Check if the row has already been updated
cache := u.getOrCreateCache(ctx, tableName)
hash, err := sql.HashOf(oldRow)
Copy link
Member

Choose a reason for hiding this comment

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

This only works if the table has primary keys. Otherwise the table could contain duplicate rows. We need some deeper integration into the table iterator on a second pass. For now, you need to error out in the analyzer if the table is keyless.

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.

General approach looks sound, see comments

var _ sql.RowUpdater = (*updatableJoinUpdater)(nil)

// StatementBegin implements the sql.TableEditor interface.
func (u *updatableJoinUpdater) StatementBegin(ctx *sql.Context) {}
Copy link
Member

Choose a reason for hiding this comment

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

This probably isn't going to cut it. Why aren't you pushing this to the child updaters?


// RowIter implements the sql.Node interface.
func (u *UpdateJoin) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter, error) {
return u.Child.RowIter(ctx, row)
Copy link
Member

Choose a reason for hiding this comment

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

A better way to do this would be to wrap this iterator and only return a row when an update occurs (i.e. the first time one of the two rows was updated)


// DiscardChanges implements the sql.TableEditor interface.
func (u *updatableJoinUpdater) DiscardChanges(ctx *sql.Context, errorEncountered error) error {
u.updatedUpdaterMap = u.initialUpdaterMap
Copy link
Member

Choose a reason for hiding this comment

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

Same Q

@@ -177,6 +177,69 @@ var UpdateTests = []WriteQueryTest{
nil,
nil}},
},
{
Copy link
Member

Choose a reason for hiding this comment

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

Need more test cases:

  1. Updating more than one column in the same table
  2. Updating a column in both tables
  3. Updating a column using the value of a column from the other table

Copy link
Member

Choose a reason for hiding this comment

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

It's good you have these in the script tests, but they should be test cases here as well

@VinaiRachakonda VinaiRachakonda changed the title [WIP] Vinai/update inner join Update INNER JOIN Alpha Oct 15, 2021
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.

You need tests for unsupported joins, should throw sql.ErrUnsupportedFeature

Otherwise, nice work!

enginetest/script_queries.go Show resolved Hide resolved
enginetest/update_queries.go Show resolved Hide resolved
@@ -145,6 +145,26 @@ func getResolvedTable(node sql.Node) *plan.ResolvedTable {
return table
}

// getTablesByNames takes a node and returns all found resolved tables in a map.
func getTablesByNames(node sql.Node) map[string]*plan.ResolvedTable {
Copy link
Member

Choose a reason for hiding this comment

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

byName

})

if schema == nil {
return nil, fmt.Errorf("UpdateJoin accumulator was requested about no join node found")
Copy link
Member

Choose a reason for hiding this comment

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

Rephrase this error message

_ = pr.WriteNode("Update Join")
_ = pr.WriteChildren(u.Child.String())
return pr.String()

Copy link
Member

Choose a reason for hiding this comment

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

Extra blank line

@VinaiRachakonda VinaiRachakonda merged commit 6eb881d into main Oct 18, 2021
@Hydrocharged Hydrocharged deleted the vinai/update-inner-join branch December 8, 2021 07:13
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