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 }