Skip to content

Commit f0a290e

Browse files
authored
Merge branch 'main' into planner-phase-metrics
2 parents d2eba5a + db68e20 commit f0a290e

35 files changed

+854
-375
lines changed

.github/workflows/nightly.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ jobs:
3333
- name: Run Gradle Test
3434
uses: ./actions/gradle-test
3535
with:
36+
gradle_command: test destructiveTest
3637
gradle_args: -PreleaseBuild=false -PpublishBuild=false
38+
# If you run this command as part of the above it complains about imlplicit dependencies, but running separately
39+
# is fine. We need to run it separately in pull_request, because we're combining different jobs
40+
- name: Run JaCoCo Report
41+
uses: ./actions/run-gradle
42+
with:
43+
gradle_command: codeCoverageReport
3744
- name: Upload coverage to teamscale
3845
# temporary until we validate that this is working correctly
3946
continue-on-error: true
@@ -61,6 +68,12 @@ jobs:
6168
with:
6269
gradle_command: mixedModeTest
6370
gradle_args: -PreleaseBuild=false -PpublishBuild=false
71+
# If you run this command as part of the above it complains about imlplicit dependencies, but running separately
72+
# is fine. We need to run it separately in pull_request, because we're combining different jobs
73+
- name: Run JaCoCo Report
74+
uses: ./actions/run-gradle
75+
with:
76+
gradle_command: codeCoverageReport
6477
# We don't commit the incremented version, but we use this to know the version when generating
6578
# the resulting markdown
6679
- name: Increment version
@@ -100,7 +113,14 @@ jobs:
100113
- name: Run Gradle Test
101114
uses: ./actions/gradle-test
102115
with:
116+
gradle_command: test destructiveTest
103117
gradle_args: "-PreleaseBuild=false -PpublishBuild=false -PspotbugsEnableHtmlReport -Ptests.includeRandom -Ptests.iterations=2 -Ptests.nightly"
118+
# If you run this command as part of the above it complains about imlplicit dependencies, but running separately
119+
# is fine. We need to run it separately in pull_request, because we're combining different jobs
120+
- name: Run JaCoCo Report
121+
uses: ./actions/run-gradle
122+
with:
123+
gradle_command: codeCoverageReport
104124
- name: Publish Test Reports
105125
if: always()
106126
uses: actions/upload-artifact@v4.6.0

actions/teamscale-upload/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ runs:
2525
server: 'https://fdb.teamscale.io'
2626
project: 'foundationdb-fdb-record-layer'
2727
user: 'fdb-record-layer-build'
28-
partition: inputs.partition
28+
partition: ${{ inputs.partition }}
2929
accesskey: ${{ inputs.teamscaleKey }}
3030
format: 'JACOCO'
3131
revision: ${{ inputs.revision }}

docs/sphinx/source/ReleaseNotes.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,77 @@ As the [versioning guide](Versioning.md) details, it cannot always be determined
77

88
## 4.5
99

