github.com/mithrandie/csvq@v1.18.1/lib/query/user_defined_function_test.go (about) 1 package query 2 3 import ( 4 "context" 5 "reflect" 6 "testing" 7 "time" 8 9 "github.com/mithrandie/csvq/lib/parser" 10 "github.com/mithrandie/csvq/lib/value" 11 ) 12 13 var userDefinedFunctionMapDeclareTests = []struct { 14 Name string 15 Expr parser.FunctionDeclaration 16 Result UserDefinedFunctionMap 17 Error string 18 }{ 19 { 20 Name: "UserDefinedFunctionMap Declare", 21 Expr: parser.FunctionDeclaration{ 22 Name: parser.Identifier{Literal: "userfunc"}, 23 Parameters: []parser.VariableAssignment{ 24 { 25 Variable: parser.Variable{Name: "arg1"}, 26 }, 27 { 28 Variable: parser.Variable{Name: "arg2"}, 29 }, 30 }, 31 Statements: []parser.Statement{ 32 parser.Print{Value: parser.Variable{Name: "var1"}}, 33 }, 34 }, 35 Result: GenerateUserDefinedFunctionMap([]*UserDefinedFunction{ 36 { 37 Name: parser.Identifier{Literal: "userfunc"}, 38 Parameters: []parser.Variable{ 39 {Name: "arg1"}, 40 {Name: "arg2"}, 41 }, 42 Defaults: map[string]parser.QueryExpression{}, 43 RequiredArgs: 2, 44 Statements: []parser.Statement{ 45 parser.Print{Value: parser.Variable{Name: "var1"}}, 46 }, 47 }, 48 }), 49 }, 50 { 51 Name: "UserDefinedFunctionMap Declare Redeclaration Error", 52 Expr: parser.FunctionDeclaration{ 53 Name: parser.Identifier{Literal: "userfunc"}, 54 Parameters: []parser.VariableAssignment{ 55 { 56 Variable: parser.Variable{Name: "arg1"}, 57 }, 58 { 59 Variable: parser.Variable{Name: "arg2"}, 60 }, 61 }, 62 Statements: []parser.Statement{ 63 parser.Print{Value: parser.Variable{Name: "var1"}}, 64 }, 65 }, 66 Error: "function userfunc is redeclared", 67 }, 68 { 69 Name: "UserDefinedFunctionMap Declare Duplicate Prameters Error", 70 Expr: parser.FunctionDeclaration{ 71 Name: parser.Identifier{Literal: "userfunc2"}, 72 Parameters: []parser.VariableAssignment{ 73 { 74 Variable: parser.Variable{Name: "arg1"}, 75 }, 76 { 77 Variable: parser.Variable{Name: "arg1"}, 78 }, 79 }, 80 Statements: []parser.Statement{ 81 parser.Print{Value: parser.Variable{Name: "var1"}}, 82 }, 83 }, 84 Error: "parameter @arg1 is a duplicate", 85 }, 86 } 87 88 func TestUserDefinedFunctionMap_Declare(t *testing.T) { 89 funcs := NewUserDefinedFunctionMap() 90 91 for _, v := range userDefinedFunctionMapDeclareTests { 92 err := funcs.Declare(v.Expr) 93 if err != nil { 94 if len(v.Error) < 1 { 95 t.Errorf("%s: unexpected error %q", v.Name, err) 96 } else if err.Error() != v.Error { 97 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 98 } 99 continue 100 } 101 if 0 < len(v.Error) { 102 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 103 continue 104 } 105 if !SyncMapEqual(funcs, v.Result) { 106 t.Errorf("%s: result = %v, want %v", v.Name, funcs, v.Result) 107 } 108 } 109 } 110 111 var userDefinedFunctionMapDeclareAggregateTests = []struct { 112 Name string 113 Expr parser.AggregateDeclaration 114 Result UserDefinedFunctionMap 115 Error string 116 }{ 117 { 118 Name: "UserDefinedFunctionMap DeclareAggregate", 119 Expr: parser.AggregateDeclaration{ 120 Name: parser.Identifier{Literal: "useraggfunc"}, 121 Cursor: parser.Identifier{Literal: "column1"}, 122 Parameters: []parser.VariableAssignment{ 123 { 124 Variable: parser.Variable{Name: "arg1"}, 125 }, 126 { 127 Variable: parser.Variable{Name: "arg2"}, 128 }, 129 }, 130 Statements: []parser.Statement{ 131 parser.Print{Value: parser.Variable{Name: "var1"}}, 132 }, 133 }, 134 Result: GenerateUserDefinedFunctionMap([]*UserDefinedFunction{ 135 { 136 Name: parser.Identifier{Literal: "useraggfunc"}, 137 IsAggregate: true, 138 Cursor: parser.Identifier{Literal: "column1"}, 139 Parameters: []parser.Variable{ 140 {Name: "arg1"}, 141 {Name: "arg2"}, 142 }, 143 RequiredArgs: 2, 144 Defaults: map[string]parser.QueryExpression{}, 145 Statements: []parser.Statement{ 146 parser.Print{Value: parser.Variable{Name: "var1"}}, 147 }, 148 }, 149 }), 150 }, 151 { 152 Name: "UserDefinedFunctionMap DeclareAggregate Redeclaration Error", 153 Expr: parser.AggregateDeclaration{ 154 Name: parser.Identifier{Literal: "useraggfunc"}, 155 Cursor: parser.Identifier{Literal: "column1"}, 156 Statements: []parser.Statement{ 157 parser.Print{Value: parser.Variable{Name: "var1"}}, 158 }, 159 }, 160 Error: "function useraggfunc is redeclared", 161 }, 162 { 163 Name: "UserDefinedFunctionMap DeclareAggregate Duplicate Parameters Error", 164 Expr: parser.AggregateDeclaration{ 165 Name: parser.Identifier{Literal: "useraggfunc2"}, 166 Cursor: parser.Identifier{Literal: "column1"}, 167 Parameters: []parser.VariableAssignment{ 168 { 169 Variable: parser.Variable{Name: "arg1"}, 170 }, 171 { 172 Variable: parser.Variable{Name: "arg1"}, 173 }, 174 }, 175 Statements: []parser.Statement{ 176 parser.Print{Value: parser.Variable{Name: "var1"}}, 177 }, 178 }, 179 Error: "parameter @arg1 is a duplicate", 180 }, 181 } 182 183 func TestUserDefinedFunctionMap_DeclareAggregate(t *testing.T) { 184 funcs := NewUserDefinedFunctionMap() 185 186 for _, v := range userDefinedFunctionMapDeclareAggregateTests { 187 err := funcs.DeclareAggregate(v.Expr) 188 if err != nil { 189 if len(v.Error) < 1 { 190 t.Errorf("%s: unexpected error %q", v.Name, err) 191 } else if err.Error() != v.Error { 192 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 193 } 194 continue 195 } 196 if 0 < len(v.Error) { 197 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 198 continue 199 } 200 if !SyncMapEqual(funcs, v.Result) { 201 t.Errorf("%s: result = %v, want %v", v.Name, funcs, v.Result) 202 } 203 } 204 } 205 206 var userDefinedFunctionMapCheckDuplicateTests = []struct { 207 Name string 208 FuncName parser.Identifier 209 Result bool 210 Error string 211 }{ 212 { 213 Name: "UserDefinedFunctionMap CheckDuplicate Redeclaration Error", 214 FuncName: parser.Identifier{Literal: "userfunc"}, 215 Error: "function userfunc is redeclared", 216 }, 217 { 218 Name: "UserDefinedFunctionMap CheckDuplicate Duplicate with Built-in Function Error", 219 FuncName: parser.Identifier{Literal: "now"}, 220 Error: "function now is a built-in function", 221 }, 222 { 223 Name: "UserDefinedFunctionMap CheckDuplicate Duplicate with Aggregate Function Error", 224 FuncName: parser.Identifier{Literal: "count"}, 225 Error: "function count is a built-in function", 226 }, 227 { 228 Name: "UserDefinedFunctionMap CheckDuplicate Duplicate with Analytic Function Error", 229 FuncName: parser.Identifier{Literal: "row_number"}, 230 Error: "function row_number is a built-in function", 231 }, 232 { 233 Name: "UserDefinedFunctionMap CheckDuplicate OK", 234 FuncName: parser.Identifier{Literal: "undefined"}, 235 Result: true, 236 }, 237 } 238 239 func TestUserDefinedFunctionMap_CheckDuplicate(t *testing.T) { 240 funcs := GenerateUserDefinedFunctionMap([]*UserDefinedFunction{ 241 { 242 Name: parser.Identifier{Literal: "userfunc"}, 243 Parameters: []parser.Variable{ 244 {Name: "arg1"}, 245 {Name: "arg2"}, 246 }, 247 Statements: []parser.Statement{ 248 parser.Print{Value: parser.Variable{Name: "var1"}}, 249 }, 250 }, 251 }) 252 253 for _, v := range userDefinedFunctionMapCheckDuplicateTests { 254 err := funcs.CheckDuplicate(v.FuncName) 255 if err != nil { 256 if v.Result { 257 continue 258 } 259 260 if len(v.Error) < 1 { 261 t.Errorf("%s: unexpected error %q", v.Name, err) 262 } else if err.Error() != v.Error { 263 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 264 } 265 continue 266 } 267 if 0 < len(v.Error) { 268 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 269 } 270 } 271 } 272 273 var userDefinedFunctionMapGetTests = []struct { 274 Name string 275 Function parser.QueryExpression 276 FuncName string 277 Result *UserDefinedFunction 278 OK bool 279 }{ 280 { 281 Name: "UserDefinedFunctionMap Get", 282 Function: parser.Function{ 283 Name: "userfunc", 284 }, 285 FuncName: "userfunc", 286 Result: &UserDefinedFunction{ 287 Name: parser.Identifier{Literal: "userfunc"}, 288 Parameters: []parser.Variable{ 289 {Name: "arg1"}, 290 {Name: "arg2"}, 291 }, 292 Statements: []parser.Statement{ 293 parser.Print{Value: parser.Variable{Name: "var1"}}, 294 }, 295 }, 296 OK: true, 297 }, 298 { 299 Name: "UserDefinedFunctionMap Get Not Exist Error", 300 Function: parser.Function{ 301 Name: "notexist", 302 }, 303 FuncName: "notexist", 304 OK: false, 305 }, 306 } 307 308 func TestUserDefinedFunctionMap_Get(t *testing.T) { 309 funcs := GenerateUserDefinedFunctionMap([]*UserDefinedFunction{ 310 { 311 Name: parser.Identifier{Literal: "userfunc"}, 312 Parameters: []parser.Variable{ 313 {Name: "arg1"}, 314 {Name: "arg2"}, 315 }, 316 Statements: []parser.Statement{ 317 parser.Print{Value: parser.Variable{Name: "var1"}}, 318 }, 319 }, 320 }) 321 322 for _, v := range userDefinedFunctionMapGetTests { 323 result, ok := funcs.Get(v.FuncName) 324 if ok != v.OK { 325 t.Errorf("%s: result = %t, want %t", v.Name, ok, v.OK) 326 continue 327 } 328 if ok && v.OK && !reflect.DeepEqual(result, v.Result) { 329 t.Errorf("%s: result = %v, want %v", v.Name, result, v.Result) 330 } 331 } 332 } 333 334 var userDefinedFunctionExecuteTests = []struct { 335 Name string 336 Func *UserDefinedFunction 337 Args []value.Primary 338 Result value.Primary 339 Error string 340 }{ 341 { 342 Name: "UserDefinedFunction Execute", 343 Func: &UserDefinedFunction{ 344 Name: parser.Identifier{Literal: "userfunc"}, 345 Parameters: []parser.Variable{ 346 {Name: "arg1"}, 347 {Name: "arg2"}, 348 }, 349 Defaults: map[string]parser.QueryExpression{ 350 "arg2": parser.NewIntegerValue(3), 351 }, 352 RequiredArgs: 1, 353 Statements: []parser.Statement{ 354 parser.VariableDeclaration{ 355 Assignments: []parser.VariableAssignment{ 356 { 357 Variable: parser.Variable{Name: "var2"}, 358 Value: parser.Arithmetic{ 359 LHS: parser.Arithmetic{ 360 LHS: parser.Variable{Name: "arg1"}, 361 RHS: parser.Variable{Name: "arg2"}, 362 Operator: parser.Token{Token: '+', Literal: "+"}, 363 }, 364 RHS: parser.Variable{Name: "var1"}, 365 Operator: parser.Token{Token: '+', Literal: "+"}, 366 }, 367 }, 368 }, 369 }, 370 parser.Return{ 371 Value: parser.Variable{Name: "var2"}, 372 }, 373 }, 374 }, 375 Args: []value.Primary{ 376 value.NewInteger(2), 377 }, 378 Result: value.NewInteger(6), 379 }, 380 { 381 Name: "UserDefinedFunction Execute No Return Statement", 382 Func: &UserDefinedFunction{ 383 Name: parser.Identifier{Literal: "userfunc"}, 384 Parameters: []parser.Variable{ 385 {Name: "arg1"}, 386 {Name: "arg2"}, 387 }, 388 Statements: []parser.Statement{ 389 parser.VariableDeclaration{ 390 Assignments: []parser.VariableAssignment{ 391 { 392 Variable: parser.Variable{Name: "var2"}, 393 Value: parser.Arithmetic{ 394 LHS: parser.Arithmetic{ 395 LHS: parser.Variable{Name: "arg1"}, 396 RHS: parser.Variable{Name: "arg2"}, 397 Operator: parser.Token{Token: '+', Literal: "+"}, 398 }, 399 RHS: parser.Variable{Name: "var1"}, 400 Operator: parser.Token{Token: '+', Literal: "+"}, 401 }, 402 }, 403 }, 404 }, 405 }, 406 }, 407 Args: []value.Primary{ 408 value.NewInteger(2), 409 value.NewInteger(3), 410 }, 411 Result: value.NewNull(), 412 }, 413 { 414 Name: "UserDefinedFunction Execute Argument Length Error", 415 Func: &UserDefinedFunction{ 416 Name: parser.Identifier{Literal: "userfunc"}, 417 Parameters: []parser.Variable{ 418 {Name: "arg1"}, 419 {Name: "arg2"}, 420 }, 421 Statements: []parser.Statement{ 422 parser.VariableDeclaration{ 423 Assignments: []parser.VariableAssignment{ 424 { 425 Variable: parser.Variable{Name: "var2"}, 426 Value: parser.Arithmetic{ 427 LHS: parser.Arithmetic{ 428 LHS: parser.Variable{Name: "arg1"}, 429 RHS: parser.Variable{Name: "arg2"}, 430 Operator: parser.Token{Token: '+', Literal: "+"}, 431 }, 432 RHS: parser.Variable{Name: "var1"}, 433 Operator: parser.Token{Token: '+', Literal: "+"}, 434 }, 435 }, 436 }, 437 }, 438 parser.Return{ 439 Value: parser.Variable{Name: "var2"}, 440 }, 441 }, 442 }, 443 Args: []value.Primary{ 444 value.NewInteger(2), 445 }, 446 Error: "function userfunc takes exactly 2 arguments", 447 }, 448 { 449 Name: "UserDefinedFunction Execute Argument Evaluation Error", 450 Func: &UserDefinedFunction{ 451 Name: parser.Identifier{Literal: "userfunc"}, 452 Parameters: []parser.Variable{ 453 {Name: "arg1"}, 454 {Name: "arg2"}, 455 }, 456 Defaults: map[string]parser.QueryExpression{ 457 "arg2": parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}, 458 }, 459 RequiredArgs: 1, 460 Statements: []parser.Statement{ 461 parser.VariableDeclaration{ 462 Assignments: []parser.VariableAssignment{ 463 { 464 Variable: parser.Variable{Name: "var2"}, 465 Value: parser.Arithmetic{ 466 LHS: parser.Arithmetic{ 467 LHS: parser.Variable{Name: "arg1"}, 468 RHS: parser.Variable{Name: "arg2"}, 469 Operator: parser.Token{Token: '+', Literal: "+"}, 470 }, 471 RHS: parser.Variable{Name: "var1"}, 472 Operator: parser.Token{Token: '+', Literal: "+"}, 473 }, 474 }, 475 }, 476 }, 477 parser.Return{ 478 Value: parser.Variable{Name: "var2"}, 479 }, 480 }, 481 }, 482 Args: []value.Primary{ 483 value.NewInteger(2), 484 }, 485 Error: "field notexist does not exist", 486 }, 487 { 488 Name: "UserDefinedFunction Execute Execution Error", 489 Func: &UserDefinedFunction{ 490 Name: parser.Identifier{Literal: "userfunc"}, 491 Parameters: []parser.Variable{ 492 {Name: "arg1"}, 493 {Name: "arg2"}, 494 }, 495 Statements: []parser.Statement{ 496 parser.VariableDeclaration{ 497 Assignments: []parser.VariableAssignment{ 498 { 499 Variable: parser.Variable{Name: "var2"}, 500 Value: parser.Subquery{ 501 Query: parser.SelectQuery{ 502 SelectEntity: parser.SelectEntity{ 503 SelectClause: parser.SelectClause{ 504 Fields: []parser.QueryExpression{ 505 parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}}, 506 }, 507 }, 508 }, 509 }, 510 }, 511 }, 512 }, 513 }, 514 }, 515 }, 516 Args: []value.Primary{ 517 value.NewInteger(2), 518 value.NewInteger(3), 519 }, 520 Error: "field notexist does not exist", 521 }, 522 } 523 524 func TestUserDefinedFunction_Execute(t *testing.T) { 525 scope := GenerateReferenceScope([]map[string]map[string]interface{}{ 526 { 527 scopeNameVariables: { 528 "var1": value.NewInteger(1), 529 }, 530 }, 531 }, nil, time.Time{}, nil) 532 533 ctx := context.Background() 534 for _, v := range userDefinedFunctionExecuteTests { 535 result, err := v.Func.Execute(ctx, scope, v.Args) 536 if err != nil { 537 if len(v.Error) < 1 { 538 t.Errorf("%s: unexpected error %q", v.Name, err) 539 } else if err.Error() != v.Error { 540 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 541 } 542 continue 543 } 544 if 0 < len(v.Error) { 545 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 546 continue 547 } 548 if !reflect.DeepEqual(result, v.Result) { 549 t.Errorf("%s: result = %s, want %s", v.Name, result, v.Result) 550 } 551 } 552 } 553 554 var userDefinedFunctionExecuteAggregateTests = []struct { 555 Name string 556 Func *UserDefinedFunction 557 Values []value.Primary 558 Args []value.Primary 559 Result value.Primary 560 Error string 561 }{ 562 { 563 Name: "UserDefinedFunction Execute Aggregate", 564 Func: &UserDefinedFunction{ 565 Name: parser.Identifier{Literal: "useraggfunc"}, 566 IsAggregate: true, 567 Cursor: parser.Identifier{Literal: "list"}, 568 Statements: []parser.Statement{ 569 parser.VariableDeclaration{ 570 Assignments: []parser.VariableAssignment{ 571 { 572 Variable: parser.Variable{Name: "value"}, 573 }, 574 { 575 Variable: parser.Variable{Name: "fetch"}, 576 }, 577 }, 578 }, 579 parser.WhileInCursor{ 580 Variables: []parser.Variable{ 581 {Name: "fetch"}, 582 }, 583 Cursor: parser.Identifier{Literal: "list"}, 584 Statements: []parser.Statement{ 585 parser.If{ 586 Condition: parser.Is{ 587 LHS: parser.Variable{Name: "fetch"}, 588 RHS: parser.NewNullValue(), 589 }, 590 Statements: []parser.Statement{ 591 parser.FlowControl{Token: parser.CONTINUE}, 592 }, 593 }, 594 parser.If{ 595 Condition: parser.Is{ 596 LHS: parser.Variable{Name: "value"}, 597 RHS: parser.NewNullValue(), 598 }, 599 Statements: []parser.Statement{ 600 parser.VariableSubstitution{ 601 Variable: parser.Variable{Name: "value"}, 602 Value: parser.Variable{Name: "fetch"}, 603 }, 604 parser.FlowControl{Token: parser.CONTINUE}, 605 }, 606 }, 607 parser.VariableSubstitution{ 608 Variable: parser.Variable{Name: "value"}, 609 Value: parser.Arithmetic{ 610 LHS: parser.Variable{Name: "value"}, 611 RHS: parser.Variable{Name: "fetch"}, 612 Operator: parser.Token{Token: '*', Literal: "*"}, 613 }, 614 }, 615 }, 616 }, 617 618 parser.Return{ 619 Value: parser.Variable{Name: "value"}, 620 }, 621 }, 622 }, 623 Values: []value.Primary{ 624 value.NewInteger(1), 625 value.NewInteger(2), 626 value.NewInteger(3), 627 }, 628 Result: value.NewInteger(6), 629 }, 630 { 631 Name: "UserDefinedFunction Execute Aggregate With Arguments", 632 Func: &UserDefinedFunction{ 633 Name: parser.Identifier{Literal: "useraggfunc"}, 634 IsAggregate: true, 635 Cursor: parser.Identifier{Literal: "list"}, 636 Parameters: []parser.Variable{ 637 {Name: "default"}, 638 }, 639 Statements: []parser.Statement{ 640 parser.VariableDeclaration{ 641 Assignments: []parser.VariableAssignment{ 642 { 643 Variable: parser.Variable{Name: "value"}, 644 }, 645 { 646 Variable: parser.Variable{Name: "fetch"}, 647 }, 648 }, 649 }, 650 parser.WhileInCursor{ 651 Variables: []parser.Variable{ 652 {Name: "fetch"}, 653 }, 654 Cursor: parser.Identifier{Literal: "list"}, 655 Statements: []parser.Statement{ 656 parser.If{ 657 Condition: parser.Is{ 658 LHS: parser.Variable{Name: "fetch"}, 659 RHS: parser.NewNullValue(), 660 }, 661 Statements: []parser.Statement{ 662 parser.FlowControl{Token: parser.CONTINUE}, 663 }, 664 }, 665 parser.If{ 666 Condition: parser.Is{ 667 LHS: parser.Variable{Name: "value"}, 668 RHS: parser.NewNullValue(), 669 }, 670 Statements: []parser.Statement{ 671 parser.VariableSubstitution{ 672 Variable: parser.Variable{Name: "value"}, 673 Value: parser.Variable{Name: "fetch"}, 674 }, 675 parser.FlowControl{Token: parser.CONTINUE}, 676 }, 677 }, 678 parser.VariableSubstitution{ 679 Variable: parser.Variable{Name: "value"}, 680 Value: parser.Arithmetic{ 681 LHS: parser.Variable{Name: "value"}, 682 RHS: parser.Variable{Name: "fetch"}, 683 Operator: parser.Token{Token: '*', Literal: "*"}, 684 }, 685 }, 686 }, 687 }, 688 689 parser.If{ 690 Condition: parser.Is{ 691 LHS: parser.Variable{Name: "value"}, 692 RHS: parser.NewNullValue(), 693 }, 694 Statements: []parser.Statement{ 695 parser.VariableSubstitution{ 696 Variable: parser.Variable{Name: "value"}, 697 Value: parser.Variable{Name: "default"}, 698 }, 699 }, 700 }, 701 702 parser.Return{ 703 Value: parser.Variable{Name: "value"}, 704 }, 705 }, 706 }, 707 Values: []value.Primary{ 708 value.NewNull(), 709 value.NewNull(), 710 value.NewNull(), 711 }, 712 Args: []value.Primary{ 713 value.NewInteger(0), 714 }, 715 Result: value.NewInteger(0), 716 }, 717 { 718 Name: "UserDefinedFunction Aggregate Argument Length Error", 719 Func: &UserDefinedFunction{ 720 Name: parser.Identifier{Literal: "useraggfunc"}, 721 IsAggregate: true, 722 Cursor: parser.Identifier{Literal: "list"}, 723 Statements: []parser.Statement{ 724 parser.VariableDeclaration{ 725 Assignments: []parser.VariableAssignment{ 726 { 727 Variable: parser.Variable{Name: "value"}, 728 }, 729 { 730 Variable: parser.Variable{Name: "fetch"}, 731 }, 732 }, 733 }, 734 parser.WhileInCursor{ 735 Variables: []parser.Variable{ 736 {Name: "fetch"}, 737 }, 738 Cursor: parser.Identifier{Literal: "list"}, 739 Statements: []parser.Statement{ 740 parser.If{ 741 Condition: parser.Is{ 742 LHS: parser.Variable{Name: "fetch"}, 743 RHS: parser.NewNullValue(), 744 }, 745 Statements: []parser.Statement{ 746 parser.FlowControl{Token: parser.CONTINUE}, 747 }, 748 }, 749 parser.If{ 750 Condition: parser.Is{ 751 LHS: parser.Variable{Name: "value"}, 752 RHS: parser.NewNullValue(), 753 }, 754 Statements: []parser.Statement{ 755 parser.VariableSubstitution{ 756 Variable: parser.Variable{Name: "value"}, 757 Value: parser.Variable{Name: "fetch"}, 758 }, 759 parser.FlowControl{Token: parser.CONTINUE}, 760 }, 761 }, 762 parser.VariableSubstitution{ 763 Variable: parser.Variable{Name: "value"}, 764 Value: parser.Arithmetic{ 765 LHS: parser.Variable{Name: "value"}, 766 RHS: parser.Variable{Name: "fetch"}, 767 Operator: parser.Token{Token: '*', Literal: "*"}, 768 }, 769 }, 770 }, 771 }, 772 773 parser.Return{ 774 Value: parser.Variable{Name: "value"}, 775 }, 776 }, 777 }, 778 Values: []value.Primary{ 779 value.NewInteger(1), 780 value.NewInteger(2), 781 value.NewInteger(3), 782 }, 783 Args: []value.Primary{ 784 value.NewInteger(0), 785 }, 786 Error: "function useraggfunc takes exactly 1 argument", 787 }, 788 } 789 790 func TestUserDefinedFunction_ExecuteAggregate(t *testing.T) { 791 scope := NewReferenceScope(TestTx) 792 ctx := context.Background() 793 794 for _, v := range userDefinedFunctionExecuteAggregateTests { 795 result, err := v.Func.ExecuteAggregate(ctx, scope, v.Values, v.Args) 796 if err != nil { 797 if len(v.Error) < 1 { 798 t.Errorf("%s: unexpected error %q", v.Name, err) 799 } else if err.Error() != v.Error { 800 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 801 } 802 continue 803 } 804 if 0 < len(v.Error) { 805 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 806 continue 807 } 808 if !reflect.DeepEqual(result, v.Result) { 809 t.Errorf("%s: result = %s, want %s", v.Name, result, v.Result) 810 } 811 } 812 } 813 814 var userDefinedFunctionCheckArgsLenTests = []struct { 815 Name string 816 Func *UserDefinedFunction 817 ArgsLen int 818 Error string 819 }{ 820 { 821 Name: "UserDefinedFunction CheckArgsLen", 822 Func: &UserDefinedFunction{ 823 Name: parser.Identifier{Literal: "userfunc"}, 824 Parameters: []parser.Variable{ 825 {Name: "arg1"}, 826 {Name: "arg2"}, 827 }, 828 Defaults: map[string]parser.QueryExpression{ 829 "arg2": parser.NewIntegerValue(3), 830 }, 831 RequiredArgs: 1, 832 Statements: []parser.Statement{}, 833 }, 834 ArgsLen: 1, 835 Error: "", 836 }, 837 { 838 Name: "UserDefinedFunction CheckArgsLen Argument Length Error", 839 Func: &UserDefinedFunction{ 840 Name: parser.Identifier{Literal: "userfunc"}, 841 Parameters: []parser.Variable{ 842 {Name: "arg1"}, 843 {Name: "arg2"}, 844 }, 845 RequiredArgs: 2, 846 Statements: []parser.Statement{}, 847 }, 848 ArgsLen: 1, 849 Error: "function userfunc takes exactly 2 arguments", 850 }, 851 { 852 Name: "UserDefinedFunction CheckArgsLen Too Little Argument Error", 853 Func: &UserDefinedFunction{ 854 Name: parser.Identifier{Literal: "userfunc"}, 855 Parameters: []parser.Variable{ 856 {Name: "arg1"}, 857 {Name: "arg2"}, 858 }, 859 Defaults: map[string]parser.QueryExpression{ 860 "arg2": parser.NewIntegerValue(3), 861 }, 862 RequiredArgs: 1, 863 Statements: []parser.Statement{}, 864 }, 865 ArgsLen: 0, 866 Error: "function userfunc takes at least 1 argument", 867 }, 868 { 869 Name: "UserDefinedFunction CheckArgsLen Too Many Argument Length Error", 870 Func: &UserDefinedFunction{ 871 Name: parser.Identifier{Literal: "userfunc"}, 872 Parameters: []parser.Variable{ 873 {Name: "arg1"}, 874 {Name: "arg2"}, 875 }, 876 Defaults: map[string]parser.QueryExpression{ 877 "arg2": parser.NewIntegerValue(3), 878 }, 879 RequiredArgs: 1, 880 Statements: []parser.Statement{}, 881 }, 882 ArgsLen: 3, 883 Error: "function userfunc takes at most 2 arguments", 884 }, 885 { 886 Name: "UserDefinedFunction CheckArgsLen Aggregate Argument Length Error", 887 Func: &UserDefinedFunction{ 888 Name: parser.Identifier{Literal: "userfunc"}, 889 IsAggregate: true, 890 Parameters: []parser.Variable{ 891 {Name: "arg1"}, 892 {Name: "arg2"}, 893 }, 894 RequiredArgs: 2, 895 Cursor: parser.Identifier{Literal: "list"}, 896 Statements: []parser.Statement{}, 897 }, 898 ArgsLen: 1, 899 Error: "function userfunc takes exactly 3 arguments", 900 }, 901 } 902 903 func TestUserDefinedFunction_CheckArgsLen(t *testing.T) { 904 for _, v := range userDefinedFunctionCheckArgsLenTests { 905 err := v.Func.CheckArgsLen(parser.Identifier{Literal: "userfunc"}, "userfunc", v.ArgsLen) 906 if err != nil { 907 if len(v.Error) < 1 { 908 t.Errorf("%s: unexpected error %q", v.Name, err) 909 } else if err.Error() != v.Error { 910 t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error) 911 } 912 continue 913 } 914 if 0 < len(v.Error) { 915 t.Errorf("%s: no error, want error %q", v.Name, v.Error) 916 continue 917 } 918 } 919 }