vitess.io/vitess@v0.16.2/go/vt/sqlparser/precedence_test.go (about)

     1  /*
     2  Copyright 2019 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  import (
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  func readable(node Expr) string {
    29  	switch node := node.(type) {
    30  	case *OrExpr:
    31  		return fmt.Sprintf("(%s or %s)", readable(node.Left), readable(node.Right))
    32  	case *AndExpr:
    33  		return fmt.Sprintf("(%s and %s)", readable(node.Left), readable(node.Right))
    34  	case *XorExpr:
    35  		return fmt.Sprintf("(%s xor %s)", readable(node.Left), readable(node.Right))
    36  	case *BinaryExpr:
    37  		return fmt.Sprintf("(%s %s %s)", readable(node.Left), node.Operator.ToString(), readable(node.Right))
    38  	case *IsExpr:
    39  		return fmt.Sprintf("(%s %s)", readable(node.Left), node.Right.ToString())
    40  	default:
    41  		return String(node)
    42  	}
    43  }
    44  
    45  func TestAndOrPrecedence(t *testing.T) {
    46  	validSQL := []struct {
    47  		input  string
    48  		output string
    49  	}{{
    50  		input:  "select * from a where a=b and c=d or e=f",
    51  		output: "((a = b and c = d) or e = f)",
    52  	}, {
    53  		input:  "select * from a where a=b or c=d and e=f",
    54  		output: "(a = b or (c = d and e = f))",
    55  	}}
    56  	for _, tcase := range validSQL {
    57  		tree, err := Parse(tcase.input)
    58  		if err != nil {
    59  			t.Error(err)
    60  			continue
    61  		}
    62  		expr := readable(tree.(*Select).Where.Expr)
    63  		if expr != tcase.output {
    64  			t.Errorf("Parse: \n%s, want: \n%s", expr, tcase.output)
    65  		}
    66  	}
    67  }
    68  
    69  func TestNotInSubqueryPrecedence(t *testing.T) {
    70  	tree, err := Parse("select * from a where not id in (select 42)")
    71  	require.NoError(t, err)
    72  	not := tree.(*Select).Where.Expr.(*NotExpr)
    73  	cmp := not.Expr.(*ComparisonExpr)
    74  	subq := cmp.Right.(*Subquery)
    75  
    76  	extracted := &ExtractedSubquery{
    77  		Original:  cmp,
    78  		OpCode:    1,
    79  		Subquery:  subq,
    80  		OtherSide: cmp.Left,
    81  	}
    82  	extracted.SetArgName("arg1")
    83  	extracted.SetHasValuesArg("has_values1")
    84  
    85  	not.Expr = extracted
    86  	output := readable(not)
    87  	assert.Equal(t, "not (:has_values1 = 1 and id in ::arg1)", output)
    88  }
    89  
    90  func TestSubqueryPrecedence(t *testing.T) {
    91  	tree, err := Parse("select * from a where id in (select 42) and false")
    92  	require.NoError(t, err)
    93  	where := tree.(*Select).Where
    94  	andExpr := where.Expr.(*AndExpr)
    95  	cmp := andExpr.Left.(*ComparisonExpr)
    96  	subq := cmp.Right.(*Subquery)
    97  
    98  	extracted := &ExtractedSubquery{
    99  		Original:  andExpr.Left,
   100  		OpCode:    1,
   101  		Subquery:  subq,
   102  		OtherSide: cmp.Left,
   103  	}
   104  	extracted.SetArgName("arg1")
   105  	extracted.SetHasValuesArg("has_values1")
   106  
   107  	andExpr.Left = extracted
   108  	output := readable(extracted)
   109  	assert.Equal(t, ":has_values1 = 1 and id in ::arg1", output)
   110  }
   111  
   112  func TestPlusStarPrecedence(t *testing.T) {
   113  	validSQL := []struct {
   114  		input  string
   115  		output string
   116  	}{{
   117  		input:  "select 1+2*3 from a",
   118  		output: "(1 + (2 * 3))",
   119  	}, {
   120  		input:  "select 1*2+3 from a",
   121  		output: "((1 * 2) + 3)",
   122  	}}
   123  	for _, tcase := range validSQL {
   124  		tree, err := Parse(tcase.input)
   125  		if err != nil {
   126  			t.Error(err)
   127  			continue
   128  		}
   129  		expr := readable(tree.(*Select).SelectExprs[0].(*AliasedExpr).Expr)
   130  		if expr != tcase.output {
   131  			t.Errorf("Parse: \n%s, want: \n%s", expr, tcase.output)
   132  		}
   133  	}
   134  }
   135  
   136  func TestIsPrecedence(t *testing.T) {
   137  	validSQL := []struct {
   138  		input  string
   139  		output string
   140  	}{{
   141  		input:  "select * from a where a+b is true",
   142  		output: "((a + b) is true)",
   143  	}, {
   144  		input:  "select * from a where a=1 and b=2 is true",
   145  		output: "(a = 1 and (b = 2 is true))",
   146  	}, {
   147  		input:  "select * from a where (a=1 and b=2) is true",
   148  		output: "((a = 1 and b = 2) is true)",
   149  	}}
   150  	for _, tcase := range validSQL {
   151  		tree, err := Parse(tcase.input)
   152  		if err != nil {
   153  			t.Error(err)
   154  			continue
   155  		}
   156  		expr := readable(tree.(*Select).Where.Expr)
   157  		if expr != tcase.output {
   158  			t.Errorf("Parse: \n%s, want: \n%s", expr, tcase.output)
   159  		}
   160  	}
   161  }
   162  
   163  func TestParens(t *testing.T) {
   164  	tests := []struct {
   165  		in, expected string
   166  	}{
   167  		{in: "12", expected: "12"},
   168  		{in: "(12)", expected: "12"},
   169  		{in: "((12))", expected: "12"},
   170  		{in: "((true) and (false))", expected: "true and false"},
   171  		{in: "((true) and (false)) and (true)", expected: "true and false and true"},
   172  		{in: "((true) and (false))", expected: "true and false"},
   173  		{in: "a=b and (c=d or e=f)", expected: "a = b and (c = d or e = f)"},
   174  		{in: "(a=b and c=d) or e=f", expected: "a = b and c = d or e = f"},
   175  		{in: "a & (b | c)", expected: "a & (b | c)"},
   176  		{in: "(a & b) | c", expected: "a & b | c"},
   177  		{in: "not (a=b and c=d)", expected: "not (a = b and c = d)"},
   178  		{in: "not (a=b) and c=d", expected: "not a = b and c = d"},
   179  		{in: "(not (a=b)) and c=d", expected: "not a = b and c = d"},
   180  		{in: "-(12)", expected: "-12"},
   181  		{in: "-(12 + 12)", expected: "-(12 + 12)"},
   182  		{in: "(1 > 2) and (1 = b)", expected: "1 > 2 and 1 = b"},
   183  		{in: "(a / b) + c", expected: "a / b + c"},
   184  		{in: "a / (b + c)", expected: "a / (b + c)"},
   185  		{in: "(1,2,3)", expected: "(1, 2, 3)"},
   186  		{in: "(a) between (5) and (7)", expected: "a between 5 and 7"},
   187  		{in: "(a | b) between (5) and (7)", expected: "a | b between 5 and 7"},
   188  		{in: "(a and b) between (5) and (7)", expected: "(a and b) between 5 and 7"},
   189  		{in: "(true is true) is null", expected: "(true is true) is null"},
   190  		{in: "3 * (100 div 3)", expected: "3 * (100 div 3)"},
   191  		{in: "100 div 2 div 2", expected: "100 div 2 div 2"},
   192  		{in: "100 div (2 div 2)", expected: "100 div (2 div 2)"},
   193  		{in: "(100 div 2) div 2", expected: "100 div 2 div 2"},
   194  		{in: "((((((1000))))))", expected: "1000"},
   195  		{in: "100 - (50 + 10)", expected: "100 - (50 + 10)"},
   196  		{in: "100 - 50 + 10", expected: "100 - 50 + 10"},
   197  		{in: "true and (true and true)", expected: "true and (true and true)"},
   198  		{in: "10 - 2 - 1", expected: "10 - 2 - 1"},
   199  		{in: "(10 - 2) - 1", expected: "10 - 2 - 1"},
   200  		{in: "10 - (2 - 1)", expected: "10 - (2 - 1)"},
   201  		{in: "0 <=> (1 and 0)", expected: "0 <=> (1 and 0)"},
   202  	}
   203  
   204  	for _, tc := range tests {
   205  		t.Run(tc.in, func(t *testing.T) {
   206  			stmt, err := Parse("select " + tc.in)
   207  			require.NoError(t, err)
   208  			out := String(stmt)
   209  			require.Equal(t, "select "+tc.expected+" from dual", out)
   210  		})
   211  	}
   212  }
   213  
   214  func TestRandom(t *testing.T) {
   215  	// The purpose of this test is to find discrepancies between Format and parsing. If for example our precedence rules are not consistent between the two, this test should find it.
   216  	// The idea is to generate random queries, and pass them through the parser and then the unparser, and one more time. The result of the first unparse should be the same as the second result.
   217  	seed := time.Now().UnixNano()
   218  	fmt.Println(fmt.Sprintf("seed is %d", seed)) // nolint
   219  	g := newGenerator(seed, 5)
   220  	endBy := time.Now().Add(1 * time.Second)
   221  
   222  	for {
   223  		if time.Now().After(endBy) {
   224  			break
   225  		}
   226  		// Given a random expression
   227  		randomExpr := g.expression()
   228  		inputQ := "select " + String(randomExpr) + " from t"
   229  
   230  		// When it's parsed and unparsed
   231  		parsedInput, err := Parse(inputQ)
   232  		require.NoError(t, err, inputQ)
   233  
   234  		// Then the unparsing should be the same as the input query
   235  		outputOfParseResult := String(parsedInput)
   236  		require.Equal(t, outputOfParseResult, inputQ)
   237  	}
   238  }