WindowExpressionAnalyzer.java

1
/*
2
 * Copyright OpenSearch Contributors
3
 * SPDX-License-Identifier: Apache-2.0
4
 */
5
6
7
package org.opensearch.sql.analysis;
8
9
import static org.opensearch.sql.ast.tree.Sort.SortOption.DEFAULT_ASC;
10
import static org.opensearch.sql.ast.tree.Sort.SortOption.DEFAULT_DESC;
11
import static org.opensearch.sql.ast.tree.Sort.SortOrder.ASC;
12
import static org.opensearch.sql.ast.tree.Sort.SortOrder.DESC;
13
14
import java.util.List;
15
import java.util.stream.Collectors;
16
import lombok.RequiredArgsConstructor;
17
import org.apache.commons.lang3.tuple.ImmutablePair;
18
import org.apache.commons.lang3.tuple.Pair;
19
import org.opensearch.sql.ast.AbstractNodeVisitor;
20
import org.opensearch.sql.ast.expression.Alias;
21
import org.opensearch.sql.ast.expression.UnresolvedExpression;
22
import org.opensearch.sql.ast.expression.WindowFunction;
23
import org.opensearch.sql.ast.tree.Sort.SortOption;
24
import org.opensearch.sql.expression.Expression;
25
import org.opensearch.sql.expression.NamedExpression;
26
import org.opensearch.sql.expression.window.WindowDefinition;
27
import org.opensearch.sql.planner.logical.LogicalPlan;
28
import org.opensearch.sql.planner.logical.LogicalSort;
29
import org.opensearch.sql.planner.logical.LogicalWindow;
30
31
/**
32
 * Window expression analyzer that analyzes window function expression in expression list
33
 * in project operator.
34
 */
35
@RequiredArgsConstructor
36
public class WindowExpressionAnalyzer extends AbstractNodeVisitor<LogicalPlan, AnalysisContext> {
37
38
  /**
39
   * Expression analyzer.
40
   */
41
  private final ExpressionAnalyzer expressionAnalyzer;
42
43
  /**
44
   * Child node to be wrapped by a new window operator.
45
   */
46
  private final LogicalPlan child;
47
48
  /**
49
   * Analyze the given project item and return window operator (with child node inside)
50
   * if the given project item is a window function.
51
   * @param projectItem   project item
52
   * @param context       analysis context
53
   * @return              window operator or original child if not windowed
54
   */
55
  public LogicalPlan analyze(UnresolvedExpression projectItem, AnalysisContext context) {
56
    LogicalPlan window = projectItem.accept(this, context);
57 2 1. analyze : negated conditional → KILLED
2. analyze : replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyze → KILLED
    return (window == null) ? child : window;
58
  }
59
60
  @Override
61
  public LogicalPlan visitAlias(Alias node, AnalysisContext context) {
62 1 1. visitAlias : negated conditional → KILLED
    if (!(node.getDelegated() instanceof WindowFunction)) {
63
      return null;
64
    }
65
66
    WindowFunction unresolved = (WindowFunction) node.getDelegated();
67
    Expression windowFunction = expressionAnalyzer.analyze(unresolved, context);
68
    List<Expression> partitionByList = analyzePartitionList(unresolved, context);
69
    List<Pair<SortOption, Expression>> sortList = analyzeSortList(unresolved, context);
70
71
    WindowDefinition windowDefinition = new WindowDefinition(partitionByList, sortList);
72
    NamedExpression namedWindowFunction =
73
        new NamedExpression(node.getName(), windowFunction, node.getAlias());
74
    List<Pair<SortOption, Expression>> allSortItems = windowDefinition.getAllSortItems();
75
76 1 1. visitAlias : negated conditional → KILLED
    if (allSortItems.isEmpty()) {
77 1 1. visitAlias : replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::visitAlias → KILLED
      return new LogicalWindow(child, namedWindowFunction, windowDefinition);
78
    }
79 1 1. visitAlias : replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::visitAlias → KILLED
    return new LogicalWindow(
80
        new LogicalSort(child, allSortItems),
81
        namedWindowFunction,
82
        windowDefinition);
83
  }
84
85
  private List<Expression> analyzePartitionList(WindowFunction node, AnalysisContext context) {
86 1 1. analyzePartitionList : replaced return value with Collections.emptyList for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyzePartitionList → KILLED
    return node.getPartitionByList()
87
               .stream()
88 1 1. lambda$analyzePartitionList$0 : replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::lambda$analyzePartitionList$0 → KILLED
               .map(expr -> expressionAnalyzer.analyze(expr, context))
89
               .collect(Collectors.toList());
90
  }
91
92
  private List<Pair<SortOption, Expression>> analyzeSortList(WindowFunction node,
93
                                                             AnalysisContext context) {
94 1 1. analyzeSortList : replaced return value with Collections.emptyList for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyzeSortList → KILLED
    return node.getSortList()
95
               .stream()
96 1 1. lambda$analyzeSortList$1 : replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::lambda$analyzeSortList$1 → KILLED
               .map(pair -> ImmutablePair
97
                   .of(analyzeSortOption(pair.getLeft()),
98
                       expressionAnalyzer.analyze(pair.getRight(), context)))
99
               .collect(Collectors.toList());
100
  }
101
102
  /**
103
   * Frontend creates sort option from query directly which means sort or null order may be null.
104
   * The final and default value for each is determined here during expression analysis.
105
   */
106
  private SortOption analyzeSortOption(SortOption option) {
107 1 1. analyzeSortOption : negated conditional → KILLED
    if (option.getNullOrder() == null) {
108 2 1. analyzeSortOption : negated conditional → KILLED
2. analyzeSortOption : replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyzeSortOption → KILLED
      return (option.getSortOrder() == DESC) ? DEFAULT_DESC : DEFAULT_ASC;
109
    }
110 1 1. analyzeSortOption : replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyzeSortOption → KILLED
    return new SortOption(
111 1 1. analyzeSortOption : negated conditional → KILLED
        (option.getSortOrder() == DESC) ? DESC : ASC,
112
        option.getNullOrder());
113
  }
114
115
}

