github.com/vedadiyan/sqlparser@v1.0.0/pkg/sqlparser/predicate_rewriting.go (about)

     1  /*
     2  Copyright 2022 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package sqlparser
    18  
    19  const (
    20  	Changed  RewriteState = true
    21  	NoChange RewriteState = false
    22  )
    23  
    24  type RewriteState bool
    25  
    26  // RewritePredicate walks the input AST and rewrites any boolean logic into a simpler form
    27  // This simpler form is CNF plus logic for extracting predicates from OR, plus logic for turning ORs into IN
    28  // Note: In order to re-plan, we need to empty the accumulated metadata in the AST,
    29  // so ColName.Metadata will be nil:ed out as part of this rewrite
    30  func RewritePredicate(ast SQLNode) SQLNode {
    31  	for {
    32  		finishedRewrite := true
    33  		ast = SafeRewrite(ast, nil, func(cursor *Cursor) bool {
    34  			if e, isExpr := cursor.node.(Expr); isExpr {
    35  				rewritten, state := simplifyExpression(e)
    36  				if state == Changed {
    37  					finishedRewrite = false
    38  					cursor.Replace(rewritten)
    39  				}
    40  			}
    41  			if col, isCol := cursor.node.(*ColName); isCol {
    42  				col.Metadata = nil
    43  			}
    44  			return true
    45  		})
    46  
    47  		if finishedRewrite {
    48  			return ast
    49  		}
    50  	}
    51  }
    52  
    53  func simplifyExpression(expr Expr) (Expr, RewriteState) {
    54  	switch expr := expr.(type) {
    55  	case *NotExpr:
    56  		return simplifyNot(expr)
    57  	case *OrExpr:
    58  		return simplifyOr(expr)
    59  	case *XorExpr:
    60  		return simplifyXor(expr)
    61  	case *AndExpr:
    62  		return simplifyAnd(expr)
    63  	}
    64  	return expr, NoChange
    65  }
    66  
    67  func simplifyNot(expr *NotExpr) (Expr, RewriteState) {
    68  	switch child := expr.Expr.(type) {
    69  	case *NotExpr:
    70  		// NOT NOT A => A
    71  		return child.Expr, Changed
    72  	case *OrExpr:
    73  		// DeMorgan Rewriter
    74  		// NOT (A OR B) => NOT A AND NOT B
    75  		return &AndExpr{Right: &NotExpr{Expr: child.Right}, Left: &NotExpr{Expr: child.Left}}, Changed
    76  	case *AndExpr:
    77  		// DeMorgan Rewriter
    78  		// NOT (A AND B) => NOT A OR NOT B
    79  		return &OrExpr{Right: &NotExpr{Expr: child.Right}, Left: &NotExpr{Expr: child.Left}}, Changed
    80  	}
    81  	return expr, NoChange
    82  }
    83  
    84  // ExtractINFromOR will add additional predicated to an OR.
    85  // this rewriter should not be used in a fixed point way, since it returns the original expression with additions,
    86  // and it will therefor OOM before it stops rewriting
    87  func ExtractINFromOR(expr *OrExpr) []Expr {
    88  	// we check if we have two comparisons on either side of the OR
    89  	// that we can add as an ANDed comparison.
    90  	// WHERE (a = 5 and B) or (a = 6 AND C) =>
    91  	// WHERE (a = 5 AND B) OR (a = 6 AND C) AND a IN (5,6)
    92  	// This rewrite makes it possible to find a better route than Scatter if the `a` column has a helpful vindex
    93  	lftPredicates := SplitAndExpression(nil, expr.Left)
    94  	rgtPredicates := SplitAndExpression(nil, expr.Right)
    95  	var ins []Expr
    96  	for _, lft := range lftPredicates {
    97  		l, ok := lft.(*ComparisonExpr)
    98  		if !ok {
    99  			continue
   100  		}
   101  		for _, rgt := range rgtPredicates {
   102  			r, ok := rgt.(*ComparisonExpr)
   103  			if !ok {
   104  				continue
   105  			}
   106  			in, state := tryTurningOrIntoIn(l, r)
   107  			if state == Changed {
   108  				ins = append(ins, in)
   109  			}
   110  		}
   111  	}
   112  
   113  	return ins
   114  }
   115  
   116  func simplifyOr(expr *OrExpr) (Expr, RewriteState) {
   117  	or := expr
   118  
   119  	// first we search for ANDs and see how they can be simplified
   120  	land, lok := or.Left.(*AndExpr)
   121  	rand, rok := or.Right.(*AndExpr)
   122  	switch {
   123  	case lok && rok:
   124  		var a, b, c Expr
   125  		switch {
   126  		// (A and B) or (A and C) => A AND (B OR C)
   127  		case Equals.Expr(land.Left, rand.Left):
   128  			a, b, c = land.Left, land.Right, rand.Right
   129  		// (A and B) or (C and A) => A AND (B OR C)
   130  		case Equals.Expr(land.Left, rand.Right):
   131  			a, b, c = land.Left, land.Right, rand.Left
   132  		// (B and A) or (A and C) => A AND (B OR C)
   133  		case Equals.Expr(land.Right, rand.Left):
   134  			a, b, c = land.Right, land.Left, rand.Right
   135  		// (B and A) or (C and A) => A AND (B OR C)
   136  		case Equals.Expr(land.Right, rand.Right):
   137  			a, b, c = land.Right, land.Left, rand.Left
   138  		default:
   139  			return expr, NoChange
   140  		}
   141  		return &AndExpr{Left: a, Right: &OrExpr{Left: b, Right: c}}, Changed
   142  	case lok:
   143  		// Simplification
   144  		// (A AND B) OR A => A
   145  		if Equals.Expr(or.Right, land.Left) || Equals.Expr(or.Right, land.Right) {
   146  			return or.Right, Changed
   147  		}
   148  		// Distribution Law
   149  		// (A AND B) OR C => (A OR C) AND (B OR C)
   150  		return &AndExpr{Left: &OrExpr{Left: land.Left, Right: or.Right}, Right: &OrExpr{Left: land.Right, Right: or.Right}}, Changed
   151  	case rok:
   152  		// Simplification
   153  		// A OR (A AND B) => A
   154  		if Equals.Expr(or.Left, rand.Left) || Equals.Expr(or.Left, rand.Right) {
   155  			return or.Left, Changed
   156  		}
   157  		// Distribution Law
   158  		// C OR (A AND B) => (C OR A) AND (C OR B)
   159  		return &AndExpr{Left: &OrExpr{Left: or.Left, Right: rand.Left}, Right: &OrExpr{Left: or.Left, Right: rand.Right}}, Changed
   160  	}
   161  
   162  	// next, we want to try to turn multiple ORs into an IN when possible
   163  	lftCmp, lok := or.Left.(*ComparisonExpr)
   164  	rgtCmp, rok := or.Right.(*ComparisonExpr)
   165  	if lok && rok {
   166  		newExpr, rewritten := tryTurningOrIntoIn(lftCmp, rgtCmp)
   167  		if rewritten {
   168  			return newExpr, Changed
   169  		}
   170  	}
   171  
   172  	// Try to make distinct
   173  	return distinctOr(expr)
   174  }
   175  
   176  func tryTurningOrIntoIn(l, r *ComparisonExpr) (Expr, RewriteState) {
   177  	// looks for A = X OR A = Y and turns them into A IN (X, Y)
   178  	col, ok := l.Left.(*ColName)
   179  	if !ok || !Equals.Expr(col, r.Left) {
   180  		return nil, NoChange
   181  	}
   182  
   183  	var tuple ValTuple
   184  
   185  	switch l.Operator {
   186  	case EqualOp:
   187  		tuple = ValTuple{l.Right}
   188  	case InOp:
   189  		lft, ok := l.Right.(ValTuple)
   190  		if !ok {
   191  			return nil, NoChange
   192  		}
   193  		tuple = lft
   194  	default:
   195  		return nil, NoChange
   196  	}
   197  
   198  	switch r.Operator {
   199  	case EqualOp:
   200  		tuple = append(tuple, r.Right)
   201  	case InOp:
   202  		lft, ok := r.Right.(ValTuple)
   203  		if !ok {
   204  			return nil, NoChange
   205  		}
   206  		tuple = append(tuple, lft...)
   207  	default:
   208  		return nil, NoChange
   209  	}
   210  
   211  	return &ComparisonExpr{
   212  		Operator: InOp,
   213  		Left:     col,
   214  		Right:    uniquefy(tuple),
   215  	}, Changed
   216  }
   217  
   218  func uniquefy(tuple ValTuple) (output ValTuple) {
   219  outer:
   220  	for _, expr := range tuple {
   221  		for _, seen := range output {
   222  			if Equals.Expr(expr, seen) {
   223  				continue outer
   224  			}
   225  		}
   226  		output = append(output, expr)
   227  	}
   228  	return
   229  }
   230  
   231  func simplifyXor(expr *XorExpr) (Expr, RewriteState) {
   232  	// DeMorgan Rewriter
   233  	// (A XOR B) => (A OR B) AND NOT (A AND B)
   234  	return &AndExpr{Left: &OrExpr{Left: expr.Left, Right: expr.Right}, Right: &NotExpr{Expr: &AndExpr{Left: expr.Left, Right: expr.Right}}}, Changed
   235  }
   236  
   237  func simplifyAnd(expr *AndExpr) (Expr, RewriteState) {
   238  	res, rewritten := distinctAnd(expr)
   239  	if rewritten {
   240  		return res, rewritten
   241  	}
   242  	and := expr
   243  	if or, ok := and.Left.(*OrExpr); ok {
   244  		// Simplification
   245  		// (A OR B) AND A => A
   246  		if Equals.Expr(or.Left, and.Right) || Equals.Expr(or.Right, and.Right) {
   247  			return and.Right, Changed
   248  		}
   249  	}
   250  	if or, ok := and.Right.(*OrExpr); ok {
   251  		// Simplification
   252  		// A OR (A AND B) => A
   253  		if Equals.Expr(or.Left, and.Left) || Equals.Expr(or.Right, and.Left) {
   254  			return or.Left, Changed
   255  		}
   256  	}
   257  
   258  	return expr, NoChange
   259  }
   260  
   261  func distinctOr(in *OrExpr) (Expr, RewriteState) {
   262  	todo := []*OrExpr{in}
   263  	var leaves []Expr
   264  	for len(todo) > 0 {
   265  		curr := todo[0]
   266  		todo = todo[1:]
   267  		addAnd := func(in Expr) {
   268  			and, ok := in.(*OrExpr)
   269  			if ok {
   270  				todo = append(todo, and)
   271  			} else {
   272  				leaves = append(leaves, in)
   273  			}
   274  		}
   275  		addAnd(curr.Left)
   276  		addAnd(curr.Right)
   277  	}
   278  	original := len(leaves)
   279  	var predicates []Expr
   280  
   281  outer1:
   282  	for len(leaves) > 0 {
   283  		curr := leaves[0]
   284  		leaves = leaves[1:]
   285  		for _, alreadyIn := range predicates {
   286  			if Equals.Expr(alreadyIn, curr) {
   287  				continue outer1
   288  			}
   289  		}
   290  		predicates = append(predicates, curr)
   291  	}
   292  	if original == len(predicates) {
   293  		return in, NoChange
   294  	}
   295  	var result Expr
   296  	for i, curr := range predicates {
   297  		if i == 0 {
   298  			result = curr
   299  			continue
   300  		}
   301  		result = &OrExpr{Left: result, Right: curr}
   302  	}
   303  	return result, Changed
   304  }
   305  
   306  func distinctAnd(in *AndExpr) (Expr, RewriteState) {
   307  	todo := []*AndExpr{in}
   308  	var leaves []Expr
   309  	for len(todo) > 0 {
   310  		curr := todo[0]
   311  		todo = todo[1:]
   312  		addAnd := func(in Expr) {
   313  			and, ok := in.(*AndExpr)
   314  			if ok {
   315  				todo = append(todo, and)
   316  			} else {
   317  				leaves = append(leaves, in)
   318  			}
   319  		}
   320  		addAnd(curr.Left)
   321  		addAnd(curr.Right)
   322  	}
   323  	original := len(leaves)
   324  	var predicates []Expr
   325  
   326  outer1:
   327  	for len(leaves) > 0 {
   328  		curr := leaves[0]
   329  		leaves = leaves[1:]
   330  		for _, alreadyIn := range predicates {
   331  			if Equals.Expr(alreadyIn, curr) {
   332  				continue outer1
   333  			}
   334  		}
   335  		predicates = append(predicates, curr)
   336  	}
   337  	if original == len(predicates) {
   338  		return in, NoChange
   339  	}
   340  	var result Expr
   341  	for i, curr := range predicates {
   342  		if i == 0 {
   343  			result = curr
   344  			continue
   345  		}
   346  		result = &AndExpr{Left: result, Right: curr}
   347  	}
   348  	return result, Changed
   349  }