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

change AndExpr to contain arbitrary many predicates #16671

Closed
wants to merge 14 commits into from
8 changes: 4 additions & 4 deletions go/vt/sqlparser/analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,10 @@ func TestAndExpressions(t *testing.T) {
equalExpr,
equalExpr,
},
expectedOutput: &AndExpr{
Left: greaterThanExpr,
Right: equalExpr,
},
expectedOutput: &AndExpr{Predicates: Exprs{
greaterThanExpr,
equalExpr,
}},
},
{
name: "two equal inputs",
Expand Down
9 changes: 6 additions & 3 deletions go/vt/sqlparser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,11 @@ type (

// IndexType is the type of index in a DDL statement
IndexType int8

WhereAble interface {
AddWhere(e Expr)
GetWherePredicate() Expr
}
)

var _ OrderAndLimit = (*Select)(nil)
Expand Down Expand Up @@ -2263,9 +2268,7 @@ type (
}

// AndExpr represents an AND expression.
AndExpr struct {
Left, Right Expr
}
AndExpr struct{ Predicates Exprs }

// OrExpr represents an OR expression.
OrExpr struct {
Expand Down
3 changes: 1 addition & 2 deletions go/vt/sqlparser/ast_clone.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions go/vt/sqlparser/ast_copy_on_rewrite.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions go/vt/sqlparser/ast_equals.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion go/vt/sqlparser/ast_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -1297,7 +1297,13 @@ func (node Exprs) Format(buf *TrackedBuffer) {

// Format formats the node.
func (node *AndExpr) Format(buf *TrackedBuffer) {
buf.astPrintf(node, "%l and %r", node.Left, node.Right)
for idx, expr := range node.Predicates {
if idx == len(node.Predicates)-1 {
buf.astPrintf(node, "%r", expr)
continue
}
buf.astPrintf(node, "%l and ", expr)
}
}

// Format formats the node.
Expand Down
11 changes: 8 additions & 3 deletions go/vt/sqlparser/ast_format_fast.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

135 changes: 115 additions & 20 deletions go/vt/sqlparser/ast_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"io"
"slices"
"strconv"
"strings"

Expand Down Expand Up @@ -66,6 +67,27 @@ func Append(buf *strings.Builder, node SQLNode) {
node.FormatFast(tbuf)
}

func CreateAndExpr(exprL, exprR Expr) *AndExpr {
leftAnd, isLeftAnd := exprL.(*AndExpr)
rightAnd, isRightAnd := exprR.(*AndExpr)
if isLeftAnd && isRightAnd {
return &AndExpr{
Predicates: append(leftAnd.Predicates, rightAnd.Predicates...),
}
}
if isLeftAnd {
leftAnd.Predicates = append(leftAnd.Predicates, exprR)
return leftAnd
}
if isRightAnd {
rightAnd.Predicates = append([]Expr{exprL}, rightAnd.Predicates...)
return rightAnd
}
return &AndExpr{
Predicates: Exprs{exprL, exprR},
}
}

// IndexColumn describes a column or expression in an index definition with optional length (for column)
type IndexColumn struct {
// Only one of Column or Expression can be specified
Expand Down Expand Up @@ -1239,10 +1261,8 @@ func addPredicate(where *Where, pred Expr) *Where {
Expr: pred,
}
}
where.Expr = &AndExpr{
Left: where.Expr,
Right: pred,
}

where.Expr = CreateAndExpr(where.Expr, pred)
return where
}

Expand Down Expand Up @@ -2336,8 +2356,7 @@ func SplitAndExpression(filters []Expr, node Expr) []Expr {
}
switch node := node.(type) {
case *AndExpr:
filters = SplitAndExpression(filters, node.Left)
return SplitAndExpression(filters, node.Right)
return append(filters, node.Predicates...)
}
return append(filters, node)
}
Expand All @@ -2350,26 +2369,33 @@ func AndExpressions(exprs ...Expr) Expr {
case 1:
return exprs[0]
default:
result := (Expr)(nil)
outer:
var unique Exprs
// we'll loop and remove any duplicates
for i, expr := range exprs {
if expr == nil {
continue
}
if result == nil {
result = expr
continue outer
uniqueAdd := func(e Expr) {
for _, existing := range unique {
if Equals.Expr(e, existing) {
return
}
}
unique = append(unique, e)
}

for j := 0; j < i; j++ {
if Equals.Expr(expr, exprs[j]) {
continue outer
for _, expr := range exprs {
switch expr := expr.(type) {
case *AndExpr:
for _, p := range expr.Predicates {
uniqueAdd(p)
}
case nil:
continue
default:
uniqueAdd(expr)
}
result = &AndExpr{Left: result, Right: expr}
}
return result
if len(unique) == 1 {
return unique[0]
}
return &AndExpr{Predicates: unique}
}
}

Expand Down Expand Up @@ -2839,3 +2865,72 @@ func ExtractAllTables(stmt Statement) []string {
}, stmt)
return tables
}

// ExtractINFromOR rewrites the OR expression into an IN clause.
// Each side of each ORs has to be an equality comparison expression and the column names have to
// match for all sides of each comparison.
// This rewriter takes a query that looks like this WHERE a = 1 and b = 11 or a = 2 and b = 12 or a = 3 and b = 13
// And rewrite that to WHERE (a, b) IN ((1,11), (2,12), (3,13))
func ExtractINFromOR(expr *OrExpr) []Expr {
var varNames []*ColName
var values []Exprs
orSlice := orToSlice(expr)
for _, expr := range orSlice {
andSlice := andToSlice(expr)
if len(andSlice) == 0 {
return nil
}

var currentVarNames []*ColName
var currentValues []Expr
for _, comparisonExpr := range andSlice {
if comparisonExpr.Operator != EqualOp {
return nil
}

var colName *ColName
if left, ok := comparisonExpr.Left.(*ColName); ok {
colName = left
currentValues = append(currentValues, comparisonExpr.Right)
}

if right, ok := comparisonExpr.Right.(*ColName); ok {
if colName != nil {
return nil
}
colName = right
currentValues = append(currentValues, comparisonExpr.Left)
}

if colName == nil {
return nil
}

currentVarNames = append(currentVarNames, colName)
}

if len(varNames) == 0 {
varNames = currentVarNames
} else if !slices.EqualFunc(varNames, currentVarNames, func(col1, col2 *ColName) bool { return col1.Equal(col2) }) {
return nil
}

values = append(values, currentValues)
}

var nameTuple ValTuple
for _, name := range varNames {
nameTuple = append(nameTuple, name)
}

var valueTuple ValTuple
for _, value := range values {
valueTuple = append(valueTuple, ValTuple(value))
}

return []Expr{&ComparisonExpr{
Operator: InOp,
Left: nameTuple,
Right: valueTuple,
}}
}
9 changes: 2 additions & 7 deletions go/vt/sqlparser/ast_rewrite.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions go/vt/sqlparser/ast_visit.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 9 additions & 8 deletions go/vt/sqlparser/cached_size.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions go/vt/sqlparser/precedence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,24 @@ package sqlparser

import (
"fmt"
"strings"
"testing"
"time"

"github.com/stretchr/testify/require"

"vitess.io/vitess/go/slice"
)

func readable(node Expr) string {
switch node := node.(type) {
case *OrExpr:
return fmt.Sprintf("(%s or %s)", readable(node.Left), readable(node.Right))
case *AndExpr:
return fmt.Sprintf("(%s and %s)", readable(node.Left), readable(node.Right))
predicates := slice.Map(node.Predicates, func(from Expr) string {
return readable(from)
})
return fmt.Sprintf("(%s)", strings.Join(predicates, " and "))
case *XorExpr:
return fmt.Sprintf("(%s xor %s)", readable(node.Left), readable(node.Right))
case *BinaryExpr:
Expand Down Expand Up @@ -153,7 +159,7 @@ func TestParens(t *testing.T) {
{in: "((((((1000))))))", expected: "1000"},
{in: "100 - (50 + 10)", expected: "100 - (50 + 10)"},
{in: "100 - 50 + 10", expected: "100 - 50 + 10"},
{in: "true and (true and true)", expected: "true and (true and true)"},
{in: "true and (true and true)", expected: "true and true and true"},
{in: "10 - 2 - 1", expected: "10 - 2 - 1"},
{in: "(10 - 2) - 1", expected: "10 - 2 - 1"},
{in: "10 - (2 - 1)", expected: "10 - (2 - 1)"},
Expand Down Expand Up @@ -193,6 +199,6 @@ func TestRandom(t *testing.T) {

// Then the unparsing should be the same as the input query
outputOfParseResult := String(parsedInput)
require.Equal(t, outputOfParseResult, inputQ)
require.Equal(t, inputQ, outputOfParseResult)
}
}
Loading
Loading