1 | /* | |
2 | * Copyright OpenSearch Contributors | |
3 | * SPDX-License-Identifier: Apache-2.0 | |
4 | */ | |
5 | ||
6 | ||
7 | package org.opensearch.sql.analysis.symbol; | |
8 | ||
9 | import static java.util.Collections.emptyMap; | |
10 | import static java.util.Collections.emptyNavigableMap; | |
11 | ||
12 | import java.util.EnumMap; | |
13 | import java.util.LinkedHashMap; | |
14 | import java.util.Map; | |
15 | import java.util.NavigableMap; | |
16 | import java.util.Optional; | |
17 | import java.util.TreeMap; | |
18 | import org.opensearch.sql.data.type.ExprType; | |
19 | ||
20 | /** | |
21 | * Symbol table for symbol definition and resolution. | |
22 | */ | |
23 | public class SymbolTable { | |
24 | ||
25 | /** | |
26 | * Two-dimension hash table to manage symbols with type in different namespace. | |
27 | */ | |
28 | private Map<Namespace, NavigableMap<String, ExprType>> tableByNamespace = | |
29 | new EnumMap<>(Namespace.class); | |
30 | ||
31 | /** | |
32 | * Two-dimension hash table to manage symbols with type in different namespace. | |
33 | * Comparing with tableByNamespace, orderedTable use the LinkedHashMap to keep the order of | |
34 | * symbol. | |
35 | */ | |
36 | private Map<Namespace, LinkedHashMap<String, ExprType>> orderedTable = | |
37 | new EnumMap<>(Namespace.class); | |
38 | ||
39 | /** | |
40 | * Store symbol with the type. Create new map for namespace for the first time. | |
41 | * | |
42 | * @param symbol symbol to define | |
43 | * @param type symbol type | |
44 | */ | |
45 | public void store(Symbol symbol, ExprType type) { | |
46 | tableByNamespace.computeIfAbsent( | |
47 | symbol.getNamespace(), | |
48 |
1
1. lambda$store$0 : replaced return value with null for org/opensearch/sql/analysis/symbol/SymbolTable::lambda$store$0 → KILLED |
ns -> new TreeMap<>() |
49 | ).put(symbol.getName(), type); | |
50 | ||
51 | orderedTable.computeIfAbsent( | |
52 | symbol.getNamespace(), | |
53 |
1
1. lambda$store$1 : replaced return value with null for org/opensearch/sql/analysis/symbol/SymbolTable::lambda$store$1 → KILLED |
ns -> new LinkedHashMap<>() |
54 | ).put(symbol.getName(), type); | |
55 | } | |
56 | ||
57 | /** | |
58 | * Remove a symbol from SymbolTable. | |
59 | */ | |
60 | public void remove(Symbol symbol) { | |
61 | tableByNamespace.computeIfPresent( | |
62 | symbol.getNamespace(), | |
63 | (k, v) -> { | |
64 | v.remove(symbol.getName()); | |
65 |
1
1. lambda$remove$2 : replaced return value with null for org/opensearch/sql/analysis/symbol/SymbolTable::lambda$remove$2 → SURVIVED |
return v; |
66 | } | |
67 | ); | |
68 | orderedTable.computeIfPresent( | |
69 | symbol.getNamespace(), | |
70 | (k, v) -> { | |
71 | v.remove(symbol.getName()); | |
72 |
1
1. lambda$remove$3 : replaced return value with null for org/opensearch/sql/analysis/symbol/SymbolTable::lambda$remove$3 → KILLED |
return v; |
73 | } | |
74 | ); | |
75 | } | |
76 | ||
77 | /** | |
78 | * Look up symbol in the namespace map. | |
79 | * | |
80 | * @param symbol symbol to look up | |
81 | * @return symbol type which is optional | |
82 | */ | |
83 | public Optional<ExprType> lookup(Symbol symbol) { | |
84 | Map<String, ExprType> table = tableByNamespace.get(symbol.getNamespace()); | |
85 | ExprType type = null; | |
86 |
1
1. lookup : negated conditional → KILLED |
if (table != null) { |
87 | type = table.get(symbol.getName()); | |
88 | } | |
89 |
1
1. lookup : replaced return value with Optional.empty for org/opensearch/sql/analysis/symbol/SymbolTable::lookup → KILLED |
return Optional.ofNullable(type); |
90 | } | |
91 | ||
92 | /** | |
93 | * Look up symbols by a prefix. | |
94 | * | |
95 | * @param prefix a symbol prefix | |
96 | * @return symbols starting with the prefix | |
97 | */ | |
98 | public Map<String, ExprType> lookupByPrefix(Symbol prefix) { | |
99 | NavigableMap<String, ExprType> table = tableByNamespace.get(prefix.getNamespace()); | |
100 |
1
1. lookupByPrefix : negated conditional → KILLED |
if (table != null) { |
101 |
1
1. lookupByPrefix : replaced return value with Collections.emptyMap for org/opensearch/sql/analysis/symbol/SymbolTable::lookupByPrefix → KILLED |
return table.subMap(prefix.getName(), prefix.getName() + Character.MAX_VALUE); |
102 | } | |
103 | return emptyMap(); | |
104 | } | |
105 | ||
106 | /** | |
107 | * Look up all top level symbols in the namespace. | |
108 | * this function is mainly used by SELECT * use case to get the top level fields | |
109 | * Todo. currently, the top level fields is the field which doesn't include "." in the name or | |
110 | * the prefix doesn't exist in the symbol table. | |
111 | * e.g. The symbol table includes person, person.name, person/2.0. | |
112 | * person, is the top level field | |
113 | * person.name, isn't the top level field, because the prefix (person) in symbol table | |
114 | * person/2.0, is the top level field, because the prefix (person/2) isn't in symbol table | |
115 | * | |
116 | * @param namespace a namespace | |
117 | * @return all symbols in the namespace map | |
118 | */ | |
119 | public Map<String, ExprType> lookupAllFields(Namespace namespace) { | |
120 | final LinkedHashMap<String, ExprType> allSymbols = | |
121 | orderedTable.getOrDefault(namespace, new LinkedHashMap<>()); | |
122 | final LinkedHashMap<String, ExprType> results = new LinkedHashMap<>(); | |
123 | allSymbols.entrySet().stream().filter(entry -> { | |
124 | String symbolName = entry.getKey(); | |
125 | int lastDot = symbolName.lastIndexOf("."); | |
126 |
3
1. lambda$lookupAllFields$4 : negated conditional → KILLED 2. lambda$lookupAllFields$4 : negated conditional → KILLED 3. lambda$lookupAllFields$4 : replaced boolean return with true for org/opensearch/sql/analysis/symbol/SymbolTable::lambda$lookupAllFields$4 → KILLED |
return -1 == lastDot || !allSymbols.containsKey(symbolName.substring(0, lastDot)); |
127 |
1
1. lookupAllFields : removed call to java/util/stream/Stream::forEach → KILLED |
}).forEach(entry -> results.put(entry.getKey(), entry.getValue())); |
128 |
1
1. lookupAllFields : replaced return value with Collections.emptyMap for org/opensearch/sql/analysis/symbol/SymbolTable::lookupAllFields → KILLED |
return results; |
129 | } | |
130 | ||
131 | /** | |
132 | * Check if namespace map in empty (none definition). | |
133 | * | |
134 | * @param namespace a namespace | |
135 | * @return true for empty | |
136 | */ | |
137 | public boolean isEmpty(Namespace namespace) { | |
138 |
2
1. isEmpty : replaced boolean return with true for org/opensearch/sql/analysis/symbol/SymbolTable::isEmpty → SURVIVED 2. isEmpty : replaced boolean return with false for org/opensearch/sql/analysis/symbol/SymbolTable::isEmpty → KILLED |
return tableByNamespace.getOrDefault(namespace, emptyNavigableMap()).isEmpty(); |
139 | } | |
140 | } | |
Mutations | ||
48 |
1.1 |
|
53 |
1.1 |
|
65 |
1.1 |
|
72 |
1.1 |
|
86 |
1.1 |
|
89 |
1.1 |
|
100 |
1.1 |
|
101 |
1.1 |
|
126 |
1.1 2.2 3.3 |
|
127 |
1.1 |
|
128 |
1.1 |
|
138 |
1.1 2.2 |