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

Improve typing during query planning #16310

Merged
merged 5 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go/vt/vtgate/planbuilder/operators/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func (a *Aggregator) planOffsets(ctx *plancontext.PlanningContext) Operator {
a.Grouping[idx].ColOffset = offset
gb.ColOffset = offset
}
if gb.WSOffset != -1 || !ctx.SemTable.NeedsWeightString(gb.Inner) {
if gb.WSOffset != -1 || !ctx.NeedsWeightString(gb.Inner) {
continue
}

Expand Down Expand Up @@ -508,7 +508,7 @@ func (a *Aggregator) pushRemainingGroupingColumnsAndWeightStrings(ctx *planconte
a.Grouping[idx].ColOffset = offset
}

if gb.WSOffset != -1 || !ctx.SemTable.NeedsWeightString(gb.Inner) {
if gb.WSOffset != -1 || !ctx.NeedsWeightString(gb.Inner) {
continue
}

Expand Down
2 changes: 1 addition & 1 deletion go/vt/vtgate/planbuilder/operators/distinct.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (d *Distinct) planOffsets(ctx *plancontext.PlanningContext) Operator {
for idx, col := range columns {
e := col.Expr
var wsCol *int
if ctx.SemTable.NeedsWeightString(e) {
if ctx.NeedsWeightString(e) {
offset := d.Source.AddWSColumn(ctx, idx, false)
wsCol = &offset
}
Expand Down
2 changes: 1 addition & 1 deletion go/vt/vtgate/planbuilder/operators/ordering.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (o *Ordering) planOffsets(ctx *plancontext.PlanningContext) Operator {
offset := o.Source.AddColumn(ctx, true, false, aeWrap(order.SimplifiedExpr))
o.Offset = append(o.Offset, offset)

if !ctx.SemTable.NeedsWeightString(order.SimplifiedExpr) {
if !ctx.NeedsWeightString(order.SimplifiedExpr) {
o.WOffset = append(o.WOffset, -1)
continue
}
Expand Down
2 changes: 1 addition & 1 deletion go/vt/vtgate/planbuilder/operators/queryprojection.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ type (
)

func (aggr Aggr) NeedsWeightString(ctx *plancontext.PlanningContext) bool {
return aggr.OpCode.NeedsComparableValues() && ctx.SemTable.NeedsWeightString(aggr.Func.GetArg())
return aggr.OpCode.NeedsComparableValues() && ctx.NeedsWeightString(aggr.Func.GetArg())
}

func (aggr Aggr) GetTypeCollation(ctx *plancontext.PlanningContext) evalengine.Type {
Expand Down
2 changes: 1 addition & 1 deletion go/vt/vtgate/planbuilder/operators/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ func (r *Route) planOffsets(ctx *plancontext.PlanningContext) Operator {
WOffset: -1,
Direction: order.Inner.Direction,
}
if ctx.SemTable.NeedsWeightString(order.SimplifiedExpr) {
if ctx.NeedsWeightString(order.SimplifiedExpr) {
ws := weightStringFor(order.SimplifiedExpr)
offset := r.AddColumn(ctx, true, false, aeWrap(ws))
o.WOffset = offset
Expand Down
109 changes: 108 additions & 1 deletion go/vt/vtgate/planbuilder/plancontext/planning_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
querypb "vitess.io/vitess/go/vt/proto/query"
"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vterrors"
"vitess.io/vitess/go/vt/vtgate/engine/opcode"
"vitess.io/vitess/go/vt/vtgate/evalengine"
"vitess.io/vitess/go/vt/vtgate/semantics"
)
Expand Down Expand Up @@ -212,7 +213,12 @@ func (ctx *PlanningContext) RewriteDerivedTableExpression(expr sqlparser.Expr, t
func (ctx *PlanningContext) TypeForExpr(e sqlparser.Expr) (evalengine.Type, bool) {
t, found := ctx.SemTable.TypeForExpr(e)
if !found {
return t, found
typ := ctx.calculateTypeFor(e)
if typ.Valid() {
ctx.SemTable.ExprTypes[e] = typ
return typ, true
}
return evalengine.NewUnknownType(), false
}
deps := ctx.SemTable.RecursiveDeps(e)
// If the expression is from an outer table, it should be nullable
Expand All @@ -224,6 +230,89 @@ func (ctx *PlanningContext) TypeForExpr(e sqlparser.Expr) (evalengine.Type, bool
return t, true
}

func (ctx *PlanningContext) calculateTypeFor(e sqlparser.Expr) evalengine.Type {
cfg := &evalengine.Config{
ResolveType: func(expr sqlparser.Expr) (evalengine.Type, bool) {
col, isCol := expr.(*sqlparser.ColName)
if !isCol {
return evalengine.NewUnknownType(), false
}
return ctx.SemTable.TypeForExpr(col)
},
Collation: ctx.SemTable.Collation,
Environment: ctx.VSchema.Environment(),
ResolveColumn: func(name *sqlparser.ColName) (int, error) {
// We don't need to resolve the column for type calculation
return 0, nil
},
}
env := evalengine.EmptyExpressionEnv(ctx.VSchema.Environment())

// We need to rewrite the aggregate functions to their corresponding types
// The evaluation engine compiler doesn't handle them, so we replace them with Arguments before
// asking the compiler for the type

// TODO: put this back in when we can calculate the aggregation types correctly
// expr, unknown := ctx.replaceAggrWithArg(e, cfg, env)
// if unknown {
// return evalengine.NewUnknownType()
// }

translatedExpr, err := evalengine.Translate(e, cfg)
if err != nil {
return evalengine.NewUnknownType()
}

typ, err := env.TypeOf(translatedExpr)
if err != nil {
return evalengine.NewUnknownType()
}
return typ
}

// replaceAggrWithArg replaces aggregate functions with Arguments in the given expression.
// this is to prepare for sending the expression to the evalengine compiler to figure out the type
func (ctx *PlanningContext) replaceAggrWithArg(e sqlparser.Expr, cfg *evalengine.Config, env *evalengine.ExpressionEnv) (expr sqlparser.Expr, unknown bool) {
expr = sqlparser.CopyOnRewrite(e, nil, func(cursor *sqlparser.CopyOnWriteCursor) {
agg, ok := cursor.Node().(sqlparser.AggrFunc)
if !ok {
Comment on lines +275 to +280
Copy link
Member

Choose a reason for hiding this comment

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

I see the TODO, do you want to keep this unused method before removing the TODO?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah, that was my thought, but it's not important. if you think it should go, I can delete it

return
}
code, ok := opcode.SupportedAggregates[agg.AggrName()]
if !ok {
// We don't know the type of this aggregate function
// The type calculation will be set to unknown
unknown = true
cursor.StopTreeWalk()
return
}
var inputType evalengine.Type
if arg := agg.GetArg(); arg != nil {
translatedExpr, err := evalengine.Translate(arg, cfg)
if err != nil {
unknown = true
cursor.StopTreeWalk()
return
}

inputType, err = env.TypeOf(translatedExpr)
if err != nil {
unknown = true
cursor.StopTreeWalk()
return
}
}
typ := code.ResolveType(inputType, ctx.VSchema.Environment().CollationEnv())
cursor.Replace(&sqlparser.Argument{
Name: "arg",
Type: typ.Type(),
Size: typ.Size(),
Scale: typ.Scale(),
})
}, nil).(sqlparser.Expr)
return expr, unknown
}

// SQLTypeForExpr returns the sql type of the given expression, with nullable set if the expression is from an outer table.
func (ctx *PlanningContext) SQLTypeForExpr(e sqlparser.Expr) sqltypes.Type {
t, found := ctx.TypeForExpr(e)
Expand All @@ -232,3 +321,21 @@ func (ctx *PlanningContext) SQLTypeForExpr(e sqlparser.Expr) sqltypes.Type {
}
return t.Type()
}

func (ctx *PlanningContext) NeedsWeightString(e sqlparser.Expr) bool {
switch e := e.(type) {
case *sqlparser.WeightStringFuncExpr, *sqlparser.Literal:
return false
default:
typ, found := ctx.TypeForExpr(e)
if !found {
return true
}

if !sqltypes.IsText(typ.Type()) {
return false
}

return !ctx.VSchema.Environment().CollationEnv().IsSupported(typ.Collation())
}
}
Loading
Loading