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  }