github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/eval_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 gosql "database/sql" 16 "fmt" 17 "path/filepath" 18 "regexp" 19 "strings" 20 "testing" 21 22 "github.com/cockroachdb/cockroach/pkg/base" 23 "github.com/cockroachdb/cockroach/pkg/col/coldata" 24 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 25 "github.com/cockroachdb/cockroach/pkg/sql/colexec" 26 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 27 "github.com/cockroachdb/cockroach/pkg/sql/execinfra" 28 "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" 29 "github.com/cockroachdb/cockroach/pkg/sql/opt/exec/execbuilder" 30 "github.com/cockroachdb/cockroach/pkg/sql/opt/optbuilder" 31 "github.com/cockroachdb/cockroach/pkg/sql/opt/xform" 32 "github.com/cockroachdb/cockroach/pkg/sql/parser" 33 "github.com/cockroachdb/cockroach/pkg/sql/rowexec" 34 _ "github.com/cockroachdb/cockroach/pkg/sql/sem/builtins" 35 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 36 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 37 "github.com/cockroachdb/cockroach/pkg/sql/types" 38 "github.com/cockroachdb/cockroach/pkg/testutils" 39 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 40 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 41 "github.com/cockroachdb/datadriven" 42 "github.com/stretchr/testify/require" 43 ) 44 45 func TestEval(t *testing.T) { 46 defer leaktest.AfterTest(t)() 47 ctx := context.Background() 48 evalCtx := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 49 defer evalCtx.Stop(ctx) 50 51 walk := func(t *testing.T, getExpr func(*testing.T, *datadriven.TestData) string) { 52 datadriven.Walk(t, filepath.Join("testdata", "eval"), func(t *testing.T, path string) { 53 datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { 54 if d.Cmd != "eval" { 55 t.Fatalf("unsupported command %s", d.Cmd) 56 } 57 return getExpr(t, d) + "\n" 58 }) 59 }) 60 } 61 62 walkExpr := func(t *testing.T, getExpr func(tree.Expr) (tree.TypedExpr, error)) { 63 walk(t, func(t *testing.T, d *datadriven.TestData) string { 64 expr, err := parser.ParseExpr(d.Input) 65 if err != nil { 66 t.Fatalf("%s: %v", d.Input, err) 67 } 68 e, err := getExpr(expr) 69 if err != nil { 70 return fmt.Sprint(err) 71 } 72 r, err := e.Eval(evalCtx) 73 if err != nil { 74 return fmt.Sprint(err) 75 } 76 return r.String() 77 }) 78 } 79 80 t.Run("opt", func(t *testing.T) { 81 walkExpr(t, func(e tree.Expr) (tree.TypedExpr, error) { 82 return optBuildScalar(evalCtx, e) 83 }) 84 }) 85 86 t.Run("no-opt", func(t *testing.T) { 87 walkExpr(t, func(e tree.Expr) (tree.TypedExpr, error) { 88 // expr.TypeCheck to avoid constant folding. 89 semaCtx := tree.MakeSemaContext() 90 typedExpr, err := e.TypeCheck(ctx, &semaCtx, types.Any) 91 if err != nil { 92 return nil, err 93 } 94 return evalCtx.NormalizeExpr(typedExpr) 95 }) 96 }) 97 98 // The opt and no-opt tests don't do an end-to-end SQL test. Do that 99 // here by executing a SELECT. In order to make the output be the same 100 // we have to also figure out what the expected output type is so we 101 // can correctly format the datum. 102 t.Run("sql", func(t *testing.T) { 103 s, sqlDB, _ := serverutils.StartServer(t, base.TestServerArgs{}) 104 defer s.Stopper().Stop(ctx) 105 106 walk(t, func(t *testing.T, d *datadriven.TestData) string { 107 var res gosql.NullString 108 if err := sqlDB.QueryRow(fmt.Sprintf("SELECT (%s)::STRING", d.Input)).Scan(&res); err != nil { 109 return strings.TrimPrefix(err.Error(), "pq: ") 110 } 111 if !res.Valid { 112 return "NULL" 113 } 114 115 // We have a non-null result. We can't just return 116 // res.String here because these strings don't 117 // match the datum.String() representations. For 118 // example, a bitarray has a res.String of something 119 // like `1001001` but the datum representation is 120 // `B'1001001'`. Thus we have to parse res.String (a 121 // SQL result) back into a datum and return that. 122 123 expr, err := parser.ParseExpr(d.Input) 124 if err != nil { 125 t.Fatal(err) 126 } 127 // expr.TypeCheck to avoid constant folding. 128 semaCtx := tree.MakeSemaContext() 129 typedExpr, err := expr.TypeCheck(ctx, &semaCtx, types.Any) 130 if err != nil { 131 // An error here should have been found above by QueryRow. 132 t.Fatal(err) 133 } 134 135 switch typedExpr.ResolvedType().Family() { 136 case types.TupleFamily: 137 // ParseAndRequireString doesn't handle tuples, so we have to convert them ourselves. 138 var datums tree.Datums 139 // Fetch the original expression's tuple values. 140 tuple := typedExpr.(*tree.Tuple) 141 for i, s := range strings.Split(res.String[1:len(res.String)-1], ",") { 142 if s == "" { 143 continue 144 } 145 // Figure out the type of the tuple value. 146 expr, err := tuple.Exprs[i].TypeCheck(ctx, &semaCtx, types.Any) 147 if err != nil { 148 t.Fatal(err) 149 } 150 // Now parse the new string as the expected type. 151 datum, err := tree.ParseAndRequireString(expr.ResolvedType(), s, evalCtx) 152 if err != nil { 153 t.Errorf("%s: %s", err, s) 154 return err.Error() 155 } 156 datums = append(datums, datum) 157 } 158 return tree.NewDTuple(typedExpr.ResolvedType(), datums...).String() 159 } 160 datum, err := tree.ParseAndRequireString(typedExpr.ResolvedType(), res.String, evalCtx) 161 if err != nil { 162 t.Errorf("%s: %s", err, res.String) 163 return err.Error() 164 } 165 return datum.String() 166 }) 167 }) 168 169 t.Run("vectorized", func(t *testing.T) { 170 walk(t, func(t *testing.T, d *datadriven.TestData) string { 171 if d.Input == "B'11111111111111111111111110000101'::int4" { 172 // Skip this test: https://github.com/cockroachdb/cockroach/pull/40790#issuecomment-532597294. 173 return strings.TrimSpace(d.Expected) 174 } 175 flowCtx := &execinfra.FlowCtx{ 176 EvalCtx: evalCtx, 177 } 178 memMonitor := execinfra.NewTestMemMonitor(ctx, cluster.MakeTestingClusterSettings()) 179 defer memMonitor.Stop(ctx) 180 acc := memMonitor.MakeBoundAccount() 181 defer acc.Close(ctx) 182 expr, err := parser.ParseExpr(d.Input) 183 require.NoError(t, err) 184 if _, ok := expr.(*tree.RangeCond); ok { 185 // RangeCond gets normalized to comparison expressions and its Eval 186 // method returns an error, so skip it for execution. 187 return strings.TrimSpace(d.Expected) 188 } 189 semaCtx := tree.MakeSemaContext() 190 typedExpr, err := expr.TypeCheck(ctx, &semaCtx, types.Any) 191 if err != nil { 192 // Skip this test as it's testing an expected error which would be 193 // caught before execution. 194 return strings.TrimSpace(d.Expected) 195 } 196 typs := []*types.T{typedExpr.ResolvedType()} 197 198 // inputTyps has no relation to the actual expression result type. Used 199 // for generating a batch. 200 inputTyps := []*types.T{types.Int} 201 202 batchesReturned := 0 203 args := colexec.NewColOperatorArgs{ 204 Spec: &execinfrapb.ProcessorSpec{ 205 Input: []execinfrapb.InputSyncSpec{{ 206 Type: execinfrapb.InputSyncSpec_UNORDERED, 207 ColumnTypes: inputTyps, 208 }}, 209 Core: execinfrapb.ProcessorCoreUnion{ 210 Noop: &execinfrapb.NoopCoreSpec{}, 211 }, 212 Post: execinfrapb.PostProcessSpec{ 213 RenderExprs: []execinfrapb.Expression{{Expr: d.Input}}, 214 }, 215 }, 216 Inputs: []colexecbase.Operator{ 217 &colexecbase.CallbackOperator{ 218 NextCb: func(_ context.Context) coldata.Batch { 219 if batchesReturned > 0 { 220 return coldata.ZeroBatch 221 } 222 // It doesn't matter what types we create the input batch with. 223 batch := coldata.NewMemBatch(inputTyps, coldata.StandardColumnFactory) 224 batch.SetLength(1) 225 batchesReturned++ 226 return batch 227 }, 228 }, 229 }, 230 StreamingMemAccount: &acc, 231 // Unsupported post processing specs are wrapped and run through the 232 // row execution engine. 233 ProcessorConstructor: rowexec.NewProcessor, 234 } 235 args.TestingKnobs.UseStreamingMemAccountForBuffering = true 236 result, err := colexec.NewColOperator(ctx, flowCtx, args) 237 if testutils.IsError(err, "unsupported type") { 238 // Skip this test as execution is not supported by the vectorized 239 // engine. 240 return strings.TrimSpace(d.Expected) 241 } else { 242 require.NoError(t, err) 243 } 244 245 mat, err := colexec.NewMaterializer( 246 flowCtx, 247 0, /* processorID */ 248 result.Op, 249 typs, 250 nil, /* output */ 251 nil, /* metadataSourcesQueue */ 252 nil, /* toClose */ 253 nil, /* outputStatsToTrace */ 254 nil, /* cancelFlow */ 255 ) 256 require.NoError(t, err) 257 258 var ( 259 row sqlbase.EncDatumRow 260 meta *execinfrapb.ProducerMetadata 261 ) 262 ctx = mat.Start(ctx) 263 row, meta = mat.Next() 264 if meta != nil { 265 if meta.Err != nil { 266 return fmt.Sprint(meta.Err) 267 } 268 t.Fatalf("unexpected metadata: %+v", meta) 269 } 270 if row == nil { 271 // Might be some metadata. 272 if meta := mat.DrainHelper(); meta.Err != nil { 273 t.Fatalf("unexpected error: %s", meta.Err) 274 } 275 t.Fatal("unexpected end of input") 276 } 277 return row[0].Datum.String() 278 }) 279 }) 280 } 281 282 func optBuildScalar(evalCtx *tree.EvalContext, e tree.Expr) (tree.TypedExpr, error) { 283 var o xform.Optimizer 284 o.Init(evalCtx, nil /* catalog */) 285 ctx := context.Background() 286 semaCtx := tree.MakeSemaContext() 287 b := optbuilder.NewScalar(ctx, &semaCtx, evalCtx, o.Factory()) 288 if err := b.Build(e); err != nil { 289 return nil, err 290 } 291 292 bld := execbuilder.New(nil /* factory */, o.Memo(), nil /* catalog */, o.Memo().RootExpr(), evalCtx) 293 ivh := tree.MakeIndexedVarHelper(nil /* container */, 0) 294 295 expr, err := bld.BuildScalar(&ivh) 296 if err != nil { 297 return nil, err 298 } 299 return expr, nil 300 } 301 302 func TestTimeConversion(t *testing.T) { 303 defer leaktest.AfterTest(t)() 304 tests := []struct { 305 start string 306 format string 307 tm string 308 revformat string 309 reverse string 310 }{ 311 // %a %A %b %B (+ %Y) 312 {`Wed Oct 05 2016`, `%a %b %d %Y`, `2016-10-05 00:00:00+00:00`, ``, ``}, 313 {`Wednesday October 05 2016`, `%A %B %d %Y`, `2016-10-05 00:00:00+00:00`, ``, ``}, 314 // %c 315 {`Wed Oct 5 01:02:03 2016`, `%c`, `2016-10-05 01:02:03+00:00`, ``, ``}, 316 // %C %d (+ %m %y) 317 {`20 06 10 12`, `%C %y %m %d`, `2006-10-12 00:00:00+00:00`, ``, ``}, 318 // %D 319 {`10/12/06`, `%D`, `2006-10-12 00:00:00+00:00`, ``, ``}, 320 // %e (+ %Y %m) 321 {`2006 10 3`, `%Y %m %e`, `2006-10-03 00:00:00+00:00`, ``, ``}, 322 // %f (+ %c) 323 {`Wed Oct 5 01:02:03 2016 .123`, `%c .%f`, `2016-10-05 01:02:03.123+00:00`, `.%f`, `.123000`}, 324 {`Wed Oct 5 01:02:03 2016 .123456`, `%c .%f`, `2016-10-05 01:02:03.123456+00:00`, `.%f`, `.123456`}, 325 {`Wed Oct 5 01:02:03 2016 .999999`, `%c .%f`, `2016-10-05 01:02:03.999999+00:00`, `.%f`, `.999999`}, 326 // %F 327 {`2006-10-03`, `%F`, `2006-10-03 00:00:00+00:00`, ``, ``}, 328 // %h (+ %Y %d) 329 {`2006 Oct 03`, `%Y %h %d`, `2006-10-03 00:00:00+00:00`, ``, ``}, 330 // %H (+ %S %M) 331 {`20061012 01:03:02`, `%Y%m%d %H:%S:%M`, `2006-10-12 01:02:03+00:00`, ``, ``}, 332 // %I (+ %Y %m %d) 333 {`20161012 11`, `%Y%m%d %I`, `2016-10-12 11:00:00+00:00`, ``, ``}, 334 // %j (+ %Y) 335 {`2016 286`, `%Y %j`, `2016-10-12 00:00:00+00:00`, ``, ``}, 336 // %k (+ %Y %m %d) 337 {`20061012 23`, `%Y%m%d %k`, `2006-10-12 23:00:00+00:00`, ``, ``}, 338 // %l (+ %Y %m %d %p) 339 {`20061012 5 PM`, `%Y%m%d %l %p`, `2006-10-12 17:00:00+00:00`, ``, ``}, 340 // %n (+ %Y %m %d) 341 {"2006\n10\n03", `%Y%n%m%n%d`, `2006-10-03 00:00:00+00:00`, ``, ``}, 342 // %p cannot be parsed before hour specifiers, so be sure that 343 // they appear in this order. 344 {`20161012 11 PM`, `%Y%m%d %I %p`, `2016-10-12 23:00:00+00:00`, ``, ``}, 345 {`20161012 11 AM`, `%Y%m%d %I %p`, `2016-10-12 11:00:00+00:00`, ``, ``}, 346 // %r 347 {`20161012 11:02:03 PM`, `%Y%m%d %r`, `2016-10-12 23:02:03+00:00`, ``, ``}, 348 // %R 349 {`20161012 11:02`, `%Y%m%d %R`, `2016-10-12 11:02:00+00:00`, ``, ``}, 350 // %s 351 {`1491920586`, `%s`, `2017-04-11 14:23:06+00:00`, ``, ``}, 352 // %t (+ %Y %m %d) 353 {"2006\t10\t03", `%Y%t%m%t%d`, `2006-10-03 00:00:00+00:00`, ``, ``}, 354 // %T (+ %Y %m %d) 355 {`20061012 01:02:03`, `%Y%m%d %T`, `2006-10-12 01:02:03+00:00`, ``, ``}, 356 // %U %u (+ %Y) 357 {`2018 10 4`, `%Y %U %u`, `2018-03-15 00:00:00+00:00`, ``, ``}, 358 // %W %w (+ %Y) 359 {`2018 10 4`, `%Y %W %w`, `2018-03-08 00:00:00+00:00`, ``, ``}, 360 // %x 361 {`10/12/06`, `%x`, `2006-10-12 00:00:00+00:00`, ``, ``}, 362 // %X 363 {`20061012 01:02:03`, `%Y%m%d %X`, `2006-10-12 01:02:03+00:00`, ``, ``}, 364 // %y (+ %m %d) 365 {`000101`, `%y%m%d`, `2000-01-01 00:00:00+00:00`, ``, ``}, 366 {`680101`, `%y%m%d`, `2068-01-01 00:00:00+00:00`, ``, ``}, 367 {`690101`, `%y%m%d`, `1969-01-01 00:00:00+00:00`, ``, ``}, 368 {`990101`, `%y%m%d`, `1999-01-01 00:00:00+00:00`, ``, ``}, 369 // %Y 370 {`19000101`, `%Y%m%d`, `1900-01-01 00:00:00+00:00`, ``, ``}, 371 {`20000101`, `%Y%m%d`, `2000-01-01 00:00:00+00:00`, ``, ``}, 372 {`30000101`, `%Y%m%d`, `3000-01-01 00:00:00+00:00`, ``, ``}, 373 // %z causes the time zone to adjust the time when parsing, but the time zone information 374 // is not retained when printing the timestamp out back. 375 {`20160101 13:00 +0655`, `%Y%m%d %H:%M %z`, `2016-01-01 06:05:00+00:00`, `%Y%m%d %H:%M %z`, `20160101 06:05 +0000`}, 376 } 377 378 for _, test := range tests { 379 ctx := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 380 defer ctx.Mon.Stop(context.Background()) 381 exprStr := fmt.Sprintf("experimental_strptime('%s', '%s')", test.start, test.format) 382 expr, err := parser.ParseExpr(exprStr) 383 if err != nil { 384 t.Errorf("%s: %v", exprStr, err) 385 continue 386 } 387 semaCtx := tree.MakeSemaContext() 388 typedExpr, err := expr.TypeCheck(context.Background(), &semaCtx, types.Timestamp) 389 if err != nil { 390 t.Errorf("%s: %v", exprStr, err) 391 continue 392 } 393 r, err := typedExpr.Eval(ctx) 394 if err != nil { 395 t.Errorf("%s: %v", exprStr, err) 396 continue 397 } 398 ts, ok := r.(*tree.DTimestampTZ) 399 if !ok { 400 t.Errorf("%s: result not a timestamp: %s", exprStr, r) 401 continue 402 } 403 404 tmS := ts.String() 405 tmS = tmS[1 : len(tmS)-1] // strip the quote delimiters 406 if tmS != test.tm { 407 t.Errorf("%s: got %q, expected %q", exprStr, tmS, test.tm) 408 continue 409 } 410 411 revfmt := test.format 412 if test.revformat != "" { 413 revfmt = test.revformat 414 } 415 416 ref := test.start 417 if test.reverse != "" { 418 ref = test.reverse 419 } 420 421 exprStr = fmt.Sprintf("experimental_strftime('%s'::timestamp, '%s')", tmS, revfmt) 422 expr, err = parser.ParseExpr(exprStr) 423 if err != nil { 424 t.Errorf("%s: %v", exprStr, err) 425 continue 426 } 427 typedExpr, err = expr.TypeCheck(context.Background(), &semaCtx, types.Timestamp) 428 if err != nil { 429 t.Errorf("%s: %v", exprStr, err) 430 continue 431 } 432 r, err = typedExpr.Eval(ctx) 433 if err != nil { 434 t.Errorf("%s: %v", exprStr, err) 435 continue 436 } 437 rs, ok := r.(*tree.DString) 438 if !ok { 439 t.Errorf("%s: result not a string: %s", exprStr, r) 440 continue 441 } 442 revS := string(*rs) 443 if ref != revS { 444 t.Errorf("%s: got %q, expected %q", exprStr, revS, ref) 445 } 446 } 447 } 448 449 func TestEvalError(t *testing.T) { 450 defer leaktest.AfterTest(t)() 451 testData := []struct { 452 expr string 453 expected string 454 }{ 455 {`1 % 0`, `zero modulus`}, 456 {`1 / 0`, `division by zero`}, 457 {`1::float / 0::float`, `division by zero`}, 458 {`1 // 0`, `division by zero`}, 459 {`1.5 / 0`, `division by zero`}, 460 {`'11h2m'::interval / 0`, `division by zero`}, 461 {`'11h2m'::interval / 0.0::float`, `division by zero`}, 462 {`'???'::bool`, 463 `could not parse "???" as type bool`}, 464 {`'foo'::int`, 465 `could not parse "foo" as type int: strconv.ParseInt: parsing "foo": invalid syntax`}, 466 {`'3\r2'::int`, 467 `could not parse "3\\r2" as type int: strconv.ParseInt: parsing "3\\r2": invalid syntax`}, 468 {`'bar'::float`, 469 `could not parse "bar" as type float: strconv.ParseFloat: parsing "bar": invalid syntax`}, 470 {`'baz'::decimal`, 471 `could not parse "baz" as type decimal`}, 472 {`'2010-09-28 12:00:00.1q'::date`, 473 `parsing as type date: could not parse "2010-09-28 12:00:00.1q"`}, 474 {`'12:00:00q'::time`, `could not parse "12:00:00q" as type time`}, 475 {`'2010-09-28 12:00.1 MST'::timestamp`, 476 `unimplemented: timestamp abbreviations not supported`}, 477 {`'abcd'::interval`, 478 `could not parse "abcd" as type interval: interval: missing unit`}, 479 {`'1- 2:3:4 9'::interval`, 480 `could not parse "1- 2:3:4 9" as type interval: invalid input syntax for type interval 1- 2:3:4 9`}, 481 {`e'\\xdedf0d36174'::BYTES`, `could not parse "\\xdedf0d36174" as type bytes: encoding/hex: odd length hex string`}, 482 {`ARRAY[NULL, ARRAY[1, 2]]`, `multidimensional arrays must have array expressions with matching dimensions`}, 483 {`ARRAY[ARRAY[1, 2], NULL]`, `multidimensional arrays must have array expressions with matching dimensions`}, 484 {`ARRAY[ARRAY[1, 2], ARRAY[1]]`, `multidimensional arrays must have array expressions with matching dimensions`}, 485 // TODO(pmattis): Check for overflow. 486 // {`~0 + 1`, `0`}, 487 {`9223372036854775807::int + 1::int`, `integer out of range`}, 488 {`-9223372036854775807::int + -2::int`, `integer out of range`}, 489 {`-9223372036854775807::int + -9223372036854775807::int`, `integer out of range`}, 490 {`9223372036854775807::int + 9223372036854775807::int`, `integer out of range`}, 491 {`9223372036854775807::int - -1::int`, `integer out of range`}, 492 {`-9223372036854775807::int - 2::int`, `integer out of range`}, 493 {`-9223372036854775807::int - 9223372036854775807::int`, `integer out of range`}, 494 {`9223372036854775807::int - -9223372036854775807::int`, `integer out of range`}, 495 {`4611686018427387904::int * 2::int`, `integer out of range`}, 496 {`4611686018427387904::int * 2::int`, `integer out of range`}, 497 {`(-9223372036854775807:::int - 1) * -1:::int`, `integer out of range`}, 498 {`123 ^ 100`, `integer out of range`}, 499 {`power(123, 100)`, `integer out of range`}, 500 // Although these next two tests are valid integers, a float cannot represent 501 // them exactly, and so rounds them to a larger number that is out of bounds 502 // for an int. Thus, they should fail during this conversion. 503 {`9223372036854775807::float::int`, `integer out of range`}, 504 {`-9223372036854775808::float::int`, `integer out of range`}, 505 // The two smallest floats that cannot be converted to an int. 506 {`9223372036854775296::float::int`, `integer out of range`}, 507 {`-9223372036854775296::float::int`, `integer out of range`}, 508 {`1e500::decimal::int`, `integer out of range`}, 509 {`1e500::decimal::float`, `float out of range`}, 510 {`1e300::decimal::float::int`, `integer out of range`}, 511 {`'Inf'::decimal::int`, `integer out of range`}, 512 {`'NaN'::decimal::int`, `integer out of range`}, 513 {`'Inf'::float::int`, `integer out of range`}, 514 {`'NaN'::float::int`, `integer out of range`}, 515 {`'1.1'::int`, `could not parse "1.1" as type int`}, 516 {`IFERROR(1/0, 123, 'unknown')`, `division by zero`}, 517 {`ISERROR(1/0, 'unknown')`, `division by zero`}, 518 {`like_escape('___', '\___', 'abc')`, `invalid escape string`}, 519 {`like_escape('abc', 'abc', 'a日')`, `invalid escape string`}, 520 {`like_escape('abc', 'abc', '漢日')`, `invalid escape string`}, 521 {`like_escape('__', '_', '_')`, `LIKE pattern must not end with escape character`}, 522 {`like_escape('%%', '%', '%')`, `LIKE pattern must not end with escape character`}, 523 {`like_escape('__', '___', '_')`, `LIKE pattern must not end with escape character`}, 524 {`like_escape('%%', '%%%', '%')`, `LIKE pattern must not end with escape character`}, 525 {`like_escape('abc', 'ab%', '%')`, `LIKE pattern must not end with escape character`}, 526 {`like_escape('abc', '%b%', '%')`, `LIKE pattern must not end with escape character`}, 527 {`like_escape('abc', 'ab_', '_')`, `LIKE pattern must not end with escape character`}, 528 {`like_escape('abc', '%b_', '_')`, `LIKE pattern must not end with escape character`}, 529 {`like_escape('abc', '%b漢', '漢')`, `LIKE pattern must not end with escape character`}, 530 {`similar_to_escape('abc', '-a-b-c', '-')`, `error parsing regexp: invalid escape sequence`}, 531 {`similar_to_escape('a(b)c', '%((_)_', '(')`, `error parsing regexp: unexpected )`}, 532 {`convert_from('\xaaaa'::bytea, 'woo')`, `convert_from(): invalid source encoding name "woo"`}, 533 {`convert_from('\xaaaa'::bytea, 'utf8')`, `convert_from(): invalid byte sequence for encoding "UTF8"`}, 534 {`convert_to('abc', 'woo')`, `convert_to(): invalid destination encoding name "woo"`}, 535 {`convert_to('漢', 'latin1')`, `convert_to(): character '漢' has no representation in encoding "LATIN1"`}, 536 {`'123'::BIT`, `could not parse string as bit array: "2" is not a valid binary digit`}, 537 {`B'1001' & B'101'`, `cannot AND bit strings of different sizes`}, 538 {`B'1001' | B'101'`, `cannot OR bit strings of different sizes`}, 539 {`B'1001' # B'101'`, `cannot XOR bit strings of different sizes`}, 540 } 541 ctx := context.Background() 542 for _, d := range testData { 543 expr, err := parser.ParseExpr(d.expr) 544 if err != nil { 545 t.Fatalf("%s: %v", d.expr, err) 546 } 547 semaCtx := tree.MakeSemaContext() 548 typedExpr, err := tree.TypeCheck(ctx, expr, &semaCtx, types.Any) 549 if err == nil { 550 evalCtx := tree.NewTestingEvalContext(cluster.MakeTestingClusterSettings()) 551 defer evalCtx.Stop(context.Background()) 552 _, err = typedExpr.Eval(evalCtx) 553 } 554 if !testutils.IsError(err, strings.Replace(regexp.QuoteMeta(d.expected), `\.\*`, `.*`, -1)) { 555 t.Errorf("%s: expected %s, but found %v", d.expr, d.expected, err) 556 } 557 } 558 }