Skip to content

Commit d92d467

Browse files
committed
Merge remote-tracking branch 'upstream/main' into enable-entitlements
2 parents 4be13e2 + ad99b0d commit d92d467

File tree

372 files changed

+9073
-8839
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

372 files changed

+9073
-8839
lines changed

benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111

1212
import org.apache.lucene.util.BytesRef;
1313
import org.elasticsearch.common.breaker.NoopCircuitBreaker;
14+
import org.elasticsearch.common.settings.Settings;
1415
import org.elasticsearch.common.util.BigArrays;
1516
import org.elasticsearch.compute.data.Block;
1617
import org.elasticsearch.compute.data.BlockFactory;
1718
import org.elasticsearch.compute.data.BooleanBlock;
1819
import org.elasticsearch.compute.data.BooleanVector;
20+
import org.elasticsearch.compute.data.BytesRefBlock;
21+
import org.elasticsearch.compute.data.BytesRefVector;
1922
import org.elasticsearch.compute.data.DoubleBlock;
2023
import org.elasticsearch.compute.data.DoubleVector;
2124
import org.elasticsearch.compute.data.LongBlock;
@@ -40,9 +43,13 @@
4043
import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMin;
4144
import org.elasticsearch.xpack.esql.expression.function.scalar.nulls.Coalesce;
4245
import org.elasticsearch.xpack.esql.expression.function.scalar.string.RLike;
46+
import org.elasticsearch.xpack.esql.expression.function.scalar.string.ToLower;
47+
import org.elasticsearch.xpack.esql.expression.function.scalar.string.ToUpper;
4348
import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Add;
4449
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.Equals;
4550
import org.elasticsearch.xpack.esql.planner.Layout;
51+
import org.elasticsearch.xpack.esql.plugin.EsqlPlugin;
52+
import org.elasticsearch.xpack.esql.session.Configuration;
4653
import org.openjdk.jmh.annotations.Benchmark;
4754
import org.openjdk.jmh.annotations.BenchmarkMode;
4855
import org.openjdk.jmh.annotations.Fork;
@@ -56,8 +63,10 @@
5663
import org.openjdk.jmh.annotations.Warmup;
5764

5865
import java.time.Duration;
66+
import java.time.ZoneOffset;
5967
import java.util.Arrays;
6068
import java.util.List;
69+
import java.util.Locale;
6170
import java.util.Map;
6271
import java.util.concurrent.TimeUnit;
6372

@@ -106,7 +115,9 @@ public class EvalBenchmark {
106115
"long_equal_to_int",
107116
"mv_min",
108117
"mv_min_ascending",
109-
"rlike" }
118+
"rlike",
119+
"to_lower",
120+
"to_upper" }
110121
)
111122
public String operation;
112123

@@ -169,7 +180,7 @@ private static EvalOperator.ExpressionEvaluator evaluator(String operation) {
169180
new Coalesce(Source.EMPTY, lhs, List.of(f2)),
170181
layout(f1, f2)
171182
).get(driverContext);
172-
String desc = operation.endsWith("lazy") ? "CoalesceLazyEvaluator" : "CoalesceEagerEvaluator";
183+
String desc = operation.endsWith("lazy") ? "CoalesceLongLazyEvaluator" : "CoalesceLongEagerEvaluator";
173184
if (evaluator.toString().contains(desc) == false) {
174185
throw new IllegalArgumentException("Evaluator was [" + evaluator + "] but expected one containing [" + desc + "]");
175186
}
@@ -214,6 +225,16 @@ private static EvalOperator.ExpressionEvaluator evaluator(String operation) {
214225
RLike rlike = new RLike(Source.EMPTY, keywordField, new RLikePattern(".ar"));
215226
yield EvalMapper.toEvaluator(FOLD_CONTEXT, rlike, layout(keywordField)).get(driverContext);
216227
}
228+
case "to_lower" -> {
229+
FieldAttribute keywordField = keywordField();
230+
ToLower toLower = new ToLower(Source.EMPTY, keywordField, configuration());
231+
yield EvalMapper.toEvaluator(FOLD_CONTEXT, toLower, layout(keywordField)).get(driverContext);
232+
}
233+
case "to_upper" -> {
234+
FieldAttribute keywordField = keywordField();
235+
ToUpper toUpper = new ToUpper(Source.EMPTY, keywordField, configuration());
236+
yield EvalMapper.toEvaluator(FOLD_CONTEXT, toUpper, layout(keywordField)).get(driverContext);
237+
}
217238
default -> throw new UnsupportedOperationException();
218239
};
219240
}
@@ -234,6 +255,23 @@ private static FieldAttribute keywordField() {
234255
return new FieldAttribute(Source.EMPTY, "keyword", new EsField("keyword", DataType.KEYWORD, Map.of(), true));
235256
}
236257

