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

Refactoring of plan building #7054

Merged
merged 11 commits into from
Nov 23, 2020
4 changes: 2 additions & 2 deletions go/test/endtoend/vtgate/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,12 +469,12 @@ func assertMatchesNoOrder(t *testing.T, conn *mysql.Conn, query, expected string
func assertIsEmpty(t *testing.T, conn *mysql.Conn, query string) {
t.Helper()
qr := exec(t, conn, query)
assert.Empty(t, qr.Rows)
assert.Empty(t, qr.Rows, "for query: "+query)
}

func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result {
t.Helper()
qr, err := conn.ExecuteFetch(query, 1000, true)
require.NoError(t, err)
require.NoError(t, err, "for query: "+query)
return qr
}
1 change: 1 addition & 0 deletions go/vt/sqlparser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type (
AddOrder(*Order)
SetLimit(*Limit)
SetLock(lock Lock)
MakeDistinct()
SQLNode
}

Expand Down
15 changes: 15 additions & 0 deletions go/vt/sqlparser/ast_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,11 @@ func (node *Select) SetLock(lock Lock) {
node.Lock = lock
}

// MakeDistinct makes the statement distinct
func (node *Select) MakeDistinct() {
node.Distinct = true
}

// AddWhere adds the boolean expression to the
// WHERE clause as an AND condition.
func (node *Select) AddWhere(expr Expr) {
Expand Down Expand Up @@ -780,6 +785,11 @@ func (node *ParenSelect) SetLock(lock Lock) {
node.Select.SetLock(lock)
}

// MakeDistinct implements the SelectStatement interface
func (node *ParenSelect) MakeDistinct() {
node.Select.MakeDistinct()
}

// AddOrder adds an order by element
func (node *Union) AddOrder(order *Order) {
node.OrderBy = append(node.OrderBy, order)
Expand All @@ -795,6 +805,11 @@ func (node *Union) SetLock(lock Lock) {
node.Lock = lock
}

// MakeDistinct implements the SelectStatement interface
func (node *Union) MakeDistinct() {
node.UnionSelects[len(node.UnionSelects)-1].Distinct = true
}

//Unionize returns a UNION, either creating one or adding SELECT to an existing one
func Unionize(lhs, rhs SelectStatement, distinct bool, by OrderBy, limit *Limit, lock Lock) *Union {
union, isUnion := lhs.(*Union)
Expand Down
213 changes: 0 additions & 213 deletions go/vt/vtgate/planbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,96 +30,6 @@ import (
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
)

//-------------------------------------------------------------------------

// builder defines the interface that a primitive must
// satisfy.
type builder interface {
// Order is the execution order of the primitive. If there are subprimitives,
// the order is one above the order of the subprimitives.
// This is because the primitive executes its subprimitives first and
// processes their results to generate its own values.
// Please copy code from an existing primitive to define this function.
Order() int

// ResultColumns returns the list of result columns the
// primitive returns.
// Please copy code from an existing primitive to define this function.
ResultColumns() []*resultColumn

// Reorder reassigns order for the primitive and its sub-primitives.
// The input is the order of the previous primitive that should
// execute before this one.
Reorder(int)

// First returns the first builder of the tree,
// which is usually the left most.
First() builder

// PushFilter pushes a WHERE or HAVING clause expression
// to the specified origin.
PushFilter(pb *primitiveBuilder, filter sqlparser.Expr, whereType string, origin builder) error

// PushSelect pushes the select expression to the specified
// originator. If successful, the originator must create
// a resultColumn entry and return it. The top level caller
// must accumulate these result columns and set the symtab
// after analysis.
PushSelect(pb *primitiveBuilder, expr *sqlparser.AliasedExpr, origin builder) (rc *resultColumn, colNumber int, err error)

// MakeDistinct makes the primitive handle the distinct clause.
MakeDistinct() (builder, error)
// PushGroupBy makes the primitive handle the GROUP BY clause.
PushGroupBy(sqlparser.GroupBy) error

// PushOrderBy pushes the ORDER BY clause. It returns the
// the current primitive or a replacement if a new one was
// created.
PushOrderBy(sqlparser.OrderBy) (builder, error)

// SetUpperLimit is an optimization hint that tells that primitive
// that it does not need to return more than the specified number of rows.
// A primitive that cannot perform this can ignore the request.
SetUpperLimit(count sqlparser.Expr)

// PushMisc pushes miscelleaneous constructs to all the primitives.
PushMisc(sel *sqlparser.Select) error

// Wireup performs the wire-up work. Nodes should be traversed
// from right to left because the rhs nodes can request vars from
// the lhs nodes.
Wireup(bldr builder, jt *jointab) error

// SupplyVar finds the common root between from and to. If it's
// the common root, it supplies the requested var to the rhs tree.
// If the primitive already has the column in its list, it should
// just supply it to the 'to' node. Otherwise, it should request
// for it by calling SupplyCol on the 'from' sub-tree to request the
// column, and then supply it to the 'to' node.
SupplyVar(from, to int, col *sqlparser.ColName, varname string)

// SupplyCol is meant to be used for the wire-up process. This function
// changes the primitive to supply the requested column and returns
// the resultColumn and column number of the result. SupplyCol
// is different from PushSelect because it may reuse an existing
// resultColumn, whereas PushSelect guarantees the addition of a new
// result column and returns a distinct symbol for it.
SupplyCol(col *sqlparser.ColName) (rc *resultColumn, colNumber int)

// SupplyWeightString must supply a weight_string expression of the
// specified column.
SupplyWeightString(colNumber int) (weightcolNumber int, err error)

// PushLock pushes "FOR UPDATE", "LOCK IN SHARE MODE" down to all routes
PushLock(lock sqlparser.Lock) error

// Primitive returns the underlying primitive.
// This function should only be called after Wireup is finished.
Primitive() engine.Primitive
}

//-------------------------------------------------------------------------

// ContextVSchema defines the interface for this package to fetch
// info about tables.
type ContextVSchema interface {
Expand All @@ -135,133 +45,10 @@ type ContextVSchema interface {
SysVarSetEnabled() bool
}

//-------------------------------------------------------------------------

// builderCommon implements some common functionality of builders.
// Make sure to override in case behavior needs to be changed.
type builderCommon struct {
order int
input builder
}

func newBuilderCommon(input builder) builderCommon {
return builderCommon{input: input}
}

func (bc *builderCommon) Order() int {
return bc.order
}

func (bc *builderCommon) Reorder(order int) {
bc.input.Reorder(order)
bc.order = bc.input.Order() + 1
}

func (bc *builderCommon) First() builder {
return bc.input.First()
}

func (bc *builderCommon) ResultColumns() []*resultColumn {
return bc.input.ResultColumns()
}

func (bc *builderCommon) SetUpperLimit(count sqlparser.Expr) {
bc.input.SetUpperLimit(count)
}

func (bc *builderCommon) PushMisc(sel *sqlparser.Select) error {
return bc.input.PushMisc(sel)
}

func (bc *builderCommon) Wireup(bldr builder, jt *jointab) error {
return bc.input.Wireup(bldr, jt)
}

func (bc *builderCommon) SupplyVar(from, to int, col *sqlparser.ColName, varname string) {
bc.input.SupplyVar(from, to, col, varname)
}

func (bc *builderCommon) SupplyCol(col *sqlparser.ColName) (rc *resultColumn, colNumber int) {
return bc.input.SupplyCol(col)
}

func (bc *builderCommon) SupplyWeightString(colNumber int) (weightcolNumber int, err error) {
return bc.input.SupplyWeightString(colNumber)
}

//-------------------------------------------------------------------------

type truncater interface {
SetTruncateColumnCount(int)
}

// resultsBuilder is a superset of builderCommon. It also handles
// resultsColumn functionality.
type resultsBuilder struct {
builderCommon
resultColumns []*resultColumn
weightStrings map[*resultColumn]int
truncater truncater
}

func newResultsBuilder(input builder, truncater truncater) resultsBuilder {
return resultsBuilder{
builderCommon: newBuilderCommon(input),
resultColumns: input.ResultColumns(),
weightStrings: make(map[*resultColumn]int),
truncater: truncater,
}
}

func (rsb *resultsBuilder) ResultColumns() []*resultColumn {
return rsb.resultColumns
}

// SupplyCol is currently unreachable because the builders using resultsBuilder
// are currently above a join, which is the only builder that uses it for now.
// This can change if we start supporting correlated subqueries.
func (rsb *resultsBuilder) SupplyCol(col *sqlparser.ColName) (rc *resultColumn, colNumber int) {
c := col.Metadata.(*column)
for i, rc := range rsb.resultColumns {
if rc.column == c {
return rc, i
}
}
rc, colNumber = rsb.input.SupplyCol(col)
if colNumber < len(rsb.resultColumns) {
return rc, colNumber
}
// Add result columns from input until colNumber is reached.
for colNumber >= len(rsb.resultColumns) {
rsb.resultColumns = append(rsb.resultColumns, rsb.input.ResultColumns()[len(rsb.resultColumns)])
}
rsb.truncater.SetTruncateColumnCount(len(rsb.resultColumns))
return rc, colNumber
}

func (rsb *resultsBuilder) SupplyWeightString(colNumber int) (weightcolNumber int, err error) {
rc := rsb.resultColumns[colNumber]
if weightcolNumber, ok := rsb.weightStrings[rc]; ok {
return weightcolNumber, nil
}
weightcolNumber, err = rsb.input.SupplyWeightString(colNumber)
if err != nil {
return 0, nil
}
rsb.weightStrings[rc] = weightcolNumber
if weightcolNumber < len(rsb.resultColumns) {
return weightcolNumber, nil
}
// Add result columns from input until weightcolNumber is reached.
for weightcolNumber >= len(rsb.resultColumns) {
rsb.resultColumns = append(rsb.resultColumns, rsb.input.ResultColumns()[len(rsb.resultColumns)])
}
rsb.truncater.SetTruncateColumnCount(len(rsb.resultColumns))
return weightcolNumber, nil
}

//-------------------------------------------------------------------------

// Build builds a plan for a query based on the specified vschema.
// This method is only used from tests
func Build(query string, vschema ContextVSchema) (*engine.Plan, error) {
Expand Down
Loading