github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/constant_test.go (about) 1 // Copyright 2016 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 "go/constant" 16 "go/token" 17 "reflect" 18 "strconv" 19 "strings" 20 "testing" 21 "time" 22 23 "github.com/cockroachdb/apd" 24 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 25 "github.com/cockroachdb/cockroach/pkg/sql/parser" 26 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 27 "github.com/cockroachdb/cockroach/pkg/sql/types" 28 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 29 ) 30 31 // TestNumericConstantVerifyAndResolveAvailableTypes verifies that test NumVals will 32 // all return expected available type sets, and that attempting to resolve the NumVals 33 // as each of these types will all succeed with an expected tree.Datum result. 34 func TestNumericConstantVerifyAndResolveAvailableTypes(t *testing.T) { 35 defer leaktest.AfterTest(t)() 36 wantInt := tree.NumValAvailInteger 37 wantDecButCanBeInt := tree.NumValAvailDecimalNoFraction 38 wantDec := tree.NumValAvailDecimalWithFraction 39 40 testCases := []struct { 41 str string 42 avail []*types.T 43 }{ 44 {"1", wantInt}, 45 {"0", wantInt}, 46 {"-1", wantInt}, 47 {"9223372036854775807", wantInt}, 48 {"1.0", wantDecButCanBeInt}, 49 {"-1234.0000", wantDecButCanBeInt}, 50 {"1e10", wantDecButCanBeInt}, 51 {"1E10", wantDecButCanBeInt}, 52 {"1.1", wantDec}, 53 {"1e-10", wantDec}, 54 {"1E-10", wantDec}, 55 {"-1231.131", wantDec}, 56 {"876543234567898765436787654321", wantDec}, 57 } 58 59 for i, test := range testCases { 60 tok := token.INT 61 if strings.ContainsAny(test.str, ".eE") { 62 tok = token.FLOAT 63 } 64 65 str := test.str 66 neg := false 67 if str[0] == '-' { 68 neg = true 69 str = str[1:] 70 } 71 72 val := constant.MakeFromLiteral(str, tok, 0) 73 if val.Kind() == constant.Unknown { 74 t.Fatalf("%d: could not parse value string %q", i, test.str) 75 } 76 77 // Check available types. 78 c := tree.NewNumVal(val, str, neg) 79 avail := c.AvailableTypes() 80 if !reflect.DeepEqual(avail, test.avail) { 81 t.Errorf("%d: expected the available type set %v for %v, found %v", 82 i, test.avail, c.ExactString(), avail) 83 } 84 85 // Make sure it can be resolved as each of those types. 86 for _, availType := range avail { 87 semaCtx := tree.MakeSemaContext() 88 if res, err := c.ResolveAsType(&semaCtx, availType); err != nil { 89 t.Errorf("%d: expected resolving %v as available type %s would succeed, found %v", 90 i, c.ExactString(), availType, err) 91 } else { 92 resErr := func(parsed, resolved interface{}) { 93 t.Errorf("%d: expected resolving %v as available type %s would produce a tree.Datum"+ 94 " with the value %v, found %v", 95 i, c, availType, parsed, resolved) 96 } 97 switch typ := res.(type) { 98 case *tree.DInt: 99 var i int64 100 var err error 101 if tok == token.INT { 102 if i, err = strconv.ParseInt(test.str, 10, 64); err != nil { 103 t.Fatal(err) 104 } 105 } else { 106 var f float64 107 if f, err = strconv.ParseFloat(test.str, 64); err != nil { 108 t.Fatal(err) 109 } 110 i = int64(f) 111 } 112 if resI := int64(*typ); i != resI { 113 resErr(i, resI) 114 } 115 case *tree.DFloat: 116 f, err := strconv.ParseFloat(test.str, 64) 117 if err != nil { 118 t.Fatal(err) 119 } 120 if resF := float64(*typ); f != resF { 121 resErr(f, resF) 122 } 123 case *tree.DDecimal: 124 d := new(apd.Decimal) 125 if !strings.ContainsAny(test.str, "eE") { 126 if _, _, err := d.SetString(test.str); err != nil { 127 t.Fatalf("could not set %q on decimal", test.str) 128 } 129 } else { 130 _, _, err = d.SetString(test.str) 131 if err != nil { 132 t.Fatal(err) 133 } 134 } 135 resD := &typ.Decimal 136 if d.Cmp(resD) != 0 { 137 resErr(d, resD) 138 } 139 } 140 } 141 } 142 } 143 } 144 145 // TestStringConstantVerifyAvailableTypes verifies that test StrVals will all 146 // return expected available type sets, and that attempting to resolve the StrVals 147 // as each of these types will either succeed or return a parse error. 148 func TestStringConstantVerifyAvailableTypes(t *testing.T) { 149 defer leaktest.AfterTest(t)() 150 wantStringButCanBeAll := tree.StrValAvailAllParsable 151 wantBytes := tree.StrValAvailBytes 152 153 testCases := []struct { 154 c *tree.StrVal 155 avail []*types.T 156 }{ 157 {tree.NewStrVal("abc 世界"), wantStringButCanBeAll}, 158 {tree.NewStrVal("t"), wantStringButCanBeAll}, 159 {tree.NewStrVal("2010-09-28"), wantStringButCanBeAll}, 160 {tree.NewStrVal("2010-09-28 12:00:00.1"), wantStringButCanBeAll}, 161 {tree.NewStrVal("PT12H2M"), wantStringButCanBeAll}, 162 {tree.NewBytesStrVal("abc 世界"), wantBytes}, 163 {tree.NewBytesStrVal("t"), wantBytes}, 164 {tree.NewBytesStrVal("2010-09-28"), wantBytes}, 165 {tree.NewBytesStrVal("2010-09-28 12:00:00.1"), wantBytes}, 166 {tree.NewBytesStrVal("PT12H2M"), wantBytes}, 167 {tree.NewBytesStrVal(string([]byte{0xff, 0xfe, 0xfd})), wantBytes}, 168 } 169 170 for i, test := range testCases { 171 // Check that the expected available types are returned. 172 avail := test.c.AvailableTypes() 173 if !reflect.DeepEqual(avail, test.avail) { 174 t.Errorf("%d: expected the available type set %v for %+v, found %v", 175 i, test.avail, test.c, avail) 176 } 177 178 // Make sure it can be resolved as each of those types or throws a parsing error. 179 for _, availType := range avail { 180 181 // The enum value in c.AvailableTypes() is AnyEnum, so we will not be able to 182 // resolve that exact type. In actual execution, the constant would be resolved 183 // as a hydrated enum type instead. 184 if availType.Family() == types.EnumFamily { 185 continue 186 } 187 188 semaCtx := tree.MakeSemaContext() 189 if _, err := test.c.ResolveAsType(&semaCtx, availType); err != nil { 190 if !strings.Contains(err.Error(), "could not parse") { 191 // Parsing errors are permitted for this test, as proper tree.StrVal parsing 192 // is tested in TestStringConstantTypeResolution. Any other error should 193 // throw a failure. 194 t.Errorf("%d: expected resolving %v as available type %s would either succeed"+ 195 " or throw a parsing error, found %v", 196 i, test.c, availType, err) 197 } 198 } 199 } 200 } 201 } 202 203 func mustParseDBool(t *testing.T, s string) tree.Datum { 204 d, err := tree.ParseDBool(s) 205 if err != nil { 206 t.Fatal(err) 207 } 208 return d 209 } 210 func mustParseDDate(t *testing.T, s string) tree.Datum { 211 d, err := tree.ParseDDate(nil, s) 212 if err != nil { 213 t.Fatal(err) 214 } 215 return d 216 } 217 func mustParseDTime(t *testing.T, s string) tree.Datum { 218 d, err := tree.ParseDTime(nil, s, time.Microsecond) 219 if err != nil { 220 t.Fatal(err) 221 } 222 return d 223 } 224 func mustParseDTimeTZ(t *testing.T, s string) tree.Datum { 225 d, err := tree.ParseDTimeTZ(nil, s, time.Microsecond) 226 if err != nil { 227 t.Fatal(err) 228 } 229 return d 230 } 231 func mustParseDTimestamp(t *testing.T, s string) tree.Datum { 232 d, err := tree.ParseDTimestamp(nil, s, time.Millisecond) 233 if err != nil { 234 t.Fatal(err) 235 } 236 return d 237 } 238 func mustParseDTimestampTZ(t *testing.T, s string) tree.Datum { 239 d, err := tree.ParseDTimestampTZ(nil, s, time.Millisecond) 240 if err != nil { 241 t.Fatal(err) 242 } 243 return d 244 } 245 func mustParseDInterval(t *testing.T, s string) tree.Datum { 246 d, err := tree.ParseDInterval(s) 247 if err != nil { 248 t.Fatal(err) 249 } 250 return d 251 } 252 func mustParseDJSON(t *testing.T, s string) tree.Datum { 253 d, err := tree.ParseDJSON(s) 254 if err != nil { 255 t.Fatal(err) 256 } 257 return d 258 } 259 func mustParseDStringArray(t *testing.T, s string) tree.Datum { 260 evalContext := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 261 d, err := tree.ParseDArrayFromString(&evalContext, s, types.String) 262 if err != nil { 263 t.Fatal(err) 264 } 265 return d 266 } 267 func mustParseDIntArray(t *testing.T, s string) tree.Datum { 268 evalContext := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 269 d, err := tree.ParseDArrayFromString(&evalContext, s, types.Int) 270 if err != nil { 271 t.Fatal(err) 272 } 273 return d 274 } 275 func mustParseDDecimalArray(t *testing.T, s string) tree.Datum { 276 evalContext := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 277 d, err := tree.ParseDArrayFromString(&evalContext, s, types.Decimal) 278 if err != nil { 279 t.Fatal(err) 280 } 281 return d 282 } 283 284 var parseFuncs = map[*types.T]func(*testing.T, string) tree.Datum{ 285 types.String: func(t *testing.T, s string) tree.Datum { return tree.NewDString(s) }, 286 types.Bytes: func(t *testing.T, s string) tree.Datum { return tree.NewDBytes(tree.DBytes(s)) }, 287 types.Bool: mustParseDBool, 288 types.Date: mustParseDDate, 289 types.Time: mustParseDTime, 290 types.TimeTZ: mustParseDTimeTZ, 291 types.Timestamp: mustParseDTimestamp, 292 types.TimestampTZ: mustParseDTimestampTZ, 293 types.Interval: mustParseDInterval, 294 types.Jsonb: mustParseDJSON, 295 types.DecimalArray: mustParseDDecimalArray, 296 types.IntArray: mustParseDIntArray, 297 types.StringArray: mustParseDStringArray, 298 } 299 300 func typeSet(tys ...*types.T) map[*types.T]struct{} { 301 set := make(map[*types.T]struct{}, len(tys)) 302 for _, t := range tys { 303 set[t] = struct{}{} 304 } 305 return set 306 } 307 308 // TestStringConstantResolveAvailableTypes verifies that test StrVals can all be 309 // resolved successfully into an expected set of tree.Datum types. The test will make sure 310 // the correct set of tree.Datum types are resolvable, and that the resolved tree.Datum match 311 // the expected results which come from running the string literal through a 312 // corresponding parseFunc (above). 313 func TestStringConstantResolveAvailableTypes(t *testing.T) { 314 defer leaktest.AfterTest(t)() 315 testCases := []struct { 316 c *tree.StrVal 317 parseOptions map[*types.T]struct{} 318 }{ 319 { 320 c: tree.NewStrVal("abc 世界"), 321 parseOptions: typeSet(types.String, types.Bytes), 322 }, 323 { 324 c: tree.NewStrVal("true"), 325 parseOptions: typeSet(types.String, types.Bytes, types.Bool, types.Jsonb), 326 }, 327 { 328 c: tree.NewStrVal("2010-09-28"), 329 parseOptions: typeSet(types.String, types.Bytes, types.Date, types.Timestamp, types.TimestampTZ), 330 }, 331 { 332 c: tree.NewStrVal("2010-09-28 12:00:00.1"), 333 parseOptions: typeSet(types.String, types.Bytes, types.Time, types.TimeTZ, types.Timestamp, types.TimestampTZ, types.Date), 334 }, 335 { 336 c: tree.NewStrVal("2006-07-08T00:00:00.000000123Z"), 337 parseOptions: typeSet(types.String, types.Bytes, types.Time, types.TimeTZ, types.Timestamp, types.TimestampTZ, types.Date), 338 }, 339 { 340 c: tree.NewStrVal("PT12H2M"), 341 parseOptions: typeSet(types.String, types.Bytes, types.Interval), 342 }, 343 { 344 c: tree.NewBytesStrVal("abc 世界"), 345 parseOptions: typeSet(types.String, types.Bytes), 346 }, 347 { 348 c: tree.NewBytesStrVal("true"), 349 parseOptions: typeSet(types.String, types.Bytes), 350 }, 351 { 352 c: tree.NewBytesStrVal("2010-09-28"), 353 parseOptions: typeSet(types.String, types.Bytes), 354 }, 355 { 356 c: tree.NewBytesStrVal("2010-09-28 12:00:00.1"), 357 parseOptions: typeSet(types.String, types.Bytes), 358 }, 359 { 360 c: tree.NewBytesStrVal("PT12H2M"), 361 parseOptions: typeSet(types.String, types.Bytes), 362 }, 363 { 364 c: tree.NewStrVal(`{"a": 1}`), 365 parseOptions: typeSet(types.String, types.Bytes, types.Jsonb), 366 }, 367 { 368 c: tree.NewStrVal(`{1,2}`), 369 parseOptions: typeSet(types.String, types.Bytes, types.StringArray, types.IntArray, types.DecimalArray), 370 }, 371 { 372 c: tree.NewStrVal(`{1.5,2.0}`), 373 parseOptions: typeSet(types.String, types.Bytes, types.StringArray, types.DecimalArray), 374 }, 375 { 376 c: tree.NewStrVal(`{a,b}`), 377 parseOptions: typeSet(types.String, types.Bytes, types.StringArray), 378 }, 379 { 380 c: tree.NewBytesStrVal(string([]byte{0xff, 0xfe, 0xfd})), 381 parseOptions: typeSet(types.String, types.Bytes), 382 }, 383 } 384 385 for i, test := range testCases { 386 parseableCount := 0 387 388 // Make sure it can be resolved as each of those types or throws a parsing error. 389 for _, availType := range test.c.AvailableTypes() { 390 391 // The enum value in c.AvailableTypes() is AnyEnum, so we will not be able to 392 // resolve that exact type. In actual execution, the constant would be resolved 393 // as a hydrated enum type instead. 394 if availType.Family() == types.EnumFamily { 395 continue 396 } 397 398 semaCtx := tree.MakeSemaContext() 399 res, err := test.c.ResolveAsType(&semaCtx, availType) 400 if err != nil { 401 if !strings.Contains(err.Error(), "could not parse") && !strings.Contains(err.Error(), "parsing") { 402 // Parsing errors are permitted for this test, but the number of correctly 403 // parseable types will be verified. Any other error should throw a failure. 404 t.Errorf("%d: expected resolving %v as available type %s would either succeed"+ 405 " or throw a parsing error, found %v", 406 i, test.c, availType, err) 407 } 408 continue 409 } 410 parseableCount++ 411 412 if _, isExpected := test.parseOptions[availType]; !isExpected { 413 t.Errorf("%d: type %s not expected to be resolvable from the tree.StrVal %v, found %v", 414 i, availType, test.c, res) 415 } else { 416 expectedDatum := parseFuncs[availType](t, test.c.RawString()) 417 evalCtx := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 418 defer evalCtx.Stop(context.Background()) 419 if res.Compare(evalCtx, expectedDatum) != 0 { 420 t.Errorf("%d: type %s expected to be resolved from the tree.StrVal %v to tree.Datum %v"+ 421 ", found %v", 422 i, availType, test.c, expectedDatum, res) 423 } 424 } 425 } 426 427 // Make sure the expected number of types can be resolved from the tree.StrVal. 428 if expCount := len(test.parseOptions); parseableCount != expCount { 429 t.Errorf("%d: expected %d successfully resolvable types for the tree.StrVal %v, found %d", 430 i, expCount, test.c, parseableCount) 431 } 432 } 433 } 434 435 type constantLiteralFoldingTestCase struct { 436 expr string 437 expected string 438 } 439 440 func testConstantLiteralFolding(t *testing.T, testData []constantLiteralFoldingTestCase) { 441 for _, d := range testData { 442 expr, err := parser.ParseExpr(d.expr) 443 if err != nil { 444 t.Fatalf("%s: %v", d.expr, err) 445 } 446 rOrig := expr.String() 447 r, err := tree.FoldConstantLiterals(expr) 448 if err != nil { 449 t.Fatalf("%s: %v", d.expr, err) 450 } 451 if s := r.String(); d.expected != s { 452 t.Errorf("%s: expected %s, but found %s", d.expr, d.expected, s) 453 } 454 // Folding again should be a no-op. 455 r2, err := tree.FoldConstantLiterals(r) 456 if err != nil { 457 t.Fatalf("%s: %v", d.expr, err) 458 } 459 if s := r2.String(); d.expected != s { 460 t.Errorf("%s: expected %s, but found %s", d.expr, d.expected, s) 461 } 462 // The original expression should be unchanged. 463 if rStr := expr.String(); rOrig != rStr { 464 t.Fatalf("Original expression `%s` changed to `%s`", rOrig, rStr) 465 } 466 } 467 } 468 469 func TestFoldNumericConstants(t *testing.T) { 470 defer leaktest.AfterTest(t)() 471 testConstantLiteralFolding(t, []constantLiteralFoldingTestCase{ 472 // Unary ops. 473 {`+1`, `1`}, 474 {`+1.2`, `1.2`}, 475 {`-1`, `-1`}, 476 {`-1.2`, `-1.2`}, 477 // Unary ops (int only). 478 {`~1`, `-2`}, 479 {`~1.2`, `~1.2`}, 480 // Binary ops. 481 {`1 + 1`, `2`}, 482 {`1.2 + 2.3`, `3.5`}, 483 {`1 + 2.3`, `3.3`}, 484 {`2 - 1`, `1`}, 485 {`1.2 - 2.3`, `-1.1`}, 486 {`1 - 2.3`, `-1.3`}, 487 {`2 * 1`, `2`}, 488 {`1.2 * 2.3`, `2.76`}, 489 {`1 * 2.3`, `2.3`}, 490 {`123456789.987654321 * 987654321`, `1.21933e+17`}, 491 {`9 / 4`, `2.25`}, 492 {`9.7 / 4`, `2.425`}, 493 {`4.72 / 2.36`, `2`}, 494 {`0 / 0`, `0 / 0`}, // Will be caught during evaluation. 495 {`1 / 0`, `1 / 0`}, // Will be caught during evaluation. 496 // Binary ops (int only). 497 {`9 // 2`, `4`}, 498 {`-5 // 3`, `-1`}, 499 {`100 // 17`, `5`}, 500 {`100.43 // 17.82`, `100.43 // 17.82`}, // Constant folding won't fold numeric modulo. 501 {`0 // 0`, `0 // 0`}, // Will be caught during evaluation. 502 {`1 // 0`, `1 // 0`}, // Will be caught during evaluation. 503 {`9 % 2`, `1`}, 504 {`100 % 17`, `15`}, 505 {`100.43 % 17.82`, `100.43 % 17.82`}, // Constant folding won't fold numeric modulo. 506 {`1 & 3`, `1`}, 507 {`1.3 & 3.2`, `1.3 & 3.2`}, // Will be caught during type checking. 508 {`1 | 2`, `3`}, 509 {`1.3 | 2.8`, `1.3 | 2.8`}, // Will be caught during type checking. 510 {`1 # 3`, `2`}, 511 {`1.3 # 3.9`, `1.3 # 3.9`}, // Will be caught during type checking. 512 {`2 ^ 3`, `2 ^ 3`}, // Constant folding won't fold power. 513 {`1.3 ^ 3.9`, `1.3 ^ 3.9`}, 514 // Shift ops (int only). 515 {`1 << 2`, `1 << 2`}, 516 {`1 << -2`, `1 << -2`}, // Should be caught during evaluation. 517 {`1 << 9999999999999999999999999999`, `1 << 9999999999999999999999999999`}, // Will be caught during type checking. 518 {`1.2 << 2.4`, `1.2 << 2.4`}, // Will be caught during type checking. 519 {`4 >> 2`, `4 >> 2`}, 520 {`4.1 >> 2.9`, `4.1 >> 2.9`}, // Will be caught during type checking. 521 // Comparison ops. 522 {`4 = 2`, `false`}, 523 {`4 = 4.0`, `true`}, 524 {`4.0 = 4`, `true`}, 525 {`4.9 = 4`, `false`}, 526 {`4.9 = 4.9`, `true`}, 527 {`4 != 2`, `true`}, 528 {`4 != 4.0`, `false`}, 529 {`4.0 != 4`, `false`}, 530 {`4.9 != 4`, `true`}, 531 {`4.9 != 4.9`, `false`}, 532 {`4 < 2`, `false`}, 533 {`4 < 4.0`, `false`}, 534 {`4.0 < 4`, `false`}, 535 {`4.9 < 4`, `false`}, 536 {`4.9 < 4.9`, `false`}, 537 {`4 <= 2`, `false`}, 538 {`4 <= 4.0`, `true`}, 539 {`4.0 <= 4`, `true`}, 540 {`4.9 <= 4`, `false`}, 541 {`4.9 <= 4.9`, `true`}, 542 {`4 > 2`, `true`}, 543 {`4 > 4.0`, `false`}, 544 {`4.0 > 4`, `false`}, 545 {`4.9 > 4`, `true`}, 546 {`4.9 > 4.9`, `false`}, 547 {`4 >= 2`, `true`}, 548 {`4 >= 4.0`, `true`}, 549 {`4.0 >= 4`, `true`}, 550 {`4.9 >= 4`, `true`}, 551 {`4.9 >= 4.9`, `true`}, 552 // With parentheses. 553 {`(4)`, `4`}, 554 {`(((4)))`, `4`}, 555 {`(((9 / 3) * (1 / 3)))`, `1`}, 556 {`(((9 / 3) % (1 / 3)))`, `((3 % 0.333333))`}, 557 {`(1.0) << ((2) + 3 / (1/9))`, `1.0 << 29`}, 558 // With non-constants. 559 {`a + 5 * b`, `a + (5 * b)`}, 560 {`a + 5 + b + 7`, `((a + 5) + b) + 7`}, 561 {`a + 5 * 2`, `a + 10`}, 562 {`a * b + 5 / 2`, `(a * b) + 2.5`}, 563 {`a - b * 5 - 3`, `(a - (b * 5)) - 3`}, 564 {`a - b + 5 * 3`, `(a - b) + 15`}, 565 }) 566 } 567 568 func TestFoldStringConstants(t *testing.T) { 569 defer leaktest.AfterTest(t)() 570 testConstantLiteralFolding(t, []constantLiteralFoldingTestCase{ 571 // Binary ops. 572 {`'string' || 'string'`, `'stringstring'`}, 573 {`'string' || b'bytes'`, `b'stringbytes'`}, 574 {`b'bytes' || b'bytes'`, `b'bytesbytes'`}, 575 {`'a' || 'b' || 'c'`, `'abc'`}, 576 {`'\' || (b'0a' || b'\x0a')`, `b'\\0a\n'`}, 577 // Comparison ops. 578 {`'string' = 'string'`, `true`}, 579 {`'string' = b'bytes'`, `false`}, 580 {`'value' = b'value'`, `true`}, 581 {`b'bytes' = b'bytes'`, `true`}, 582 {`'string' != 'string'`, `false`}, 583 {`'string' != b'bytes'`, `true`}, 584 {`'value' != b'value'`, `false`}, 585 {`b'bytes' != b'bytes'`, `false`}, 586 {`'string' < 'string'`, `false`}, 587 {`'string' < b'bytes'`, `false`}, 588 {`'value' < b'value'`, `false`}, 589 {`b'bytes' < b'bytes'`, `false`}, 590 {`'string' <= 'string'`, `true`}, 591 {`'string' <= b'bytes'`, `false`}, 592 {`'value' <= b'value'`, `true`}, 593 {`b'bytes' <= b'bytes'`, `true`}, 594 {`'string' > 'string'`, `false`}, 595 {`'string' > b'bytes'`, `true`}, 596 {`'value' > b'value'`, `false`}, 597 {`b'bytes' > b'bytes'`, `false`}, 598 {`'string' >= 'string'`, `true`}, 599 {`'string' >= b'bytes'`, `true`}, 600 {`'value' >= b'value'`, `true`}, 601 {`b'bytes' >= b'bytes'`, `true`}, 602 // With parentheses. 603 {`('string') || (b'bytes')`, `b'stringbytes'`}, 604 {`('a') || (('b') || ('c'))`, `'abc'`}, 605 // With non-constants. 606 {`a > 'str' || b`, `a > ('str' || b)`}, 607 {`a > 'str' || 'ing'`, `a > 'string'`}, 608 }) 609 }