258+
private static Configuration configuration() {
259+
return new Configuration(
260+
ZoneOffset.UTC,
261+
Locale.ROOT,
262+
null,
263+
null,
264+
null,
265+
EsqlPlugin.QUERY_RESULT_TRUNCATION_MAX_SIZE.get(Settings.EMPTY),
266+
EsqlPlugin.QUERY_RESULT_TRUNCATION_DEFAULT_SIZE.get(Settings.EMPTY),
267+
null,
268+
false,
269+
Map.of(),
270+
0,
271+
false
272+
);
273+
}
274+
237275
private static Layout layout(FieldAttribute... fields) {
238276
Layout.Builder layout = new Layout.Builder();
239277
layout.append(Arrays.asList(fields));
@@ -366,10 +404,24 @@ private static void checkExpected(String operation, Page actual) {
366404
}
367405
}
368406
}
407+
case "to_lower" -> checkBytes(operation, actual, new BytesRef[] { new BytesRef("foo"), new BytesRef("bar") });
408+
case "to_upper" -> checkBytes(operation, actual, new BytesRef[] { new BytesRef("FOO"), new BytesRef("BAR") });
369409
default -> throw new UnsupportedOperationException(operation);
370410
}
371411
}
372412

413+
private static void checkBytes(String operation, Page actual, BytesRef[] expectedVals) {
414+
BytesRef scratch = new BytesRef();
415+
BytesRefVector v = actual.<BytesRefBlock>getBlock(1).asVector();
416+
for (int i = 0; i < BLOCK_LENGTH; i++) {
417+
BytesRef expected = expectedVals[i % 2];
418+
BytesRef b = v.getBytesRef(i, scratch);
419+
if (b.equals(expected) == false) {
420+
throw new AssertionError("[" + operation + "] expected [" + expected + "] but was [" + b + "]");
421+
}
422+
}
423+
}
424+
373425
private static Page page(String operation) {
374426
return switch (operation) {
375427
case "abs", "add", "date_trunc", "equal_to_const" -> {
@@ -440,7 +492,7 @@ private static Page page(String operation) {
440492
}
441493
yield new Page(builder.build());
442494
}
443-
case "rlike" -> {
495+
case "rlike", "to_lower", "to_upper" -> {
444496
var builder = blockFactory.newBytesRefVectorBuilder(BLOCK_LENGTH);
445497
BytesRef[] values = new BytesRef[] { new BytesRef("foo"), new BytesRef("bar") };
446498
for (int i = 0; i < BLOCK_LENGTH; i++) {

docs/changelog/121920.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 121920
2+
summary: Account for the `SearchHit` source in circuit breaker
3+
area: Search
4+
type: enhancement
5+
issues:
6+
- 89656

docs/changelog/122821.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 122821
2+
summary: Fix functions emitting warnings with no source
3+
area: ES|QL
4+
type: bug
5+
issues:
6+
- 122588

docs/changelog/122860.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 122860
2+
summary: Improved error message when index field type is invalid
3+
area: Mapping
4+
type: enhancement
5+
issues: []

docs/changelog/123105.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 123105
2+
summary: fix stale data in synthetic source for string stored field
3+
area: Mapping
4+
type: bug
5+
issues:
6+
- 123110

docs/changelog/123197.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 123197
2+
summary: Fix early termination in `LuceneSourceOperator`
3+
area: ES|QL
4+
type: bug
5+
issues: []

docs/changelog/123246.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 123246
2+
summary: Deduplicate allocation stats calls
3+
area: Allocation
4+
type: bug
5+
issues: []

docs/changelog/123272.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 123272
2+
summary: Set Connect Timeout to 5s
3+
area: Machine Learning
4+
type: bug
5+
issues: []

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.nio.file.Paths;
2222
import java.util.ArrayList;
2323
import java.util.Arrays;
24+
import java.util.Comparator;
2425
import java.util.List;
2526
import java.util.Objects;
2627
import java.util.function.BiConsumer;
@@ -82,8 +83,8 @@ private FileAccessTree(FilesEntitlement filesEntitlement, PathLookup pathLookup)
8283
Path jdk = Paths.get(System.getProperty("java.home"));
8384
addPathAndMaybeLink.accept(jdk.resolve("conf"), Mode.READ);
8485

85-
readPaths.sort(String::compareTo);
86-
writePaths.sort(String::compareTo);
86+
readPaths.sort(PATH_ORDER);
87+
writePaths.sort(PATH_ORDER);
8788

8889
this.readPaths = pruneSortedPaths(readPaths).toArray(new String[0]);
8990
this.writePaths = pruneSortedPaths(writePaths).toArray(new String[0]);
@@ -96,7 +97,7 @@ private static List<String> pruneSortedPaths(List<String> paths) {
9697
prunedReadPaths.add(currentPath);
9798
for (int i = 1; i < paths.size(); ++i) {
9899
String nextPath = paths.get(i);
99-
if (nextPath.startsWith(currentPath) == false) {
100+
if (isParent(currentPath, nextPath) == false) {
100101
prunedReadPaths.add(nextPath);
101102
currentPath = nextPath;
102103
}
@@ -124,25 +125,28 @@ static String normalizePath(Path path) {
124125
// Note that toAbsolutePath produces paths separated by the default file separator,
125126
// so on Windows, if the given path uses forward slashes, this consistently
126127
// converts it to backslashes.
127-
return path.toAbsolutePath().normalize().toString();
128+
String result = path.toAbsolutePath().normalize().toString();
129+
while (result.endsWith(FILE_SEPARATOR)) {
130+
result = result.substring(0, result.length() - FILE_SEPARATOR.length());
131+
}
132+
return result;
128133
}
129134

130135
private static boolean checkPath(String path, String[] paths) {
131136
if (paths.length == 0) {
132137
return false;
133138
}
134-
int ndx = Arrays.binarySearch(paths, path);
139+
int ndx = Arrays.binarySearch(paths, path, PATH_ORDER);
135140
if (ndx < -1) {
136-
// logger.warn("Couldn't find path [{}] in paths:\n{}", path, Arrays.asList(paths));
137-
String maybeParent = paths[-ndx - 2];
138-
// logger.warn("Possible parent path: [{}]", maybeParent);
139-
// Normalization on Windows does not allways remove trailing backslashes, we need to check both patterns
140-
return path.startsWith(maybeParent)
141-
&& (maybeParent.endsWith(FILE_SEPARATOR) || path.startsWith(FILE_SEPARATOR, maybeParent.length()));
141+
return isParent(paths[-ndx - 2], path);
142142
}
143143
return ndx >= 0;
144144
}
145145

146+
private static boolean isParent(String maybeParent, String path) {
147+
return path.startsWith(maybeParent) && path.startsWith(FILE_SEPARATOR, maybeParent.length());
148+
}
149+
146150
@Override
147151
public boolean equals(Object o) {
148152
if (o == null || getClass() != o.getClass()) return false;
@@ -154,4 +158,30 @@ public boolean equals(Object o) {
154158
public int hashCode() {
155159
return Objects.hash(Arrays.hashCode(readPaths), Arrays.hashCode(writePaths));
156160
}
161+
162+
/**
163+
* For our lexicographic sort trick to work correctly, we must have path separators sort before
164+
* any other character so that files in a directory appear immediately after that directory.
165+
* For example, we require [/a, /a/b, /a.xml] rather than the natural order [/a, /a.xml, /a/b].
166+
*/
167+
private static final Comparator<String> PATH_ORDER = (s1, s2) -> {
168+
Path p1 = Path.of(s1);
169+
Path p2 = Path.of(s2);
170+
var i1 = p1.iterator();
171+
var i2 = p2.iterator();
172+
while (i1.hasNext() && i2.hasNext()) {
173+
int cmp = i1.next().compareTo(i2.next());
174+
if (cmp != 0) {
175+
return cmp;
176+
}
177+
}
178+
if (i1.hasNext()) {
179+
return 1;
180+
} else if (i2.hasNext()) {
181+
return -1;
182+
} else {
183+
assert p1.equals(p2);
184+
return 0;
185+
}
186+
};
157187
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.Objects;
2222
import java.util.stream.Stream;
2323

24+
import static java.lang.Character.isLetter;
25+
2426
/**
2527
* Describes a file entitlement with a path and mode.
2628
*/
@@ -90,6 +92,51 @@ static FileData ofPathSetting(String setting, Mode mode) {
9092
static FileData ofRelativePathSetting(String setting, BaseDir baseDir, Mode mode) {
9193
return new RelativePathSettingFileData(setting, baseDir, mode, null);
9294
}
95+
96+
/**
97+
* Tests if a path is absolute or relative, taking into consideration both Unix and Windows conventions.
98+
* Note that this leads to a conflict, resolved in favor of Unix rules: `/foo` can be either a Unix absolute path, or a Windows
99+
* relative path with "wrong" directory separator (using non-canonical slash in Windows).
100+
*/
101+
static boolean isAbsolutePath(String path) {
102+
if (path.isEmpty()) {
103+
return false;
104+
}
105+
if (path.charAt(0) == '/') {
106+
// Unix/BSD absolute
107+
return true;
108+
}
109+
110+
return isWindowsAbsolutePath(path);
111+
}
112+
113+
private static boolean isSlash(char c) {
114+
return (c == '\\') || (c == '/');
115+
}
116+
117+
private static boolean isWindowsAbsolutePath(String input) {
118+
// if a prefix is present, we expected (long) UNC or (long) absolute
119+
if (input.startsWith("\\\\?\\")) {
120+
return true;
121+
}
122+
123+
if (input.length() > 1) {
124+
char c0 = input.charAt(0);
125+
char c1 = input.charAt(1);
126+
char c = 0;
127+
int next = 2;
128+
if (isSlash(c0) && isSlash(c1)) {
129+
// Two slashes or more: UNC
130+
return true;
131+
}
132+
if (isLetter(c0) && c1 == ':') {
133+
// A drive: absolute
134+
return true;
135+
}
136+
}
137+
// Otherwise relative
138+
return false;
139+
}
93140
}
94141

95142
private sealed interface RelativeFileData extends FileData {
@@ -284,13 +331,13 @@ public static FilesEntitlement build(List<Object> paths) {
284331
}
285332

286333
Path relativePath = Path.of(relativePathAsString);
287-
if (platform != null && platform.isCurrent() && relativePath.isAbsolute()) {
334+
if (platform != null && platform.isCurrent() && FileData.isAbsolutePath(relativePathAsString)) {
288335
throw new PolicyValidationException("'relative_path' [" + relativePathAsString + "] must be relative");
289336
}
290337
fileData = FileData.ofRelativePath(relativePath, baseDir, mode);
291338
} else if (pathAsString != null) {
292339
Path path = Path.of(pathAsString);
293-
if (platform != null && platform.isCurrent() && path.isAbsolute() == false) {
340+
if (platform != null && platform.isCurrent() && FileData.isAbsolutePath(pathAsString) == false) {
294341
throw new PolicyValidationException("'path' [" + pathAsString + "] must be absolute");
295342
}
296343
fileData = FileData.ofPath(path, mode);

0 commit comments

Comments
 (0)