1 | /* | |
2 | * Copyright OpenSearch Contributors | |
3 | * SPDX-License-Identifier: Apache-2.0 | |
4 | */ | |
5 | ||
6 | ||
7 | package org.opensearch.sql.expression.conditional.cases; | |
8 | ||
9 | import static org.opensearch.sql.data.type.ExprCoreType.UNDEFINED; | |
10 | ||
11 | import com.google.common.collect.ImmutableList; | |
12 | import java.util.List; | |
13 | import java.util.stream.Collectors; | |
14 | import lombok.EqualsAndHashCode; | |
15 | import lombok.Getter; | |
16 | import lombok.ToString; | |
17 | import org.opensearch.sql.data.model.ExprNullValue; | |
18 | import org.opensearch.sql.data.model.ExprValue; | |
19 | import org.opensearch.sql.data.type.ExprType; | |
20 | import org.opensearch.sql.expression.Expression; | |
21 | import org.opensearch.sql.expression.ExpressionNodeVisitor; | |
22 | import org.opensearch.sql.expression.FunctionExpression; | |
23 | import org.opensearch.sql.expression.env.Environment; | |
24 | import org.opensearch.sql.expression.function.FunctionName; | |
25 | ||
26 | /** | |
27 | * A CASE clause is very different from a regular function. Functions have well-defined signature, | |
28 | * though CASE clause is more like a function implementation which requires type check "manually". | |
29 | */ | |
30 | @EqualsAndHashCode(callSuper = false) | |
31 | @Getter | |
32 | @ToString | |
33 | public class CaseClause extends FunctionExpression { | |
34 | ||
35 | /** | |
36 | * List of WHEN clauses. | |
37 | */ | |
38 | private final List<WhenClause> whenClauses; | |
39 | ||
40 | /** | |
41 | * Default result if none of WHEN conditions match. | |
42 | */ | |
43 | private final Expression defaultResult; | |
44 | ||
45 | /** | |
46 | * Initialize case clause. | |
47 | */ | |
48 | public CaseClause(List<WhenClause> whenClauses, Expression defaultResult) { | |
49 | super(FunctionName.of("case"), concatArgs(whenClauses, defaultResult)); | |
50 | this.whenClauses = whenClauses; | |
51 | this.defaultResult = defaultResult; | |
52 | } | |
53 | ||
54 | @Override | |
55 | public ExprValue valueOf(Environment<Expression, ExprValue> valueEnv) { | |
56 | for (WhenClause when : whenClauses) { | |
57 |
1
1. valueOf : negated conditional → KILLED |
if (when.isTrue(valueEnv)) { |
58 |
1
1. valueOf : replaced return value with null for org/opensearch/sql/expression/conditional/cases/CaseClause::valueOf → KILLED |
return when.valueOf(valueEnv); |
59 | } | |
60 | } | |
61 |
2
1. valueOf : negated conditional → KILLED 2. valueOf : replaced return value with null for org/opensearch/sql/expression/conditional/cases/CaseClause::valueOf → KILLED |
return (defaultResult == null) ? ExprNullValue.of() : defaultResult.valueOf(valueEnv); |
62 | } | |
63 | ||
64 | @Override | |
65 | public ExprType type() { | |
66 | List<ExprType> types = allResultTypes(); | |
67 | ||
68 | // Return undefined if all WHEN/ELSE return NULL | |
69 |
2
1. type : negated conditional → KILLED 2. type : replaced return value with null for org/opensearch/sql/expression/conditional/cases/CaseClause::type → KILLED |
return types.isEmpty() ? UNDEFINED : types.get(0); |
70 | } | |
71 | ||
72 | @Override | |
73 | public <T, C> T accept(ExpressionNodeVisitor<T, C> visitor, C context) { | |
74 |
1
1. accept : replaced return value with null for org/opensearch/sql/expression/conditional/cases/CaseClause::accept → KILLED |
return visitor.visitCase(this, context); |
75 | } | |
76 | ||
77 | /** | |
78 | * Get types of each result in WHEN clause and ELSE clause. | |
79 | * Exclude UNKNOWN type from NULL literal which means NULL in THEN or ELSE clause | |
80 | * is not included in result. | |
81 | * @return all result types. Use list so caller can generate friendly error message. | |
82 | */ | |
83 | public List<ExprType> allResultTypes() { | |
84 | List<ExprType> types = whenClauses.stream() | |
85 | .map(WhenClause::type) | |
86 | .collect(Collectors.toList()); | |
87 |
1
1. allResultTypes : negated conditional → KILLED |
if (defaultResult != null) { |
88 | types.add(defaultResult.type()); | |
89 | } | |
90 | ||
91 |
2
1. lambda$allResultTypes$0 : negated conditional → KILLED 2. lambda$allResultTypes$0 : replaced boolean return with true for org/opensearch/sql/expression/conditional/cases/CaseClause::lambda$allResultTypes$0 → KILLED |
types.removeIf(type -> (type == UNDEFINED)); |
92 |
1
1. allResultTypes : replaced return value with Collections.emptyList for org/opensearch/sql/expression/conditional/cases/CaseClause::allResultTypes → KILLED |
return types; |
93 | } | |
94 | ||
95 | private static List<Expression> concatArgs(List<WhenClause> whenClauses, | |
96 | Expression defaultResult) { | |
97 | ImmutableList.Builder<Expression> args = ImmutableList.builder(); | |
98 |
1
1. concatArgs : removed call to java/util/List::forEach → SURVIVED |
whenClauses.forEach(args::add); |
99 | ||
100 |
1
1. concatArgs : negated conditional → KILLED |
if (defaultResult != null) { |
101 | args.add(defaultResult); | |
102 | } | |
103 |
1
1. concatArgs : replaced return value with Collections.emptyList for org/opensearch/sql/expression/conditional/cases/CaseClause::concatArgs → SURVIVED |
return args.build(); |
104 | } | |
105 | ||
106 | } | |
Mutations | ||
57 |
1.1 |
|
58 |
1.1 |
|
61 |
1.1 2.2 |
|
69 |
1.1 2.2 |
|
74 |
1.1 |
|
87 |
1.1 |
|
91 |
1.1 2.2 |
|
92 |
1.1 |
|
98 |
1.1 |
|
100 |
1.1 |
|
103 |
1.1 |