Mutations

57

1.1
Location : analyze
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:should_return_original_child_if_project_item_not_windowed()]
negated conditional → KILLED

2.2
Location : analyze
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:should_return_original_child_if_project_item_not_windowed()]
replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyze → KILLED

62

1.1
Location : visitAlias
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:should_return_original_child_if_project_item_not_windowed()]
negated conditional → KILLED

76

1.1
Location : visitAlias
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:should_not_generate_sort_operator_if_no_partition_by_and_order_by_list()]
negated conditional → KILLED

77

1.1
Location : visitAlias
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:should_not_generate_sort_operator_if_no_partition_by_and_order_by_list()]
replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::visitAlias → KILLED

79

1.1
Location : visitAlias
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:can_analyze_sort_options()]
replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::visitAlias → KILLED

86

1.1
Location : analyzePartitionList
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:should_wrap_child_with_window_and_sort_operator_if_project_item_windowed()]
replaced return value with Collections.emptyList for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyzePartitionList → KILLED

88

1.1
Location : lambda$analyzePartitionList$0
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:should_wrap_child_with_window_and_sort_operator_if_project_item_windowed()]
replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::lambda$analyzePartitionList$0 → KILLED

94

1.1
Location : analyzeSortList
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:can_analyze_sort_options()]
replaced return value with Collections.emptyList for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyzeSortList → KILLED

96

1.1
Location : lambda$analyzeSortList$1
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:can_analyze_sort_options()]
replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::lambda$analyzeSortList$1 → KILLED

107

1.1
Location : analyzeSortOption
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:can_analyze_sort_options()]
negated conditional → KILLED

108

1.1
Location : analyzeSortOption
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:can_analyze_sort_options()]
negated conditional → KILLED

2.2
Location : analyzeSortOption
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:can_analyze_sort_options()]
replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyzeSortOption → KILLED

110

1.1
Location : analyzeSortOption
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:can_analyze_sort_options()]
replaced return value with null for org/opensearch/sql/analysis/WindowExpressionAnalyzer::analyzeSortOption → KILLED

111

1.1
Location : analyzeSortOption
Killed by : org.opensearch.sql.analysis.WindowExpressionAnalyzerTest.[engine:junit-jupiter]/[class:org.opensearch.sql.analysis.WindowExpressionAnalyzerTest]/[method:can_analyze_sort_options()]
negated conditional → KILLED

Active mutators

Tests examined


Report generated by PIT 1.9.0