github.com/mithrandie/csvq@v1.18.1/lib/query/field_analyzer_test.go (about)

     1  package query
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/mithrandie/csvq/lib/parser"
     9  )
    10  
    11  var hasAggregateFunctionTests = []struct {
    12  	Name   string
    13  	Expr   parser.QueryExpression
    14  	Result bool
    15  	Error  string
    16  }{
    17  	{
    18  		Name: "Aggregate Function",
    19  		Expr: parser.AggregateFunction{
    20  			Name:     "avg",
    21  			Distinct: parser.Token{},
    22  			Args: []parser.QueryExpression{
    23  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
    24  			},
    25  		},
    26  		Result: true,
    27  	},
    28  	{
    29  		Name: "List Function",
    30  		Expr: parser.ListFunction{
    31  			Name:     "listagg",
    32  			Distinct: parser.Token{Token: parser.DISTINCT, Literal: "distinct"},
    33  			Args: []parser.QueryExpression{
    34  				parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
    35  				parser.NewStringValue(","),
    36  			},
    37  			OrderBy: parser.OrderByClause{
    38  				Items: []parser.QueryExpression{
    39  					parser.OrderItem{Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
    40  				},
    41  			},
    42  		},
    43  		Result: true,
    44  	},
    45  	{
    46  		Name: "Function",
    47  		Expr: parser.Function{
    48  			Name: "coalesce",
    49  			Args: []parser.QueryExpression{
    50  				parser.NewNullValue(),
    51  				parser.NewStringValue("str"),
    52  			},
    53  		},
    54  		Result: false,
    55  	},
    56  	{
    57  		Name: "JSON_OBJECT Function",
    58  		Expr: parser.Function{
    59  			Name: "json_object",
    60  			Args: []parser.QueryExpression{
    61  				parser.Field{
    62  					Object: parser.FieldReference{
    63  						Column: parser.Identifier{Literal: "column1"},
    64  					},
    65  				},
    66  			},
    67  		},
    68  		Result: false,
    69  	},
    70  	{
    71  		Name: "User Defined Aggregate Function",
    72  		Expr: parser.Function{
    73  			Name: "useraggfunc",
    74  			Args: []parser.QueryExpression{
    75  				parser.NewIntegerValue(1),
    76  			},
    77  		},
    78  		Result: true,
    79  	},
    80  	{
    81  		Name: "Aggregate Function in Function Arguments",
    82  		Expr: parser.Function{
    83  			Name: "coalesce",
    84  			Args: []parser.QueryExpression{
    85  				parser.AggregateFunction{
    86  					Name:     "avg",
    87  					Distinct: parser.Token{},
    88  					Args: []parser.QueryExpression{
    89  						parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
    90  					},
    91  				},
    92  				parser.NewStringValue("str"),
    93  			},
    94  		},
    95  		Result: true,
    96  	},
    97  	{
    98  		Name: "Analytic Function",
    99  		Expr: parser.AnalyticFunction{
   100  			Name: "rank",
   101  			AnalyticClause: parser.AnalyticClause{
   102  				PartitionClause: parser.PartitionClause{
   103  					Values: []parser.QueryExpression{
   104  						parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   105  					},
   106  				},
   107  				OrderByClause: parser.OrderByClause{
   108  					Items: []parser.QueryExpression{
   109  						parser.OrderItem{
   110  							Value: parser.AggregateFunction{
   111  								Name:     "avg",
   112  								Distinct: parser.Token{},
   113  								Args: []parser.QueryExpression{
   114  									parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   115  								},
   116  							},
   117  						},
   118  					},
   119  				},
   120  			},
   121  		},
   122  		Result: true,
   123  	},
   124  	{
   125  		Name: "Case Expression",
   126  		Expr: parser.CaseExpr{
   127  			Value: parser.NewIntegerValue(2),
   128  			When: []parser.QueryExpression{
   129  				parser.CaseExprWhen{
   130  					Condition: parser.NewIntegerValue(1),
   131  					Result: parser.AggregateFunction{
   132  						Name:     "avg",
   133  						Distinct: parser.Token{},
   134  						Args: []parser.QueryExpression{
   135  							parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   136  						},
   137  					},
   138  				},
   139  				parser.CaseExprWhen{
   140  					Condition: parser.NewIntegerValue(2),
   141  					Result:    parser.NewStringValue("B"),
   142  				},
   143  			},
   144  		},
   145  		Result: true,
   146  	},
   147  	{
   148  		Name: "Case Expression without Comparison",
   149  		Expr: parser.CaseExpr{
   150  			When: []parser.QueryExpression{
   151  				parser.CaseExprWhen{
   152  					Condition: parser.NewIntegerValue(1),
   153  					Result: parser.AggregateFunction{
   154  						Name:     "avg",
   155  						Distinct: parser.Token{},
   156  						Args: []parser.QueryExpression{
   157  							parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   158  						},
   159  					},
   160  				},
   161  				parser.CaseExprWhen{
   162  					Condition: parser.NewIntegerValue(2),
   163  					Result:    parser.NewStringValue("B"),
   164  				},
   165  			},
   166  		},
   167  		Result: true,
   168  	},
   169  	{
   170  		Name: "Case Expression with Else",
   171  		Expr: parser.CaseExpr{
   172  			When: []parser.QueryExpression{
   173  				parser.CaseExprWhen{
   174  					Condition: parser.NewIntegerValue(1),
   175  					Result:    parser.NewStringValue("A"),
   176  				},
   177  				parser.CaseExprWhen{
   178  					Condition: parser.NewIntegerValue(2),
   179  					Result:    parser.NewStringValue("B"),
   180  				},
   181  			},
   182  			Else: parser.CaseExprElse{
   183  				Result: parser.AggregateFunction{
   184  					Name:     "avg",
   185  					Distinct: parser.Token{},
   186  					Args: []parser.QueryExpression{
   187  						parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   188  					},
   189  				},
   190  			},
   191  		},
   192  		Result: true,
   193  	},
   194  }
   195  
   196  func TestHasAggregateFunction(t *testing.T) {
   197  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
   198  		{
   199  			scopeNameFunctions: {
   200  				"USERFUNC": &UserDefinedFunction{
   201  					Name: parser.Identifier{Literal: "userfunc"},
   202  					Parameters: []parser.Variable{
   203  						{Name: "arg1"},
   204  					},
   205  					RequiredArgs: 1,
   206  					Statements: []parser.Statement{
   207  						parser.Return{Value: parser.Variable{Name: "arg1"}},
   208  					},
   209  				},
   210  				"USERAGGFUNC": &UserDefinedFunction{
   211  					Name: parser.Identifier{Literal: "userfunc"},
   212  					Parameters: []parser.Variable{
   213  						{Name: "arg1"},
   214  					},
   215  					RequiredArgs: 1,
   216  					Statements: []parser.Statement{
   217  						parser.Return{Value: parser.Variable{Name: "arg1"}},
   218  					},
   219  					IsAggregate: true,
   220  				},
   221  			},
   222  		},
   223  	}, nil, time.Time{}, nil)
   224  
   225  	for _, v := range hasAggregateFunctionTests {
   226  		result, err := HasAggregateFunction(v.Expr, scope)
   227  		if err != nil {
   228  			if len(v.Error) < 1 {
   229  				t.Errorf("%s: unexpected error %q", v.Name, err)
   230  			} else if err.Error() != v.Error {
   231  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
   232  			}
   233  			continue
   234  		}
   235  		if 0 < len(v.Error) {
   236  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
   237  			continue
   238  		}
   239  		if result != v.Result {
   240  			t.Errorf("%s: result = %t, want %t", v.Name, result, v.Result)
   241  		}
   242  	}
   243  }
   244  
   245  var searchAnalyticFunctionsTests = []struct {
   246  	Name   string
   247  	Expr   parser.QueryExpression
   248  	Result []parser.AnalyticFunction
   249  	Error  string
   250  }{
   251  	{
   252  		Name: "Analytic Function",
   253  		Expr: parser.AnalyticFunction{
   254  			Name: "rank",
   255  			AnalyticClause: parser.AnalyticClause{
   256  				PartitionClause: parser.PartitionClause{
   257  					Values: []parser.QueryExpression{
   258  						parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   259  					},
   260  				},
   261  				OrderByClause: parser.OrderByClause{
   262  					Items: []parser.QueryExpression{
   263  						parser.OrderItem{
   264  							Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   265  						},
   266  					},
   267  				},
   268  			},
   269  		},
   270  		Result: []parser.AnalyticFunction{
   271  			{
   272  				Name: "rank",
   273  				AnalyticClause: parser.AnalyticClause{
   274  					PartitionClause: parser.PartitionClause{
   275  						Values: []parser.QueryExpression{
   276  							parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   277  						},
   278  					},
   279  					OrderByClause: parser.OrderByClause{
   280  						Items: []parser.QueryExpression{
   281  							parser.OrderItem{
   282  								Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   283  							},
   284  						},
   285  					},
   286  				},
   287  			},
   288  		},
   289  	},
   290  	{
   291  		Name: "Nested Analytic Function",
   292  		Expr: parser.AnalyticFunction{
   293  			Name: "sum",
   294  			Args: []parser.QueryExpression{
   295  				parser.AnalyticFunction{
   296  					Name: "rank",
   297  					AnalyticClause: parser.AnalyticClause{
   298  						PartitionClause: parser.PartitionClause{
   299  							Values: []parser.QueryExpression{
   300  								parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   301  							},
   302  						},
   303  						OrderByClause: parser.OrderByClause{
   304  							Items: []parser.QueryExpression{
   305  								parser.OrderItem{
   306  									Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   307  								},
   308  							},
   309  						},
   310  					},
   311  				},
   312  			},
   313  			AnalyticClause: parser.AnalyticClause{},
   314  		},
   315  		Result: []parser.AnalyticFunction{
   316  			{
   317  				Name: "rank",
   318  				AnalyticClause: parser.AnalyticClause{
   319  					PartitionClause: parser.PartitionClause{
   320  						Values: []parser.QueryExpression{
   321  							parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   322  						},
   323  					},
   324  					OrderByClause: parser.OrderByClause{
   325  						Items: []parser.QueryExpression{
   326  							parser.OrderItem{
   327  								Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   328  							},
   329  						},
   330  					},
   331  				},
   332  			},
   333  			{
   334  				Name: "sum",
   335  				Args: []parser.QueryExpression{
   336  					parser.AnalyticFunction{
   337  						Name: "rank",
   338  						AnalyticClause: parser.AnalyticClause{
   339  							PartitionClause: parser.PartitionClause{
   340  								Values: []parser.QueryExpression{
   341  									parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   342  								},
   343  							},
   344  							OrderByClause: parser.OrderByClause{
   345  								Items: []parser.QueryExpression{
   346  									parser.OrderItem{
   347  										Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   348  									},
   349  								},
   350  							},
   351  						},
   352  					},
   353  				},
   354  				AnalyticClause: parser.AnalyticClause{},
   355  			},
   356  		},
   357  	},
   358  	{
   359  		Name: "Arithmetic",
   360  		Expr: parser.Arithmetic{
   361  			LHS: parser.AnalyticFunction{
   362  				Name:           "rank",
   363  				AnalyticClause: parser.AnalyticClause{},
   364  			},
   365  			RHS: parser.AnalyticFunction{
   366  				Name:           "row_number",
   367  				AnalyticClause: parser.AnalyticClause{},
   368  			},
   369  			Operator: parser.Token{Token: '+', Literal: "+"},
   370  		},
   371  		Result: []parser.AnalyticFunction{
   372  			{
   373  				Name:           "row_number",
   374  				AnalyticClause: parser.AnalyticClause{},
   375  			},
   376  			{
   377  				Name:           "rank",
   378  				AnalyticClause: parser.AnalyticClause{},
   379  			},
   380  		},
   381  	},
   382  	{
   383  		Name: "Case Expression",
   384  		Expr: parser.CaseExpr{
   385  			Value: parser.NewIntegerValue(2),
   386  			When: []parser.QueryExpression{
   387  				parser.CaseExprWhen{
   388  					Condition: parser.NewIntegerValue(1),
   389  					Result: parser.AnalyticFunction{
   390  						Name: "rank",
   391  						AnalyticClause: parser.AnalyticClause{
   392  							PartitionClause: parser.PartitionClause{
   393  								Values: []parser.QueryExpression{
   394  									parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   395  								},
   396  							},
   397  							OrderByClause: parser.OrderByClause{
   398  								Items: []parser.QueryExpression{
   399  									parser.OrderItem{
   400  										Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   401  									},
   402  								},
   403  							},
   404  						},
   405  					},
   406  				},
   407  				parser.CaseExprWhen{
   408  					Condition: parser.NewIntegerValue(2),
   409  					Result:    parser.NewStringValue("B"),
   410  				},
   411  			},
   412  		},
   413  		Result: []parser.AnalyticFunction{
   414  			{
   415  				Name: "rank",
   416  				AnalyticClause: parser.AnalyticClause{
   417  					PartitionClause: parser.PartitionClause{
   418  						Values: []parser.QueryExpression{
   419  							parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   420  						},
   421  					},
   422  					OrderByClause: parser.OrderByClause{
   423  						Items: []parser.QueryExpression{
   424  							parser.OrderItem{
   425  								Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   426  							},
   427  						},
   428  					},
   429  				},
   430  			},
   431  		},
   432  	},
   433  	{
   434  		Name: "Case Expression without Comparison",
   435  		Expr: parser.CaseExpr{
   436  			When: []parser.QueryExpression{
   437  				parser.CaseExprWhen{
   438  					Condition: parser.NewIntegerValue(1),
   439  					Result: parser.AnalyticFunction{
   440  						Name: "rank",
   441  						AnalyticClause: parser.AnalyticClause{
   442  							PartitionClause: parser.PartitionClause{
   443  								Values: []parser.QueryExpression{
   444  									parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   445  								},
   446  							},
   447  							OrderByClause: parser.OrderByClause{
   448  								Items: []parser.QueryExpression{
   449  									parser.OrderItem{
   450  										Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   451  									},
   452  								},
   453  							},
   454  						},
   455  					},
   456  				},
   457  				parser.CaseExprWhen{
   458  					Condition: parser.NewIntegerValue(2),
   459  					Result:    parser.NewStringValue("B"),
   460  				},
   461  			},
   462  		},
   463  		Result: []parser.AnalyticFunction{
   464  			{
   465  				Name: "rank",
   466  				AnalyticClause: parser.AnalyticClause{
   467  					PartitionClause: parser.PartitionClause{
   468  						Values: []parser.QueryExpression{
   469  							parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   470  						},
   471  					},
   472  					OrderByClause: parser.OrderByClause{
   473  						Items: []parser.QueryExpression{
   474  							parser.OrderItem{
   475  								Value: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
   476  							},
   477  						},
   478  					},
   479  				},
   480  			},
   481  		},
   482  	},
   483  	{
   484  		Name: "Case Expression with Else",
   485  		Expr: parser.CaseExpr{
   486  			When: []parser.QueryExpression{
   487  				parser.CaseExprWhen{
   488  					Condition: parser.NewIntegerValue(1),
   489  					Result:    parser.NewStringValue("A"),
   490  				},
   491  				parser.CaseExprWhen{
   492  					Condition: parser.NewIntegerValue(2),
   493  					Result:    parser.NewStringValue("B"),
   494  				},
   495  			},
   496  			Else: parser.CaseExprElse{
   497  				Result: parser.AnalyticFunction{
   498  					Name:           "rank",
   499  					AnalyticClause: parser.AnalyticClause{},
   500  				},
   501  			},
   502  		},
   503  		Result: []parser.AnalyticFunction{
   504  			{
   505  				Name:           "rank",
   506  				AnalyticClause: parser.AnalyticClause{},
   507  			},
   508  		},
   509  	},
   510  	{
   511  		Name: "JSON_OBJECT Function",
   512  		Expr: parser.Function{
   513  			Name: "json_object",
   514  			Args: []parser.QueryExpression{
   515  				parser.Field{
   516  					Object: parser.FieldReference{
   517  						Column: parser.Identifier{Literal: "column1"},
   518  					},
   519  				},
   520  			},
   521  		},
   522  		Result: []parser.AnalyticFunction(nil),
   523  	},
   524  }
   525  
   526  func TestSearchAnalyticFunctions(t *testing.T) {
   527  	for _, v := range searchAnalyticFunctionsTests {
   528  		result, err := SearchAnalyticFunctions(v.Expr)
   529  		if err != nil {
   530  			if len(v.Error) < 1 {
   531  				t.Errorf("%s: unexpected error %q", v.Name, err)
   532  			} else if err.Error() != v.Error {
   533  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
   534  			}
   535  			continue
   536  		}
   537  		if 0 < len(v.Error) {
   538  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
   539  			continue
   540  		}
   541  		if !reflect.DeepEqual(result, v.Result) {
   542  			t.Errorf("%s: result = %v, want %v", v.Name, result, v.Result)
   543  		}
   544  	}
   545  }