Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit b8d84bf

Browse files
authored
Merge pull request #567 from erizocosmico/fix/reorder-unresolved
analyzer: wait until NaturalJoins are resolved on reorder rule
2 parents 9cdcea0 + 7a97539 commit b8d84bf

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

sql/analyzer/analyzer_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,81 @@ func TestMixInnerAndNaturalJoins(t *testing.T) {
353353
require.NoError(err)
354354
require.Equal(expected, result)
355355
}
356+
357+
func TestReorderProjectionUnresolvedChild(t *testing.T) {
358+
require := require.New(t)
359+
node := plan.NewProject(
360+
[]sql.Expression{
361+
expression.NewUnresolvedQualifiedColumn("rc", "commit_hash"),
362+
expression.NewUnresolvedColumn("commit_author_when"),
363+
},
364+
plan.NewFilter(
365+
expression.JoinAnd(
366+
expression.NewEquals(
367+
expression.NewUnresolvedQualifiedColumn("rc", "repository_id"),
368+
expression.NewLiteral("foo", sql.Text),
369+
),
370+
expression.NewEquals(
371+
expression.NewUnresolvedQualifiedColumn("rc", "ref_name"),
372+
expression.NewLiteral("HEAD", sql.Text),
373+
),
374+
expression.NewEquals(
375+
expression.NewUnresolvedQualifiedColumn("rc", "history_index"),
376+
expression.NewLiteral(int64(0), sql.Int64),
377+
),
378+
),
379+
plan.NewNaturalJoin(
380+
plan.NewInnerJoin(
381+
plan.NewUnresolvedTable("refs", ""),
382+
plan.NewTableAlias("rc",
383+
plan.NewUnresolvedTable("ref_commits", ""),
384+
),
385+
expression.NewAnd(
386+
expression.NewEquals(
387+
expression.NewUnresolvedQualifiedColumn("refs", "ref_name"),
388+
expression.NewUnresolvedQualifiedColumn("rc", "ref_name"),
389+
),
390+
expression.NewEquals(
391+
expression.NewUnresolvedQualifiedColumn("refs", "repository_id"),
392+
expression.NewUnresolvedQualifiedColumn("rc", "repository_id"),
393+
),
394+
),
395+
),
396+
plan.NewTableAlias("c",
397+
plan.NewUnresolvedTable("commits", ""),
398+
),
399+
),
400+
),
401+
)
402+
403+
commits := mem.NewTable("commits", sql.Schema{
404+
{Name: "repository_id", Source: "commits", Type: sql.Text},
405+
{Name: "commit_hash", Source: "commits", Type: sql.Text},
406+
{Name: "commit_author_when", Source: "commits", Type: sql.Text},
407+
})
408+
409+
refs := mem.NewTable("refs", sql.Schema{
410+
{Name: "repository_id", Source: "refs", Type: sql.Text},
411+
{Name: "ref_name", Source: "refs", Type: sql.Text},
412+
})
413+
414+
refCommits := mem.NewTable("ref_commits", sql.Schema{
415+
{Name: "repository_id", Source: "ref_commits", Type: sql.Text},
416+
{Name: "ref_name", Source: "ref_commits", Type: sql.Text},
417+
{Name: "commit_hash", Source: "ref_commits", Type: sql.Text},
418+
{Name: "history_index", Source: "ref_commits", Type: sql.Int64},
419+
})
420+
421+
db := mem.NewDatabase("")
422+
db.AddTable("refs", refs)
423+
db.AddTable("ref_commits", refCommits)
424+
db.AddTable("commits", commits)
425+
426+
catalog := sql.NewCatalog()
427+
catalog.AddDatabase(db)
428+
a := withoutProcessTracking(NewDefault(catalog))
429+
430+
result, err := a.Analyze(sql.NewEmptyContext(), node)
431+
require.NoError(err)
432+
require.True(result.Resolved())
433+
}

sql/analyzer/optimization_rules.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,16 @@ func reorderProjection(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Node, err
6767
// Then we transform the projection
6868
return n.TransformUp(func(node sql.Node) (sql.Node, error) {
6969
project, ok := node.(*plan.Project)
70-
if !ok {
70+
// When we transform the projection, the children will always be
71+
// unresolved in the case we want to fix, as the reorder happens just
72+
// so some columns can be resolved.
73+
// For that, we need to account for NaturalJoin, whose schema can't be
74+
// obtained until it's resolved and ignore the projection for the
75+
// moment until the resolve_natural_joins has finished resolving the
76+
// node and we can tackle it in the next iteration.
77+
// Without this check, it would cause a panic, because NaturalJoin's
78+
// schema method is just a placeholder that should not be called.
79+
if !ok || hasNaturalJoin(project.Child) {
7180
return node, nil
7281
}
7382

@@ -420,3 +429,17 @@ func isTrue(e sql.Expression) bool {
420429
lit.Type() == sql.Boolean &&
421430
lit.Value().(bool)
422431
}
432+
433+
// hasNaturalJoin checks whether there is a natural join at some point in the
434+
// given node and its children.
435+
func hasNaturalJoin(node sql.Node) bool {
436+
var found bool
437+
plan.Inspect(node, func(node sql.Node) bool {
438+
if _, ok := node.(*plan.NaturalJoin); ok {
439+
found = true
440+
return false
441+
}
442+
return true
443+
})
444+
return found
445+
}

0 commit comments

Comments
 (0)