github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/expr_test.go (about)

     1  // Copyright 2015 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package tree_test
    12  
    13  import (
    14  	"context"
    15  	"reflect"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/parser"
    20  	_ "github.com/cockroachdb/cockroach/pkg/sql/sem/builtins"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    23  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    24  	"github.com/stretchr/testify/require"
    25  )
    26  
    27  // TestUnresolvedNameString tests the string representation of tree.UnresolvedName and thus tree.Name.
    28  func TestUnresolvedNameString(t *testing.T) {
    29  	defer leaktest.AfterTest(t)()
    30  	testCases := []struct {
    31  		in, out string
    32  	}{
    33  		{"*", `"*"`},
    34  		// Non-reserved keyword.
    35  		{"DATABASE", `"DATABASE"`},
    36  		{"dAtAbAse", `"dAtAbAse"`},
    37  		// Reserved keyword.
    38  		{"SELECT", `"SELECT"`},
    39  		{"sElEcT", `"sElEcT"`},
    40  		// Ident format: starts with [a-zA-Z_] or extended ascii,
    41  		// and is then followed by [a-zA-Z0-9$_] or extended ascii.
    42  		{"foo$09", "foo$09"},
    43  		{"_Ab10", `"_Ab10"`},
    44  		// Everything else quotes the string and escapes double quotes.
    45  		{".foobar", `".foobar"`},
    46  		{`".foobar"`, `""".foobar"""`},
    47  		{`\".foobar\"`, `"\"".foobar\"""`},
    48  	}
    49  
    50  	for _, tc := range testCases {
    51  		q := tree.UnresolvedName{NumParts: 1, Parts: tree.NameParts{tc.in}}
    52  		if q.String() != tc.out {
    53  			t.Errorf("expected q.String() == %q, got %q", tc.out, q.String())
    54  		}
    55  	}
    56  }
    57  
    58  // TestCastFromNull checks every type can be cast from NULL.
    59  func TestCastFromNull(t *testing.T) {
    60  	defer leaktest.AfterTest(t)()
    61  	for _, typ := range types.Scalar {
    62  		castExpr := tree.CastExpr{Expr: tree.DNull, Type: typ}
    63  		res, err := castExpr.Eval(nil)
    64  		require.NoError(t, err)
    65  		require.Equal(t, tree.DNull, res)
    66  	}
    67  }
    68  
    69  // TestExprString verifies that converting an expression to a string and back
    70  // doesn't change the (normalized) expression.
    71  func TestExprString(t *testing.T) {
    72  	defer leaktest.AfterTest(t)()
    73  	defer tree.MockNameTypes(map[string]*types.T{
    74  		"a": types.Bool,
    75  		"b": types.Bool,
    76  		"c": types.Bool,
    77  		"d": types.Bool,
    78  		"e": types.Bool,
    79  		"f": types.Int,
    80  		"g": types.Int,
    81  		"h": types.Int,
    82  		"i": types.Int,
    83  		"j": types.Int,
    84  		"k": types.Int,
    85  	})()
    86  	testExprs := []string{
    87  		`a AND b`,
    88  		`a AND b OR c`,
    89  		`(a AND b) OR c`,
    90  		`a AND (b OR c)`,
    91  		`a AND NOT ((b OR c) AND (d AND e))`,
    92  		`~-f`,
    93  		`-2*(f+3)*g`,
    94  		`f&g<<(g+h)&i > 0 AND (g&i)+h>>(i&f) > 0`,
    95  		`f&(g<<g+h)&i > 0 AND g&(i+h>>i)&f > 0`,
    96  		`f = g|h`,
    97  		`f != g|h`,
    98  		`NOT a AND b`,
    99  		`NOT (a AND b)`,
   100  		`(NOT a) AND b`,
   101  		`NOT (a = NOT b = c)`,
   102  		`NOT NOT a = b`,
   103  		`NOT NOT (a = b)`,
   104  		`NOT (NOT a) < b`,
   105  		`NOT (NOT a = b)`,
   106  		`(NOT NOT a) >= b`,
   107  		`(a OR (g BETWEEN (h+i) AND (j+k))) AND b`,
   108  		`(1 >= 2) IS OF (BOOL)`,
   109  		`(1 >= 2) = (2 IS OF (BOOL))`,
   110  		`count(1) FILTER (WHERE true)`,
   111  	}
   112  	ctx := context.Background()
   113  	for _, exprStr := range testExprs {
   114  		expr, err := parser.ParseExpr(exprStr)
   115  		if err != nil {
   116  			t.Fatalf("%s: %v", exprStr, err)
   117  		}
   118  		typedExpr, err := tree.TypeCheck(ctx, expr, nil, types.Any)
   119  		if err != nil {
   120  			t.Fatalf("%s: %v", expr, err)
   121  		}
   122  		// str may differ than exprStr (we may be adding some parens).
   123  		str := typedExpr.String()
   124  		expr2, err := parser.ParseExpr(str)
   125  		if err != nil {
   126  			t.Fatalf("%s: %v", exprStr, err)
   127  		}
   128  		typedExpr2, err := tree.TypeCheck(ctx, expr2, nil, types.Any)
   129  		if err != nil {
   130  			t.Fatalf("%s: %v", expr2, err)
   131  		}
   132  		// Verify that when we stringify the expression again, the string is the
   133  		// same. This is important because we don't want cycles of parsing and
   134  		// printing an expression to keep adding parens.
   135  		if str2 := typedExpr2.String(); str != str2 {
   136  			t.Errorf("Print/parse/print cycle changes the string: `%s` vs `%s`", str, str2)
   137  		}
   138  		// Compare the normalized expressions.
   139  		ctx := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings())
   140  		defer ctx.Mon.Stop(context.Background())
   141  		normalized, err := ctx.NormalizeExpr(typedExpr)
   142  		if err != nil {
   143  			t.Fatalf("%s: %v", exprStr, err)
   144  		}
   145  		normalized2, err := ctx.NormalizeExpr(typedExpr2)
   146  		if err != nil {
   147  			t.Fatalf("%s: %v", exprStr, err)
   148  		}
   149  		if !reflect.DeepEqual(tree.StripMemoizedFuncs(normalized), tree.StripMemoizedFuncs(normalized2)) {
   150  			t.Errorf("normalized expressions differ\n"+
   151  				"original:     %s\n"+
   152  				"intermediate: %s\n"+
   153  				"before: %#v\n"+
   154  				"after:  %#v", exprStr, str, normalized, normalized2)
   155  		}
   156  	}
   157  }
   158  
   159  func TestStripParens(t *testing.T) {
   160  	defer leaktest.AfterTest(t)()
   161  	testExprs := []struct {
   162  		in, out string
   163  	}{
   164  		{`1`, `1`},
   165  		{`(1)`, `1`},
   166  		{`((1))`, `1`},
   167  		{`(1) + (2)`, `(1) + (2)`},
   168  		{`((1) + (2))`, `(1) + (2)`},
   169  	}
   170  	for i, test := range testExprs {
   171  		expr, err := parser.ParseExpr(test.in)
   172  		if err != nil {
   173  			t.Fatalf("%d: %v", i, err)
   174  		}
   175  		stripped := tree.StripParens(expr)
   176  		if str := stripped.String(); str != test.out {
   177  			t.Fatalf("%d: expected tree.StripParens(%s) = %s, but found %s", i, test.in, test.out, str)
   178  		}
   179  	}
   180  }