10+
### 4.5.13.0
11+
12+
<h4> New Features </h4>
13+
14+
* Support SQL array subscript operator - [PR #3586](https://github.com/FoundationDB/fdb-record-layer/pull/3586)
15+
<h4> Bug Fixes </h4>
16+
17+
* Fix compensation logic to not early out if child compensations are needed - [PR #3593](https://github.com/FoundationDB/fdb-record-layer/pull/3593)
18+
19+
<details>
20+
<summary>
21+
22+
<h4> Build/Test/Documentation/Style Improvements (click to expand) </h4>
23+
24+
</summary>
25+
26+
* Run Jupiter tests in relational-core, not just auto-test - [PR #3590](https://github.com/FoundationDB/fdb-record-layer/pull/3590)
27+
28+
</details>
29+
30+
31+
**[Full Changelog (4.5.12.0...4.5.13.0)](https://github.com/FoundationDB/fdb-record-layer/compare/4.5.12.0...4.5.13.0)**
32+
33+
#### Mixed Mode Test Results
34+
35+
Mixed mode testing run against the following previous versions:
36+
37+
`4.5.2.0`, ❌`4.5.3.0`, ❌`4.5.4.0`, ❌`4.5.5.0`, ❌`4.5.6.0`, ❌`4.5.7.0`, ❌`4.5.8.0`, ❌`4.5.9.0`, ❌`4.5.10.0`, ✅`4.5.12.0`
38+
39+
[See full test run](https://github.com/FoundationDB/fdb-record-layer/actions/runs/17678739498)
40+
41+
42+
43+
### 4.5.12.0
44+
45+
<h4> New Features </h4>
46+
47+
* Composite aggregates - [PR #3266](https://github.com/FoundationDB/fdb-record-layer/pull/3266)
48+
49+
<details>
50+
<summary>
51+
52+
<h4> Build/Test/Documentation/Style Improvements (click to expand) </h4>
53+
54+
</summary>
55+
56+
* Remove release notes for 4.5.11.0 - [PR #3585](https://github.com/FoundationDB/fdb-record-layer/pull/3585)
57+
* Add codeCoverageReport task to nightly run jobs - [PR #3587](https://github.com/FoundationDB/fdb-record-layer/pull/3587)
58+
* teamscale upload action incorrectly referenced the partition input - [PR #3582](https://github.com/FoundationDB/fdb-record-layer/pull/3582)
59+
* Checkout main during teamscale upload workflow - [PR #3581](https://github.com/FoundationDB/fdb-record-layer/pull/3581)
60+
* Do not mark nightly gradle test as release or publish build - [PR #3580](https://github.com/FoundationDB/fdb-record-layer/pull/3580)
61+
* Fix the indentation in the teamscale upload action. - [PR #3579](https://github.com/FoundationDB/fdb-record-layer/pull/3579)
62+
* When correcting yamsql metrics, only update queries with differences - [PR #3563](https://github.com/FoundationDB/fdb-record-layer/pull/3563)
63+
* Re enable auto test - [PR #3558](https://github.com/FoundationDB/fdb-record-layer/pull/3558)
64+
* Upload coverage from nightly builds - [PR #3568](https://github.com/FoundationDB/fdb-record-layer/pull/3568)
65+
66+
</details>
67+
68+
69+
**[Full Changelog (4.5.10.0...4.5.12.0)](https://github.com/FoundationDB/fdb-record-layer/compare/4.5.10.0...4.5.12.0)**
70+
71+
#### Mixed Mode Test Results
72+
73+
Mixed mode testing run against the following previous versions:
74+
75+
`4.5.1.0`, ✅`4.5.2.0`, ✅`4.5.3.0`, ✅`4.5.4.0`, ✅`4.5.5.0`, ✅`4.5.6.0`, ✅`4.5.7.0`, ✅`4.5.8.0`, ✅`4.5.9.0`, ✅`4.5.10.0`
76+
77+
[See full test run](https://github.com/FoundationDB/fdb-record-layer/actions/runs/17640520208)
78+
79+
80+
1081
### 4.5.10.0
1182

1283
<h4> New Features </h4>

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/expressions/GroupByExpression.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,8 @@ public Compensation compensate(@Nonnull final PartialMatch partialMatch,
652652
return Compensation.impossibleCompensation();
653653
}
654654
final var compensatedResult = compensatedResultOptional.get();
655-
if (!compensatedResult.getResultCompensationFunction().isNeeded()) {
655+
if (!childCompensation.isNeeded() &&
656+
!compensatedResult.getResultCompensationFunction().isNeeded()) {
656657
return Compensation.noCompensation();
657658
}
658659

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/expressions/LogicalTypeFilterExpression.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ public Compensation compensate(@Nonnull final PartialMatch partialMatch,
205205
return Compensation.impossibleCompensation();
206206
}
207207
final var compensatedResult = compensatedResultOptional.get();
208-
if (!compensatedResult.getResultCompensationFunction().isNeeded()) {
208+
if (!childCompensation.isNeeded() &&
209+
!compensatedResult.getResultCompensationFunction().isNeeded()) {
209210
return Compensation.noCompensation();
210211
}
211212

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/expressions/SelectExpression.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,8 @@ public Compensation compensate(@Nonnull final PartialMatch partialMatch,
880880
isAnyCompensationFunctionImpossible |= compensatedResult.isCompensationImpossible();
881881

882882
final var isCompensationNeeded =
883-
!unmatchedQuantifiers.isEmpty() ||
883+
childCompensation.isNeeded() ||
884+
!unmatchedQuantifiers.isEmpty() ||
884885
isAnyCompensationFunctionNeeded ||
885886
compensatedResult.getResultCompensationFunction().isNeeded();
886887

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* SubscriptValue.java
3+
*
4+
* This source file is part of the FoundationDB open source project
5+
*
6+
* Copyright 2015-2025 Apple Inc. and the FoundationDB project authors
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package com.apple.foundationdb.record.query.plan.cascades.values;
22+
23+
import com.apple.foundationdb.record.EvaluationContext;
24+
import com.apple.foundationdb.record.ObjectPlanHash;
25+
import com.apple.foundationdb.record.PlanDeserializer;
26+
import com.apple.foundationdb.record.PlanHashable;
27+
import com.apple.foundationdb.record.PlanSerializationContext;
28+
import com.apple.foundationdb.record.planprotos.PSubscriptValue;
29+
import com.apple.foundationdb.record.planprotos.PValue;
30+
import com.apple.foundationdb.record.provider.foundationdb.FDBRecordStoreBase;
31+
import com.apple.foundationdb.record.query.plan.cascades.BuiltInFunction;
32+
import com.apple.foundationdb.record.query.plan.cascades.SemanticException;
33+
import com.apple.foundationdb.record.query.plan.cascades.typing.Type;
34+
import com.apple.foundationdb.record.query.plan.cascades.typing.Typed;
35+
import com.apple.foundationdb.record.query.plan.explain.ExplainTokensWithPrecedence;
36+
import com.google.auto.service.AutoService;
37+
import com.google.common.base.Verify;
38+
import com.google.common.collect.Iterables;
39+
import com.google.protobuf.Message;
40+
41+
import javax.annotation.Nonnull;
42+
import javax.annotation.Nullable;
43+
import java.util.List;
44+
import java.util.function.Supplier;
45+
46+
public class SubscriptValue extends AbstractValue {
47+
private static final ObjectPlanHash BASE_HASH = new ObjectPlanHash("Subscript-Value");
48+
49+
@Nonnull
50+
private final Value indexValue;
51+
52+
@Nonnull
53+
private final Value sourceValue;
54+
55+
@Nonnull
56+
private final Type type;
57+
58+
public SubscriptValue(@Nonnull final Value indexValue,
59+
@Nonnull final Value sourceValue) {
60+
this.indexValue = indexValue;
61+
this.sourceValue = sourceValue;
62+
this.type = Verify.verifyNotNull(((Type.Array)sourceValue.getResultType()).getElementType()).nullable();
63+
}
64+
65+
@Nonnull
66+
@Override
67+
protected Iterable<? extends Value> computeChildren() {
68+
return List.of(indexValue, sourceValue);
69+
}
70+
71+
@Nonnull
72+
@Override
73+
public ExplainTokensWithPrecedence explain(@Nonnull final Iterable<Supplier<ExplainTokensWithPrecedence>> explainSuppliers) {
74+
final var indexValueExplain = Iterables.get(explainSuppliers, 0).get();
75+
final var sourceValueExplain = Iterables.get(explainSuppliers, 1).get();
76+
return ExplainTokensWithPrecedence.of(sourceValueExplain.getExplainTokens()
77+
.addOpeningSquareBracket().addOptionalWhitespace()
78+
.addNested(indexValueExplain.getExplainTokens())
79+
.addClosingAngledBracket());
80+
}
81+
82+
@Nullable
83+
@Override
84+
public <M extends Message> Object eval(@Nullable final FDBRecordStoreBase<M> store, @Nonnull final EvaluationContext context) {
85+
final var index = indexValue.eval(store, context);
86+
if (index == null) {
87+
return null;
88+
}
89+
final var source = sourceValue.eval(store, context);
90+
if (source == null) {
91+
return null;
92+
}
93+
// index is 1-based as defined in SQL standard (Foundation, Section: 4.10.2):
94+
// > An array is a collection A in which each element is associated with exactly one ordinal position in A.
95+
// If n is the cardinality of A, then the ordinal position p of an element is an integer in the range 1 (one)
96+
// ≤ p ≤ n.
97+
final var sourceAsList = (List<?>)source;
98+
final var adjustedIndex = (int)index - 1;
99+
if (adjustedIndex < 0 || adjustedIndex >= sourceAsList.size()) {
100+
// this does not raise out-of-bound error.
101+
return null;
102+
}
103+
return sourceAsList.get(adjustedIndex);
104+
}
105+
106+
@Nonnull
107+
@Override
108+
public Type getResultType() {
109+
return type;
110+
}
111+
112+
@Override
113+
public int hashCodeWithoutChildren() {
114+
return PlanHashable.objectsPlanHash(PlanHashable.CURRENT_FOR_CONTINUATION, BASE_HASH);
115+
}
116+
117+
@Nonnull
118+
@Override
119+
public PValue toValueProto(@Nonnull final PlanSerializationContext serializationContext) {
120+
return PValue.newBuilder()
121+
.setSubscriptValue(toProto(serializationContext))
122+
.build();
123+
}
124+
125+
@Override
126+
public int planHash(@Nonnull final PlanHashMode hashMode) {
127+
return PlanHashable.objectsPlanHash(hashMode, BASE_HASH);
128+
}
129+
130+
@Nonnull
131+
@Override
132+
public PSubscriptValue toProto(@Nonnull final PlanSerializationContext serializationContext) {
133+
return PSubscriptValue.newBuilder()
134+
.setIndex(indexValue.toValueProto(serializationContext))
135+
.setSource(sourceValue.toValueProto(serializationContext))
136+
.build();
137+
}
138+
139+
@Nonnull
140+
public static SubscriptValue fromProto(@Nonnull final PlanSerializationContext serializationContext,
141+
@Nonnull final PSubscriptValue subscriptValueProto) {
142+
return new SubscriptValue(Value.fromValueProto(serializationContext, subscriptValueProto.getIndex()),
143+
Value.fromValueProto(serializationContext, subscriptValueProto.getSource()));
144+
}
145+
146+
@Nonnull
147+
@Override
148+
@SuppressWarnings("PMD.CompareObjectsWithEquals")
149+
public Value withChildren(final Iterable<? extends Value> newChildren) {
150+
Verify.verify(Iterables.size(newChildren) == 2);
151+
final var newIndexValue = Iterables.get(newChildren, 0);
152+
final var newSourceValue = Iterables.get(newChildren, 1);
153+
if (indexValue == newIndexValue && newSourceValue == sourceValue) {
154+
return this;
155+
}
156+
return new SubscriptValue(newIndexValue, newSourceValue);
157+
}
158+
159+
/**
160+
* Deserializer.
161+
*/
162+
@AutoService(PlanDeserializer.class)
163+
public static class Deserializer implements PlanDeserializer<PSubscriptValue, SubscriptValue> {
164+
@Nonnull
165+
@Override
166+
public Class<PSubscriptValue> getProtoMessageClass() {
167+
return PSubscriptValue.class;
168+
}
169+
170+
@Nonnull
171+
@Override
172+
public SubscriptValue fromProto(@Nonnull final PlanSerializationContext serializationContext,
173+
@Nonnull final PSubscriptValue subscriptValueProto) {
174+
return SubscriptValue.fromProto(serializationContext, subscriptValueProto);
175+
}
176+
}
177+
178+
/**
179+
* The {@code subscript} function.
180+
*/
181+
@AutoService(BuiltInFunction.class)
182+
public static class SubscriptValueFn extends BuiltInFunction<Value> {
183+
public SubscriptValueFn() {
184+
super("subscript", List.of(Type.primitiveType(Type.TypeCode.INT), Type.any()), Type.any(), SubscriptValue.SubscriptValueFn::encapsulate);
185+
}
186+
187+
@SuppressWarnings({"PMD.UnusedFormalParameter", "PMD.UnusedPrivateMethod"}) // false positive, method is used
188+
private static Value encapsulate(@Nonnull BuiltInFunction<Value> ignored,
189+
@Nonnull final List<? extends Typed> arguments) {
190+
Verify.verify(arguments.size() == 2);
191+
var indexValue = (Value)arguments.get(0);
192+
final var indexMaxType = Type.maximumType(indexValue.getResultType(), Type.primitiveType(Type.TypeCode.INT));
193+
SemanticException.check(indexMaxType != null, SemanticException.ErrorCode.INCOMPATIBLE_TYPE);
194+
indexValue = PromoteValue.inject(indexValue, indexMaxType);
195+
196+
var sourceValue = (Value)arguments.get(1);
197+
Verify.verify(sourceValue.getResultType().isArray());
198+
199+
return new SubscriptValue(indexValue, sourceValue);
200+
}
201+
}
202+
}

fdb-record-layer-core/src/main/proto/record_query_plan.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ message PValue {
255255
PFirstOrDefaultStreamingValue first_or_default_streaming_value = 49;
256256
PEvaluatesToValue evaluates_to_value = 50;
257257
PArrayDistinctValue array_distinct_value = 51;
258+
PSubscriptValue subscript_value = 52;
258259
}
259260
}
260261

@@ -457,6 +458,11 @@ message PEvaluatesToValue {
457458
optional PEvaluation evaluation = 2;
458459
}
459460

461+
message PSubscriptValue {
462+
optional PValue index = 1;
463+
optional PValue source = 2;
464+
}
465+
460466
message PFieldValue {
461467
optional PValue child_value = 1;
462468
optional PFieldPath field_path = 2;

fdb-relational-core/fdb-relational-core.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ dependencies {
6767
// task should be moved to testing.gradle
6868
tasks.withType(Test).configureEach { theTask ->
6969
theTask.testFramework.options.includeEngines.add('auto-test')
70+
theTask.testFramework.options.includeEngines.add('junit-jupiter')
7071
}
7172

7273
publishing {

0 commit comments

Comments
 (0)