diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java index c462fefabfd..7d3ce4863b2 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java @@ -110,7 +110,6 @@ import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.projectColumn; import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.resultColumn; import static com.apple.foundationdb.record.provider.foundationdb.query.FDBQueryGraphTestHelpers.sortExpression; -import static com.apple.foundationdb.record.query.plan.ScanComparisons.anyParameterComparison; import static com.apple.foundationdb.record.query.plan.ScanComparisons.anyValueComparison; import static com.apple.foundationdb.record.query.plan.ScanComparisons.equalities; import static com.apple.foundationdb.record.query.plan.ScanComparisons.range; @@ -123,12 +122,9 @@ import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyComparison; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.anyPredicate; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.notPredicate; -import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.QueryPredicateMatchers.valuePredicate; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.comparisonKey; -import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.comparisonKeyValues; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.coveringIndexPlan; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.descendantPlans; -import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.equalsInList; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.fetchFromPartialRecordPlan; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.filterPlan; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.inComparandJoinPlan; @@ -148,7 +144,6 @@ import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.indexPlan; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.indexPlanOf; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.intersectionOnExpressionPlan; -import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.intersectionOnValuesPlan; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.isNotReverse; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.isReverse; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.mapPlan; @@ -162,13 +157,11 @@ import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.unionOnExpressionPlan; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.unorderedPrimaryKeyDistinctPlan; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.RecordQueryPlanMatchers.unorderedUnionPlan; -import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.anyValue; import static com.apple.foundationdb.record.query.plan.cascades.matching.structure.ValueMatchers.fieldValueWithFieldNames; import static com.apple.foundationdb.record.query.plan.cascades.properties.UsedTypesProperty.usedTypes; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasSize; @@ -191,7 +184,9 @@ class FDBInQueryTest extends FDBRecordStoreQueryTestBase { /** * Verify that an IN without an index is implemented as a filter on a scan, as opposed to a loop of a filter on a scan. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryNoIndex() throws Exception { complexQuerySetup(NO_HOOK); final QueryComponent filter = Query.field("num_value_2").in(asList(0, 2)); @@ -203,21 +198,13 @@ void testInQueryNoIndex() throws Exception { // Scan(<,>) | [MySimpleRecord] | num_value_2 IN [0, 2] RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - filterPlan(descendantPlans(scanPlan().where(scanComparisons(unbounded())))) - .where(queryComponents(exactly(equalsObject(filter))))); + assertMatchesExactly(plan, + filterPlan(descendantPlans(scanPlan().where(scanComparisons(unbounded())))) + .where(queryComponents(exactly(equalsObject(filter))))); - assertEquals(-1139367278, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1691932867, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - predicatesFilterPlan(descendantPlans(scanPlan().where(scanComparisons(unbounded())))) - .where(predicates(valuePredicate(fieldValueWithFieldNames("num_value_2"), new Comparisons.ListComparison(Comparisons.Type.IN, ImmutableList.of(0, 2)))))); + assertEquals(-1139367278, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(1691932867, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - assertEquals(738091077, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-2062922437, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } assertEquals(67, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> assertThat(rec.getNumValue2(), anyOf(is(0), is(2))), context -> assertDiscardedAtMost(33, context))); @@ -226,7 +213,9 @@ void testInQueryNoIndex() throws Exception { /** * Verify that an IN (with parameter) without an index is implemented as a filter on a scan. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryNoIndexWithParameter() throws Exception { complexQuerySetup(NO_HOOK); final QueryComponent filter = Query.field("num_value_2").in("valuesThree"); @@ -237,21 +226,13 @@ void testInQueryNoIndexWithParameter() throws Exception { // Scan(<,>) | [MySimpleRecord] | num_value_2 IN $valuesThree RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - filterPlan(descendantPlans(scanPlan().where(scanComparisons(unbounded())))) - .where(queryComponents(exactly(equalsObject(filter))))); + assertMatchesExactly(plan, + filterPlan(descendantPlans(scanPlan().where(scanComparisons(unbounded())))) + .where(queryComponents(exactly(equalsObject(filter))))); - assertEquals(-1677754212, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-888563796, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - predicatesFilterPlan(descendantPlans(scanPlan().where(scanComparisons(unbounded())))) - .where(predicates(valuePredicate(fieldValueWithFieldNames("num_value_2"), new Comparisons.ParameterComparison(Comparisons.Type.IN, "valuesThree"))))); + assertEquals(-1677754212, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(-888563796, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - assertEquals(199704143, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-348451804, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } assertEquals(33, querySimpleRecordStore(NO_HOOK, plan, () -> EvaluationContext.forBinding("valuesThree", asList(1, 3)), rec -> assertThat(rec.getNumValue2(), anyOf(is(1), is(3))), @@ -261,7 +242,9 @@ void testInQueryNoIndexWithParameter() throws Exception { /** * Verify that an IN with an index is implemented as an index scan, with an IN join. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInQueryIndex[reverse={0}]") @BooleanSource void testInQueryIndex(boolean reverse) throws Exception { @@ -278,31 +261,17 @@ void testInQueryIndex(boolean reverse) throws Exception { .build()); RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - // Index(MySimpleRecord$num_value_3_indexed [EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN [1, 2, 4] - assertMatchesExactly(plan, - inValuesJoinPlan( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) - .and(reverse ? isReverse() : isNotReverse()) - ).where(inValuesList(equalsObject(comparisonLs)))); - assertEquals(reverse ? -2004057427 : -2004060309, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 399454035 : 571226247, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inValuesJoinPlan( - coveringIndexPlan() - .where(indexPlanOf(indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(equalities(only(anyValueComparison()))))) - .and(isNotReverse()) - )) - .where(inValuesList(equalsObject(comparisonLs))))); - assertEquals(reverse ? 721946094 : 702573636, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 521033883 : 518266389, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + // Index(MySimpleRecord$num_value_3_indexed [EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN [1, 2, 4] + assertMatchesExactly(plan, + inValuesJoinPlan( + indexPlan() + .where(indexName("MySimpleRecord$num_value_3_indexed")) + .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) + .and(reverse ? isReverse() : isNotReverse()) + ).where(inValuesList(equalsObject(comparisonLs)))); + assertEquals(reverse ? -2004057427 : -2004060309, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(reverse ? 399454035 : 571226247, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(60, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> assertThat(rec.getNumValue3Indexed(), anyOf(is(1), is(2), is(4))), rec -> Tuple.from(rec.getNumValue3Indexed()), @@ -524,7 +493,9 @@ void testTupleInListCannotPromote() throws Exception { /** * Verify that an IN with an index is implemented as an index scan, with an IN join. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInQueryCoveringIndex[reverse={0}]") @BooleanSource void testInQueryCoveringIndex(boolean reverse) throws Exception { @@ -544,32 +515,19 @@ void testInQueryCoveringIndex(boolean reverse) throws Exception { .build()); final RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - inValuesJoinPlan( - coveringIndexPlan() - .where(indexPlanOf( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) - .and(reverse ? isReverse() : isNotReverse()) - )) - ).where(inValuesList(equalsObject(comparisonLs)))); - assertEquals(reverse ? -2004057427 : -2004060309, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 467383055 : 639155267, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - inValuesJoinPlan( - coveringIndexPlan() - .where(indexPlanOf(indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(equalities(only(anyValueComparison())))) - .and(isNotReverse()) - ))) - .where(inValuesList(equalsObject(comparisonLs)))); - assertEquals(reverse ? 716701911 : 713934417, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? -1862646972 : -1865414466, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inValuesJoinPlan( + coveringIndexPlan() + .where(indexPlanOf( + indexPlan() + .where(indexName("MySimpleRecord$num_value_3_indexed")) + .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) + .and(reverse ? isReverse() : isNotReverse()) + )) + ).where(inValuesList(equalsObject(comparisonLs)))); + assertEquals(reverse ? -2004057427 : -2004060309, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(reverse ? 467383055 : 639155267, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(60, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> assertThat(rec.getNumValue3Indexed(), anyOf(is(1), is(2), is(4))), rec -> Tuple.from(rec.getNumValue3Indexed()), @@ -580,7 +538,9 @@ void testInQueryCoveringIndex(boolean reverse) throws Exception { /** * Verify that an IN (with parameter) with an index is implemented as an index scan, with an IN join. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryParameter() throws Exception { complexQuerySetup(NO_HOOK); RecordQuery query = RecordQuery.newBuilder() @@ -593,26 +553,16 @@ void testInQueryParameter() throws Exception { RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - // Index(MySimpleRecord$num_value_3_indexed [EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN $valueThrees - assertMatchesExactly(plan, - inParameterJoinPlan( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) - ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees")))); - assertEquals(883815022, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(514739864, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan(inParameterJoinPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(equalities(only(anyValueComparison())))))) - ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees"))))); - assertEquals(-371539778, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-248745141, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + // Index(MySimpleRecord$num_value_3_indexed [EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN $valueThrees + assertMatchesExactly(plan, + inParameterJoinPlan( + indexPlan() + .where(indexName("MySimpleRecord$num_value_3_indexed")) + .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) + ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees")))); + assertEquals(883815022, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(514739864, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + int count = querySimpleRecordStore(NO_HOOK, plan, () -> EvaluationContext.forBinding("valueThrees", asList(1, 3, 4)), rec -> assertThat(rec.getNumValue3Indexed(), anyOf(is(1), is(3), is(4))), @@ -620,7 +570,9 @@ void testInQueryParameter() throws Exception { assertEquals(60, count); } - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInQuerySortedByParameter[reverse={0}]") @BooleanSource void testInQuerySortedByParameter(boolean reverse) throws Exception { @@ -636,26 +588,16 @@ void testInQuerySortedByParameter(boolean reverse) throws Exception { RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - // Index(MySimpleRecord$num_value_3_indexed [EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN $valueThrees SORTED (DESC) - assertMatchesExactly(plan, - inParameterJoinPlan( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) - ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees")))); - assertEquals(reverse ? 883815025 : 883815023, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 342959006 : 514734098, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan(inParameterJoinPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(equalities(only(anyValueComparison())))))) - ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees"))))); - assertEquals(reverse ? 1904349145 : 1904350447, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 76381848 : 76382034, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + // Index(MySimpleRecord$num_value_3_indexed [EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN $valueThrees SORTED (DESC) + assertMatchesExactly(plan, + inParameterJoinPlan( + indexPlan() + .where(indexName("MySimpleRecord$num_value_3_indexed")) + .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) + ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees")))); + assertEquals(reverse ? 883815025 : 883815023, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(reverse ? 342959006 : 514734098, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + int count = querySimpleRecordStore(NO_HOOK, plan, () -> EvaluationContext.forBinding("valueThrees", asList(1, 3, 4)), rec -> assertThat(rec.getNumValue3Indexed(), anyOf(is(1), is(3), is(4))), @@ -726,7 +668,9 @@ void testInQuerySortedByConstantValue(boolean reverse) throws Exception { * Verify that an IN (with parameter) with an index is implemented as an index scan, with an IN union in the presence * of other equality-bound index parts. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInQueryParameter2[reverse={0}]") @BooleanSource void testInQueryParameter2(boolean reverse) throws Exception { @@ -749,29 +693,15 @@ void testInQueryParameter2(boolean reverse) throws Exception { RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - inUnionOnExpressionPlan( - indexPlan() - .where(indexName("multi_index")) - .and(scanComparisons(range("[EQUALS $p, EQUALS $__in_num_value_2__0]"))) - .and(reverse ? isReverse() : isNotReverse()) - ).where(inUnionValuesSources(exactly(RecordQueryPlanMatchers.inUnionInParameter(equalsObject("valueThrees")))))); - assertEquals(reverse ? -1109407604 : -1109408565, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 600067025 : 600245771, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inUnionOnValuesPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName("multi_index")) - .and(scanComparisons(equalities(exactly(anyParameterComparison(), anyValueComparison())))) - .and(reverse ? isReverse() : isNotReverse()) - )) - ).where(RecordQueryPlanMatchers.inUnionValuesSources(exactly(inUnionInParameter(equalsObject("valueThrees"))))))); - assertEquals(reverse ? -1896400167 : -1896406894, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? -1763552763 : -1763374017, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inUnionOnExpressionPlan( + indexPlan() + .where(indexName("multi_index")) + .and(scanComparisons(range("[EQUALS $p, EQUALS $__in_num_value_2__0]"))) + .and(reverse ? isReverse() : isNotReverse()) + ).where(inUnionValuesSources(exactly(RecordQueryPlanMatchers.inUnionInParameter(equalsObject("valueThrees")))))); + assertEquals(reverse ? -1109407604 : -1109408565, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(reverse ? 600067025 : 600245771, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); for (int i = 0; i < 5; i++) { final int p = i; @@ -792,7 +722,9 @@ void testInQueryParameter2(boolean reverse) throws Exception { * Verify that an IN (with parameter) with an index is implemented as an index scan, with an IN union in the presence * of other equality-bound index parts. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInQueryParameter2UsingCoveringIndexScans[reverse={0}]") @BooleanSource void testInQueryParameter2UsingCoveringIndexScans(boolean reverse) throws Exception { @@ -818,30 +750,17 @@ void testInQueryParameter2UsingCoveringIndexScans(boolean reverse) throws Except RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - inUnionOnExpressionPlan( - coveringIndexPlan() - .where(indexPlanOf(indexPlan() - .where(indexName("multi_index")) - .and(scanComparisons(range("[EQUALS $p, EQUALS $__in_num_value_2__0]"))) - .and(reverse ? isReverse() : isNotReverse()) - )) - ).where(inUnionValuesSources(exactly(RecordQueryPlanMatchers.inUnionInParameter(equalsObject("valueThrees")))))); - assertEquals(reverse ? -1109407604 : -1109408565, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? -1017737587 : -1017558841, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - inUnionOnValuesPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName("multi_index")) - .and(scanComparisons(equalities(exactly(anyParameterComparison(), anyValueComparison())))) - .and(reverse ? isReverse() : isNotReverse()) - )) - ).where(RecordQueryPlanMatchers.inUnionValuesSources(exactly(inUnionInParameter(equalsObject("valueThrees")))))); - assertEquals(reverse ? -884481068 : -884482029, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 147733678 : 147912424, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inUnionOnExpressionPlan( + coveringIndexPlan() + .where(indexPlanOf(indexPlan() + .where(indexName("multi_index")) + .and(scanComparisons(range("[EQUALS $p, EQUALS $__in_num_value_2__0]"))) + .and(reverse ? isReverse() : isNotReverse()) + )) + ).where(inUnionValuesSources(exactly(RecordQueryPlanMatchers.inUnionInParameter(equalsObject("valueThrees")))))); + assertEquals(reverse ? -1109407604 : -1109408565, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(reverse ? -1017737587 : -1017558841, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); for (int i = 0; i < 5; i++) { final int p = i; @@ -861,7 +780,9 @@ void testInQueryParameter2UsingCoveringIndexScans(boolean reverse) throws Except /** * Verify that an in with a bad parameter plans correctly but fails upon execution. */ - @DualPlannerTest + // SQL Port status: (#4843) not portable + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryParameterBad() throws Exception { complexQuerySetup(NO_HOOK); RecordQuery query = RecordQuery.newBuilder() @@ -871,30 +792,15 @@ void testInQueryParameterBad() throws Exception { // Index(MySimpleRecord$num_value_3_indexed [EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN $valueThrees RecordQueryPlan plan = planQuery(query); - if (useCascadesPlanner) { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inParameterJoinPlan( - coveringIndexPlan() - .where(indexPlanOf( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(equalities(exactly(anyValueComparison())))) - )) - ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees")))) - ); - assertEquals(-371539778, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-248745141, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - inParameterJoinPlan( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) - ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees")))); - assertEquals(883815022, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(514739864, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inParameterJoinPlan( + indexPlan() + .where(indexName("MySimpleRecord$num_value_3_indexed")) + .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) + ).where(RecordQueryPlanMatchers.inParameter(equalsObject("valueThrees")))); + assertEquals(883815022, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(514739864, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(0, querySimpleRecordStore(NO_HOOK, plan, () -> EvaluationContext.forBinding("valueThrees", Collections.emptyList()), rec -> fail("There should be no results"))); @@ -907,7 +813,6 @@ void testInQueryParameterBad() throws Exception { rec -> fail("There should be no results"))); } - /** * Verify that NOT IN is planned correctly, and fails if no binding is provided. */ @@ -949,7 +854,9 @@ void testNotInQueryParameterBad() throws Exception { * Verify that an IN against an unsorted list with an index is implemented as an index scan, with an IN join on * a sorted copy of the list. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInQueryIndexSorted[reverse={0}]") @BooleanSource void testInQueryIndexSorted(boolean reverse) throws Exception { @@ -963,26 +870,15 @@ void testInQueryIndexSorted(boolean reverse) throws Exception { // Index(MySimpleRecord$num_value_3_indexed [EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN [1, 2, 4] SORTED RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - inValuesJoinPlan( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) - ).where(inValuesList(equalsObject(comparisonList)))); - assertEquals(reverse ? -2004057427 : -2004060309, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 399454035 : 571226247, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inValuesJoinPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(equalities(only(anyValueComparison())))))) - ).where(inValuesList(equalsObject(comparisonList))))); - assertEquals(reverse ? 721946094 : 702573636, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 521033883 : 518266389, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inValuesJoinPlan( + indexPlan() + .where(indexName("MySimpleRecord$num_value_3_indexed")) + .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) + ).where(inValuesList(equalsObject(comparisonList)))); + assertEquals(reverse ? -2004057427 : -2004060309, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(reverse ? 399454035 : 571226247, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(60, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> assertThat(rec.getNumValue3Indexed(), anyOf(is(1), is(2), is(4))), TestHelpers::assertDiscardedNone)); @@ -992,7 +888,9 @@ void testInQueryIndexSorted(boolean reverse) throws Exception { * Verify that an IN against an unsorted list with an index is not implemented as an IN JOIN when the query sort is * not by the field with an IN filter. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInQueryIndexSortedDifferently[reverse={0}]") @BooleanSource void testInQueryIndexSortedDifferently(boolean reverse) throws Exception { @@ -1007,19 +905,12 @@ void testInQueryIndexSortedDifferently(boolean reverse) throws Exception { // Index(MySimpleRecord$str_value_indexed <,>) | num_value_3_indexed IN [1, 4, 2] RecordQueryPlan plan = planQuery(query); // IN join is cancelled on account of incompatible sorting. - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - filterPlan(selfOrDescendantPlans(indexPlan().where(indexName("MySimpleRecord$str_value_indexed")).and(scanComparisons(unbounded())).and(reverse ? isReverse() : isNotReverse()))) - .where(queryComponents(exactly(equalsObject(filter))))); - assertEquals(reverse ? 1775865787 : 1775865786, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? 966501 : 972267, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - predicatesFilterPlan(selfOrDescendantPlans(indexPlan().where(indexName("MySimpleRecord$str_value_indexed")).and(scanComparisons(unbounded()).and(reverse ? isReverse() : isNotReverse())))) - .where(predicates(valuePredicate(fieldValueWithFieldNames("num_value_3_indexed"), new Comparisons.ListComparison(Comparisons.Type.IN, ImmutableList.of(1, 4, 2)))))); - assertEquals(reverse ? -560248358 : -560248359, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(reverse ? -1230651057 : -1230645291, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + filterPlan(selfOrDescendantPlans(indexPlan().where(indexName("MySimpleRecord$str_value_indexed")).and(scanComparisons(unbounded())).and(reverse ? isReverse() : isNotReverse()))) + .where(queryComponents(exactly(equalsObject(filter))))); + assertEquals(reverse ? 1775865787 : 1775865786, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(reverse ? 966501 : 972267, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(60, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> assertThat(rec.getNumValue3Indexed(), anyOf(is(1), is(2), is(4))), rec -> Tuple.from(rec.getStrValueIndexed()), @@ -1035,7 +926,9 @@ static Stream testParameterizedInQuerySortedWithExtraUnorderedColumns Arguments.of(omitPrimaryKeyInOrdering, reverse))); } - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testParameterizedInQuerySortedWithExtraUnorderedColumns[omitPrimaryKeyInOrdering={0}, reverse={1}]") @MethodSource void testParameterizedInQuerySortedWithExtraUnorderedColumns(boolean omitPrimaryKeyInOrdering, boolean reverse) throws Exception { @@ -1062,49 +955,33 @@ void testParameterizedInQuerySortedWithExtraUnorderedColumns(boolean omitPrimary .setOmitPrimaryKeyInOrderingKeyForInUnion(omitPrimaryKeyInOrdering) .build()); final RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - if (omitPrimaryKeyInOrdering) { - // Execute this plan as an in-union: - // ∪(__in_num_value_3_indexed__0 IN $value3List) Covering(Index(indexForInUnion [EQUALS $value2, EQUALS $__in_num_value_3_indexed__0]) -> [num_value_2: KEY[0], num_value_3_indexed: KEY[1], num_value_unique: KEY[2], rec_no: KEY[4], str_value_indexed: KEY[3]]) - assertMatchesExactly(plan, - inUnionOnExpressionPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName(index.getName())) - .and(reverse ? isReverse() : isNotReverse()) - .and(scanComparisons(range("[EQUALS $" + value2Param + ", EQUALS $__in_num_value_3_indexed__0]"))) - )) - ).where(inUnionValuesSources(exactly(inUnionInParameter(equalsObject(value3ListParam))))).and(inUnionComparisonKey(concatenateFields("num_value_unique", "str_value_indexed", "rec_no")))); - - assertEquals(reverse ? -1664186714 : -1664187675, plan.planHash(CURRENT_LEGACY)); - assertEquals(reverse ? -1525853359 : -1525674613, plan.planHash(CURRENT_FOR_CONTINUATION)); - } else { - // In this config, the extra str_value_indexed column trips up the ordering key calculation, so we have to scan the num_value_unique index and filter: - // Index(MySimpleRecord$num_value_unique <,>) | And([num_value_2 EQUALS $value2, num_value_3_indexed IN $value3List]) - List filters = Objects.requireNonNull((AndComponent)query.getFilter()).getChildren(); - assertMatchesExactly(plan, - filterPlan(indexPlan() - .where(indexName("MySimpleRecord$num_value_unique")) - .and(scanComparisons(unbounded())) - .and(reverse ? isReverse() : isNotReverse()) - ).where(queryComponents(exactly(filters.stream().map(PrimitiveMatchers::equalsObject).collect(Collectors.toList()))))); - - assertEquals(reverse ? 2025860977 : 2025860976, plan.planHash(CURRENT_LEGACY)); - assertEquals(reverse ? 1776783587 : 1776789353, plan.planHash(CURRENT_FOR_CONTINUATION)); - } - } else { - // Similar to the old planner with the more permissive ordering key option, but using Cascades planner objects: - // ∪(__corr_q70 IN $value3List) Covering(Index(indexForInUnion [EQUALS $value2, EQUALS $q70]) -> [num_value_2: KEY[0], num_value_3_indexed: KEY[1], num_value_unique: KEY[2], rec_no: KEY[4], str_value_indexed: KEY[3]]) + if (omitPrimaryKeyInOrdering) { + // Execute this plan as an in-union: + // ∪(__in_num_value_3_indexed__0 IN $value3List) Covering(Index(indexForInUnion [EQUALS $value2, EQUALS $__in_num_value_3_indexed__0]) -> [num_value_2: KEY[0], num_value_3_indexed: KEY[1], num_value_unique: KEY[2], rec_no: KEY[4], str_value_indexed: KEY[3]]) assertMatchesExactly(plan, - inUnionOnValuesPlan( + inUnionOnExpressionPlan( coveringIndexPlan().where(indexPlanOf(indexPlan() .where(indexName(index.getName())) .and(reverse ? isReverse() : isNotReverse()) - .and(scanComparisons(equalities(exactly(anyParameterComparison(), anyValueComparison())))) + .and(scanComparisons(range("[EQUALS $" + value2Param + ", EQUALS $__in_num_value_3_indexed__0]"))) )) - ).where(inUnionComparisonValues(exactly(fieldValueWithFieldNames("num_value_unique"), fieldValueWithFieldNames("str_value_indexed"), fieldValueWithFieldNames("rec_no"), fieldValueWithFieldNames("num_value_3_indexed"))))); + ).where(inUnionValuesSources(exactly(inUnionInParameter(equalsObject(value3ListParam))))).and(inUnionComparisonKey(concatenateFields("num_value_unique", "str_value_indexed", "rec_no")))); + + assertEquals(reverse ? -1664186714 : -1664187675, plan.planHash(CURRENT_LEGACY)); + assertEquals(reverse ? -1525853359 : -1525674613, plan.planHash(CURRENT_FOR_CONTINUATION)); + } else { + // In this config, the extra str_value_indexed column trips up the ordering key calculation, so we have to scan the num_value_unique index and filter: + // Index(MySimpleRecord$num_value_unique <,>) | And([num_value_2 EQUALS $value2, num_value_3_indexed IN $value3List]) + List filters = Objects.requireNonNull((AndComponent)query.getFilter()).getChildren(); + assertMatchesExactly(plan, + filterPlan(indexPlan() + .where(indexName("MySimpleRecord$num_value_unique")) + .and(scanComparisons(unbounded())) + .and(reverse ? isReverse() : isNotReverse()) + ).where(queryComponents(exactly(filters.stream().map(PrimitiveMatchers::equalsObject).collect(Collectors.toList()))))); - assertEquals(reverse ? 2090127536 : 2090126575, plan.planHash(CURRENT_LEGACY)); - assertEquals(reverse ? 1582256076 : 1582434822, plan.planHash(CURRENT_FOR_CONTINUATION)); + assertEquals(reverse ? 2025860977 : 2025860976, plan.planHash(CURRENT_LEGACY)); + assertEquals(reverse ? 1776783587 : 1776789353, plan.planHash(CURRENT_FOR_CONTINUATION)); } assertEquals(21, querySimpleRecordStore(hook, plan, @@ -1115,7 +992,7 @@ void testParameterizedInQuerySortedWithExtraUnorderedColumns(boolean omitPrimary }, rec -> Tuple.from(rec.getNumValueUnique()), reverse, - (isUseCascadesPlanner() || omitPrimaryKeyInOrdering) ? TestHelpers::assertDiscardedNone : (cx -> TestHelpers.assertDiscardedAtMost(79, cx)))); + omitPrimaryKeyInOrdering ? TestHelpers::assertDiscardedNone : (cx -> TestHelpers.assertDiscardedAtMost(79, cx)))); } @DualPlannerTest(planner = DualPlannerTest.Planner.CASCADES) @@ -1216,7 +1093,9 @@ RecordQueryPlannerConfiguration.Builder configure(RecordQueryPlannerConfiguratio * * @see com.apple.foundationdb.record.query.plan.planning.InExtractor#asOr() */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest @EnumSource(InAsOrUnionMode.class) void inQueryWithSortBySecondFieldOfCompoundIndex(InAsOrUnionMode inAsOrMode) throws Exception { @@ -1239,25 +1118,7 @@ void inQueryWithSortBySecondFieldOfCompoundIndex(InAsOrUnionMode inAsOrMode) thr // Index(compoundIndex [[1],[1]]) ∪[Field { 'str_value_indexed' None}, Field { 'rec_no' None}] Index(compoundIndex [[4],[4]]) ∪[Field { 'str_value_indexed' None}, Field { 'rec_no' None}] Index(compoundIndex [[2],[2]]) // ∪(__in_num_value_3_indexed__0 IN [1, 4, 2]) Index(compoundIndex [EQUALS $__in_num_value_3_indexed__0]) RecordQueryPlan plan = planQuery(query); - if (useCascadesPlanner) { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inUnionOnValuesPlan( - coveringIndexPlan().where(indexPlanOf( - indexPlan() - .where(indexName("compoundIndex")) - .and(isNotReverse()) - .and(scanComparisons(equalities(exactly(anyValueComparison())))) - )) - ) - )); - assertEquals(637018497, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1575544496, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - - // Cascades always plans AS_UNION, so if we execute the plan with other modes, we get errors corresponding to - // the number of legs exceeding the limit (i.e., 0). - Assumptions.assumeTrue(inAsOrMode == InAsOrUnionMode.AS_UNION); - } else if (inAsOrMode == InAsOrUnionMode.AS_OR) { + if (inAsOrMode == InAsOrUnionMode.AS_OR) { assertMatchesExactly(plan, unionOnExpressionPlan(inList.stream().map(number -> indexPlan().where(indexName("compoundIndex")) @@ -1299,7 +1160,9 @@ void inQueryWithSortBySecondFieldOfCompoundIndex(InAsOrUnionMode inAsOrMode) thr * * @see com.apple.foundationdb.record.query.plan.planning.InExtractor#asOr() */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest @EnumSource(InAsOrUnionMode.class) void inQueryWithSortAndRangePredicateOnSecondFieldOfCompoundIndex(InAsOrUnionMode inAsOrMode) throws Exception { @@ -1323,25 +1186,7 @@ void inQueryWithSortAndRangePredicateOnSecondFieldOfCompoundIndex(InAsOrUnionMod // Index(compoundIndex ([1, bar],[1, foo])) ∪[Field { 'str_value_indexed' None}, Field { 'rec_no' None}] Index(compoundIndex ([4, bar],[4, foo])) ∪[Field { 'str_value_indexed' None}, Field { 'rec_no' None}] Index(compoundIndex ([2, bar],[2, foo])) // ∪(__in_num_value_3_indexed__0 IN [1, 4, 2]) Index(compoundIndex [EQUALS $__in_num_value_3_indexed__0, [GREATER_THAN bar && LESS_THAN foo]]) RecordQueryPlan plan = planQuery(query); - if (useCascadesPlanner) { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inUnionOnValuesPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName("compoundIndex")) - .and(isNotReverse()) - .and(scanComparisons(equalities(exactly(anyValueComparison())))) - )) - ) - ) - ); - assertEquals(-2146200107, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-769647490, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - - // Cascades always plans AS_UNION, so if we execute the plan with other modes, we get errors corresponding to - // the number of legs exceeding the limit (i.e., 0). - Assumptions.assumeTrue(inAsOrMode == InAsOrUnionMode.AS_UNION); - } else if (inAsOrMode == InAsOrUnionMode.AS_OR) { + if (inAsOrMode == InAsOrUnionMode.AS_OR) { // IN join is impossible because of incompatible sorting, but we can still plan as an OR on the compound index. assertMatchesExactly(plan, unionOnExpressionPlan( @@ -1385,7 +1230,9 @@ static Stream testListInQuerySortedWithExtraUnorderedColumns() { Arguments.of(inAsOrUnionMode, omitPrimaryKeyInOrdering, reverse)))); } - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testListInQuerySortedWithExtraUnorderedColumns[inAsOrUnionMode={0}, omitPrimaryKeyInOrdering={1}, reverse={2}]") @MethodSource void testListInQuerySortedWithExtraUnorderedColumns(InAsOrUnionMode inAsOrUnionMode, boolean omitPrimaryKeyInOrdering, boolean reverse) throws Exception { @@ -1410,82 +1257,57 @@ void testListInQuerySortedWithExtraUnorderedColumns(InAsOrUnionMode inAsOrUnionM .setOmitPrimaryKeyInOrderingKeyForInUnion(omitPrimaryKeyInOrdering) // for AS_OR mode .build()); final RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - if (inAsOrUnionMode == InAsOrUnionMode.NONE || !omitPrimaryKeyInOrdering) { - // In this config, the extra str_value_indexed column trips up the ordering key calculation, so we have to scan the num_value_unique index and filter: - // Index(MySimpleRecord$num_value_unique <,>) | And([num_value_2 EQUALS 1, num_value_3_indexed IN (1, 2, 4)]) - List filters = Objects.requireNonNull((AndComponent)query.getFilter()).getChildren(); - assertMatchesExactly(plan, - filterPlan(indexPlan() - .where(indexName("MySimpleRecord$num_value_unique")) - .and(scanComparisons(unbounded())) - .and(reverse ? isReverse() : isNotReverse()) - ).where(queryComponents(exactly(filters.stream().map(PrimitiveMatchers::equalsObject).collect(Collectors.toList()))))); - - assertEquals(reverse ? -207141918 : -207141919, plan.planHash(CURRENT_LEGACY)); - assertEquals(reverse ? 898139552 : 898145318, plan.planHash(CURRENT_FOR_CONTINUATION)); - } else if (inAsOrUnionMode == InAsOrUnionMode.AS_UNION) { - // Execute this plan as an in-union: - // ∪(__in_num_value_3_indexed__0 IN $value3List) Covering(Index(indexForInUnion [EQUALS $value2, EQUALS $__in_num_value_3_indexed__0]) -> [num_value_2: KEY[0], num_value_3_indexed: KEY[1], num_value_unique: KEY[2], rec_no: KEY[4], str_value_indexed: KEY[3]]) - assertMatchesExactly(plan, - inUnionOnExpressionPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName(index.getName())) - .and(reverse ? isReverse() : isNotReverse()) - .and(scanComparisons(range("[EQUALS 1, EQUALS $__in_num_value_3_indexed__0]"))) - )) - ).where(inUnionValuesSources(exactly(inUnionInValues(containsAll(Set.of(1, 2, 4)))))).and(inUnionComparisonKey(concatenateFields("num_value_unique", "str_value_indexed", "rec_no")))); - - assertEquals(reverse ? -1827031155 : -1827032116, plan.planHash(CURRENT_LEGACY)); - assertEquals(reverse ? 1591762319 : 1591941065, plan.planHash(CURRENT_FOR_CONTINUATION)); - } else { - // Execute this plan as an normal union: - // ∪(__in_num_value_3_indexed__0 IN $value3List) Covering(Index(indexForInUnion [EQUALS $value2, EQUALS $__in_num_value_3_indexed__0]) -> [num_value_2: KEY[0], num_value_3_indexed: KEY[1], num_value_unique: KEY[2], rec_no: KEY[4], str_value_indexed: KEY[3]]) - assertMatchesExactly(plan, - unionOnExpressionPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName(index.getName())) - .and(scanComparisons(range("[[1, 1],[1, 1]]"))) - .and(reverse ? isReverse() : isNotReverse()) - )), - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName(index.getName())) - .and(scanComparisons(range("[[1, 2],[1, 2]]"))) - .and(reverse ? isReverse() : isNotReverse()) - )), - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName(index.getName())) - .and(scanComparisons(range("[[1, 4],[1, 4]]"))) - .and(reverse ? isReverse() : isNotReverse()) - )) - ).where(comparisonKey(concatenateFields("num_value_unique", "str_value_indexed", "rec_no")))); + if (inAsOrUnionMode == InAsOrUnionMode.NONE || !omitPrimaryKeyInOrdering) { + // In this config, the extra str_value_indexed column trips up the ordering key calculation, so we have to scan the num_value_unique index and filter: + // Index(MySimpleRecord$num_value_unique <,>) | And([num_value_2 EQUALS 1, num_value_3_indexed IN (1, 2, 4)]) + List filters = Objects.requireNonNull((AndComponent)query.getFilter()).getChildren(); + assertMatchesExactly(plan, + filterPlan(indexPlan() + .where(indexName("MySimpleRecord$num_value_unique")) + .and(scanComparisons(unbounded())) + .and(reverse ? isReverse() : isNotReverse()) + ).where(queryComponents(exactly(filters.stream().map(PrimitiveMatchers::equalsObject).collect(Collectors.toList()))))); - assertEquals(reverse ? -1042918614 : -1042919608, plan.planHash(CURRENT_LEGACY)); - assertEquals(reverse ? -2120887025 : -1943392061, plan.planHash(CURRENT_FOR_CONTINUATION)); - } - } else { - // Cascades always uses an in-union, and it doesn't have trouble with the extra unordered columns, so it behaves like the old planner - // when omitPrimaryKeyInOrdering is true and the mode is AS_UNION - // ∪(__corr_q70 IN (1, 2, 4)) Covering(Index(indexForInUnion [EQUALS 1, EQUALS $q70]) -> [num_value_2: KEY[0], num_value_3_indexed: KEY[1], num_value_unique: KEY[2], rec_no: KEY[4], str_value_indexed: KEY[3]]) + assertEquals(reverse ? -207141918 : -207141919, plan.planHash(CURRENT_LEGACY)); + assertEquals(reverse ? 898139552 : 898145318, plan.planHash(CURRENT_FOR_CONTINUATION)); + } else if (inAsOrUnionMode == InAsOrUnionMode.AS_UNION) { + // Execute this plan as an in-union: + // ∪(__in_num_value_3_indexed__0 IN $value3List) Covering(Index(indexForInUnion [EQUALS $value2, EQUALS $__in_num_value_3_indexed__0]) -> [num_value_2: KEY[0], num_value_3_indexed: KEY[1], num_value_unique: KEY[2], rec_no: KEY[4], str_value_indexed: KEY[3]]) assertMatchesExactly(plan, - inUnionOnValuesPlan( + inUnionOnExpressionPlan( coveringIndexPlan().where(indexPlanOf(indexPlan() .where(indexName(index.getName())) .and(reverse ? isReverse() : isNotReverse()) - .and(scanComparisons(equalities(exactly(equalsObject(new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 1)), anyValueComparison())))) + .and(scanComparisons(range("[EQUALS 1, EQUALS $__in_num_value_3_indexed__0]"))) )) - ).where(inUnionComparisonValues(exactly(fieldValueWithFieldNames("num_value_unique"), fieldValueWithFieldNames("str_value_indexed"), fieldValueWithFieldNames("rec_no"), fieldValueWithFieldNames("num_value_3_indexed"))))); + ).where(inUnionValuesSources(exactly(inUnionInValues(containsAll(Set.of(1, 2, 4)))))).and(inUnionComparisonKey(concatenateFields("num_value_unique", "str_value_indexed", "rec_no")))); - assertEquals(reverse ? 1927283095 : 1927282134, plan.planHash(CURRENT_LEGACY)); - assertEquals(reverse ? 404904458 : 405083204, plan.planHash(CURRENT_FOR_CONTINUATION)); + assertEquals(reverse ? -1827031155 : -1827032116, plan.planHash(CURRENT_LEGACY)); + assertEquals(reverse ? 1591762319 : 1591941065, plan.planHash(CURRENT_FOR_CONTINUATION)); + } else { + // Execute this plan as an normal union: + // ∪(__in_num_value_3_indexed__0 IN $value3List) Covering(Index(indexForInUnion [EQUALS $value2, EQUALS $__in_num_value_3_indexed__0]) -> [num_value_2: KEY[0], num_value_3_indexed: KEY[1], num_value_unique: KEY[2], rec_no: KEY[4], str_value_indexed: KEY[3]]) + assertMatchesExactly(plan, + unionOnExpressionPlan( + coveringIndexPlan().where(indexPlanOf(indexPlan() + .where(indexName(index.getName())) + .and(scanComparisons(range("[[1, 1],[1, 1]]"))) + .and(reverse ? isReverse() : isNotReverse()) + )), + coveringIndexPlan().where(indexPlanOf(indexPlan() + .where(indexName(index.getName())) + .and(scanComparisons(range("[[1, 2],[1, 2]]"))) + .and(reverse ? isReverse() : isNotReverse()) + )), + coveringIndexPlan().where(indexPlanOf(indexPlan() + .where(indexName(index.getName())) + .and(scanComparisons(range("[[1, 4],[1, 4]]"))) + .and(reverse ? isReverse() : isNotReverse()) + )) + ).where(comparisonKey(concatenateFields("num_value_unique", "str_value_indexed", "rec_no")))); - if (inAsOrUnionMode != InAsOrUnionMode.AS_UNION) { - // When the mode is not AS_UNION, the plan should fail to execute because the parameter list is - // limited in size to 0 parameters. Potentially, this should chose an alternative plan - final RecordCoreException rce = assertThrows(RecordCoreException.class, () -> executeQuery(plan)); - assertThat(rce.getMessage(), containsString("too many IN values")); - return; - } + assertEquals(reverse ? -1042918614 : -1042919608, plan.planHash(CURRENT_LEGACY)); + assertEquals(reverse ? -2120887025 : -1943392061, plan.planHash(CURRENT_FOR_CONTINUATION)); } assertEquals(21, querySimpleRecordStore(hook, plan, @@ -1496,15 +1318,17 @@ void testListInQuerySortedWithExtraUnorderedColumns(InAsOrUnionMode inAsOrUnionM }, rec -> Tuple.from(rec.getNumValueUnique()), reverse, - (isUseCascadesPlanner() || (omitPrimaryKeyInOrdering && inAsOrUnionMode != InAsOrUnionMode.NONE)) ? TestHelpers::assertDiscardedNone : (cx -> TestHelpers.assertDiscardedAtMost(79, cx)))); + (omitPrimaryKeyInOrdering && inAsOrUnionMode != InAsOrUnionMode.NONE) ? TestHelpers::assertDiscardedNone : (cx -> TestHelpers.assertDiscardedAtMost(79, cx)))); } /** * Verify that an IN query over a parameter with a sort keeps filter even if OR is allowed. */ + // SQL Port status: (#4843) ported, plan generated @ParameterizedTest @EnumSource(InAsOrUnionMode.class) - @DualPlannerTest + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void inQueryWithSortAndParameter(InAsOrUnionMode inAsOrMode) throws Exception { RecordMetaDataHook hook = metaData -> metaData.addIndex("MySimpleRecord", "compoundIndex", @@ -1524,23 +1348,7 @@ void inQueryWithSortAndParameter(InAsOrUnionMode inAsOrMode) throws Exception { // Index(MySimpleRecord$str_value_indexed ([bar],[foo])) | num_value_3_indexed IN $inList // ∪(__in_num_value_3_indexed__0 IN $inList) Index(compoundIndex [EQUALS $__in_num_value_3_indexed__0, [GREATER_THAN bar && LESS_THAN foo]]) RecordQueryPlan plan = planQuery(query); - if (useCascadesPlanner) { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inUnionOnValuesPlan( - coveringIndexPlan() - .where(indexPlanOf( - indexPlan() - .where(indexName("compoundIndex")) - .and(scanComparisons(equalities(exactly(anyValueComparison())))) - )) - ) - )); - assertEquals(96196115, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(164261584, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - - Assumptions.assumeTrue(inAsOrMode == InAsOrUnionMode.AS_UNION); - } else if (inAsOrMode == InAsOrUnionMode.AS_UNION) { + if (inAsOrMode == InAsOrUnionMode.AS_UNION) { assertMatchesExactly(plan, inUnionOnExpressionPlan(indexPlan().where(indexName("compoundIndex")) .and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0, [GREATER_THAN bar && LESS_THAN foo]]")))) @@ -1610,8 +1418,10 @@ void cnfAsInQuery() throws Exception { * Verify that a query with an IN on the second nested field of a multi-index for which there is also a first nested * field is translated into an appropriate index scan. */ + // SQL Port status: (#4843) ported, plan generated @ParameterizedTest(name = "testInWithNesting[normalizeNestedFields={0}]") - @DualPlannerTest + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @BooleanSource void testInWithNesting(boolean normalizeNestedFields) throws Exception { final RecordMetaDataHook recordMetaDataHook = metaData -> { @@ -1636,29 +1446,18 @@ void testInWithNesting(boolean normalizeNestedFields) throws Exception { // Index(ind [EQUALS 1, EQUALS $__in_path__0]) WHERE __in_path__0 IN [String6, String1, String25, String11] setNormalizeNestedFields(normalizeNestedFields); RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - inValuesJoinPlan(indexPlan() - .where(indexName("ind")) - .and(RecordQueryPlanMatchers.scanComparisons(range("[EQUALS 1, EQUALS $__in_path__0]"))) - ).where(inValuesList(equalsObject(ls)))); - assertEquals(1075889283, plan.planHash(PlanHashable.CURRENT_LEGACY)); - if (normalizeNestedFields) { - assertEquals(1870256531, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertEquals(1864715405, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inValuesJoinPlan(indexPlan() + .where(indexName("ind")) + .and(RecordQueryPlanMatchers.scanComparisons(range("[EQUALS 1, EQUALS $__in_path__0]"))) + ).where(inValuesList(equalsObject(ls)))); + assertEquals(1075889283, plan.planHash(PlanHashable.CURRENT_LEGACY)); + if (normalizeNestedFields) { + assertEquals(1870256531, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inValuesJoinPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan() - .where(indexName("ind")) - .and(scanComparisons(equalities(exactly(equalsObject(new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 1L)), anyValueComparison())))))) - ).where(inValuesList(equalsObject(ImmutableList.of("String6", "String1", "String25", "String11")))))); - assertEquals(-932896977, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(771072275, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(1864715405, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } + queryRecordsWithHeader(recordMetaDataHook, plan, cursor -> assertEquals(asList( "_56", "_6", "_1", "_51", "_11", "_61"), cursor.map(TestRecordsWithHeaderProto.MyRecord.Builder::getStrValue).asList().get()), @@ -1668,8 +1467,10 @@ void testInWithNesting(boolean normalizeNestedFields) throws Exception { /** * Verify that a query with multiple INs is translated into an index scan within multiple IN joins. */ + // SQL Port status: (#4843) ported, plan generated @ParameterizedTest(name = "testMultipleInQueryIndex[normalizeNestedFields={0}]") - @DualPlannerTest + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @BooleanSource void testMultipleInQueryIndex(boolean normalizeNestedFields) throws Exception { final RecordMetaDataHook recordMetaDataHook = metaData -> { @@ -1694,40 +1495,24 @@ void testMultipleInQueryIndex(boolean normalizeNestedFields) throws Exception { // Index(ind [EQUALS $__in_rec_no__0, EQUALS $__in_path__1]) WHERE __in_path__1 IN [String6, String25, String1, String34] WHERE __in_rec_no__0 IN [1, 4] setNormalizeNestedFields(normalizeNestedFields); RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - final BindingMatcher indexPlanMatcher = - indexPlan() - .where(indexName("ind")) - .and(scanComparisons(range("[EQUALS $__in_rec_no__0, EQUALS $__in_path__1]"))); + final BindingMatcher indexPlanMatcher = + indexPlan() + .where(indexName("ind")) + .and(scanComparisons(range("[EQUALS $__in_rec_no__0, EQUALS $__in_path__1]"))); - assertMatchesExactly(plan, - inValuesJoinPlan( - inValuesJoinPlan(indexPlanMatcher).where(inValuesList(equalsObject(stringList))) - ).where(inValuesList(equalsObject(longList)) - ).or(inValuesJoinPlan( - inValuesJoinPlan(indexPlanMatcher).where(inValuesList(equalsObject(longList))) - ).where(inValuesList(equalsObject(stringList))))); - - assertEquals(-1869764109, plan.planHash(PlanHashable.CURRENT_LEGACY)); - if (normalizeNestedFields) { - assertEquals(-1019951714, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertEquals(1234840472, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inValuesJoinPlan( + inValuesJoinPlan(indexPlanMatcher).where(inValuesList(equalsObject(stringList))) + ).where(inValuesList(equalsObject(longList)) + ).or(inValuesJoinPlan( + inValuesJoinPlan(indexPlanMatcher).where(inValuesList(equalsObject(longList))) + ).where(inValuesList(equalsObject(stringList))))); + + assertEquals(-1869764109, plan.planHash(PlanHashable.CURRENT_LEGACY)); + if (normalizeNestedFields) { + assertEquals(-1019951714, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inValuesJoinPlan( - inValuesJoinPlan( - coveringIndexPlan() - .where(indexPlanOf(indexPlan() - .where(indexName("ind")) - .and(scanComparisons(equalities(exactly(anyValueComparison(), anyValueComparison()))))))) - .where(inValuesList(equalsObject(longList)))) - .where(inValuesList(equalsObject(stringList))))); - - assertEquals(1750857420, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-558676321, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(1234840472, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } queryRecordsWithHeader(recordMetaDataHook, plan, cursor -> @@ -1740,8 +1525,10 @@ void testMultipleInQueryIndex(boolean normalizeNestedFields) throws Exception { * Verify that a query with multiple INs is translated into an index scan within multiple IN joins, when the query * sort order is compatible with the nesting of the IN joins. */ + // SQL Port status: (#4843) ported, plan generated @ParameterizedTest(name = "testInWithNesting[normalizeNestedFields={0}]" ) - @DualPlannerTest + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @BooleanSource void testMultipleInQueryIndexSorted(boolean normalizeNestedFields) throws Exception { final RecordMetaDataHook recordMetaDataHook = metaData -> { @@ -1768,32 +1555,17 @@ void testMultipleInQueryIndexSorted(boolean normalizeNestedFields) throws Except List sortedStringList = asList("String1", "String25", "String34", "String6"); List sortedLongList = asList(1L, 4L); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - inValuesJoinPlan( - inValuesJoinPlan( - indexPlan().where(indexName("ind")).and(scanComparisons(range("[EQUALS $__in_rec_no__1, EQUALS $__in_path__0]"))) - ).where(inValuesList(equalsObject(sortedStringList))) - ).where(inValuesList(equalsObject(sortedLongList)))); - assertEquals(303286809, plan.planHash(PlanHashable.CURRENT_LEGACY)); - if (normalizeNestedFields) { - assertEquals(378183994, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertEquals(-1661991116, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inValuesJoinPlan( + inValuesJoinPlan( + indexPlan().where(indexName("ind")).and(scanComparisons(range("[EQUALS $__in_rec_no__1, EQUALS $__in_path__0]"))) + ).where(inValuesList(equalsObject(sortedStringList))) + ).where(inValuesList(equalsObject(sortedLongList)))); + assertEquals(303286809, plan.planHash(PlanHashable.CURRENT_LEGACY)); + if (normalizeNestedFields) { + assertEquals(378183994, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inValuesJoinPlan( - inValuesJoinPlan( - coveringIndexPlan() - .where(indexPlanOf(indexPlan() - .where(indexName("ind")) - .and(scanComparisons(equalities(exactly(anyValueComparison(), anyValueComparison()))))))) - .where(inValuesList(equalsObject(sortedStringList)))) - .where(inValuesList(equalsObject(sortedLongList))))); - assertEquals(38185226, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1650923249, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(-1661991116, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } queryRecordsWithHeader(recordMetaDataHook, plan, cursor -> @@ -1805,8 +1577,10 @@ void testMultipleInQueryIndexSorted(boolean normalizeNestedFields) throws Except /** * Verify that an IN join is executed correctly when the number of records to retrieve is limited. */ + // SQL Port status: (#4843) ported, plan generated @ParameterizedTest(name = "testInWithLimit[normalizeNestedFields={0}]") - @DualPlannerTest + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @BooleanSource void testInWithLimit(boolean normalizeNestedFields) throws Exception { final RecordMetaDataHook recordMetaDataHook = metaData -> { @@ -1832,28 +1606,15 @@ void testInWithLimit(boolean normalizeNestedFields) throws Exception { setNormalizeNestedFields(normalizeNestedFields); RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - inValuesJoinPlan( - indexPlan().where(indexName("ind")).and(scanComparisons(range("[EQUALS 1, EQUALS $__in_path__0]"))) - ).where(inValuesList(equalsObject(ls)))); - assertEquals(1075889283, plan.planHash(PlanHashable.CURRENT_LEGACY)); - if (normalizeNestedFields) { - assertEquals(1870256531, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertEquals(1864715405, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inValuesJoinPlan( + indexPlan().where(indexName("ind")).and(scanComparisons(range("[EQUALS 1, EQUALS $__in_path__0]"))) + ).where(inValuesList(equalsObject(ls)))); + assertEquals(1075889283, plan.planHash(PlanHashable.CURRENT_LEGACY)); + if (normalizeNestedFields) { + assertEquals(1870256531, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inValuesJoinPlan( - coveringIndexPlan() - .where(indexPlanOf(indexPlan() - .where(indexName("ind")) - .and(scanComparisons(equalities(exactly(equalsObject(new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 1L)), anyValueComparison()))))))) - .where(inValuesList(equalsObject(ls))))); - assertEquals(-932896977, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(771072275, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(1864715405, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } queryRecordsWithHeader(recordMetaDataHook, plan, null, 3, cursor -> @@ -1865,8 +1626,10 @@ void testInWithLimit(boolean normalizeNestedFields) throws Exception { /** * Verify that an IN join is executed correctly when continuations are used. */ + // SQL Port status: (#4843) ported, plan generated @ParameterizedTest(name = "testInWithContinuation[normalizeNestedFields={0}]") - @DualPlannerTest + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @BooleanSource void testInWithContinuation(boolean normalizeNestedFields) throws Exception { final RecordMetaDataHook recordMetaDataHook = metaData -> { @@ -1891,29 +1654,17 @@ void testInWithContinuation(boolean normalizeNestedFields) throws Exception { // Index(ind [EQUALS 1, EQUALS $__in_path__0]) WHERE __in_path__0 IN [String1, String6, String25, String11] setNormalizeNestedFields(normalizeNestedFields); RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - inValuesJoinPlan( - indexPlan().where(indexName("ind")).and(scanComparisons(range("[EQUALS 1, EQUALS $__in_path__0]"))) - ).where(inValuesList(equalsObject(ls)))); - assertEquals(1075745133, plan.planHash(PlanHashable.CURRENT_LEGACY)); - if (normalizeNestedFields) { - assertEquals(1870112381, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertEquals(1864571255, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + inValuesJoinPlan( + indexPlan().where(indexName("ind")).and(scanComparisons(range("[EQUALS 1, EQUALS $__in_path__0]"))) + ).where(inValuesList(equalsObject(ls)))); + assertEquals(1075745133, plan.planHash(PlanHashable.CURRENT_LEGACY)); + if (normalizeNestedFields) { + assertEquals(1870112381, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inValuesJoinPlan( - coveringIndexPlan() - .where(indexPlanOf(indexPlan() - .where(indexName("ind")) - .and(scanComparisons(equalities(exactly(equalsObject(new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 1L)), anyValueComparison()))))))) - .where(inValuesList(equalsObject(ls))))); - assertEquals(-964177527, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(766603625, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(1864571255, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); } + // result: [ "_1", "_51", "_56", "_6", "_11", "_61"] final Holder continuation = new Holder<>(); queryRecordsWithHeader(recordMetaDataHook, plan, null, 10, @@ -2163,7 +1914,9 @@ void testRecordFunctionInUngrouped() throws Exception { * Verify that IN queries can be planned using index scans, then used in a UNION to implement OR with an inequality * on the same field, and that the resulting union will be ordered by that field. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryOr() throws Exception { complexQuerySetup(NO_HOOK); RecordQuery query = RecordQuery.newBuilder() @@ -2175,31 +1928,16 @@ void testInQueryOr() throws Exception { RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - // Index(MySimpleRecord$num_value_unique [EQUALS $__in_num_value_unique__0]) WHERE __in_num_value_unique__0 IN [901, 903, 905] SORTED ∪[Field { 'num_value_unique' None}, Field { 'rec_no' None}] Index(MySimpleRecord$num_value_unique ([950],>) - assertMatchesExactly(plan, - RecordQueryPlanMatchers.unionOnExpressionPlan( - indexPlan().where(indexName("MySimpleRecord$num_value_unique")), - inValuesJoinPlan( - indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("[EQUALS $__in_num_value_unique__0]"))) - ).where(inValuesList(equalsObject(Arrays.asList(901, 903, 905))))) - .where(comparisonKey(concat(field("num_value_unique"), primaryKey("MySimpleRecord"))))); - assertEquals(1116661716, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-924293640, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - RecordQueryPlanMatchers.unionOnValuesPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan().where(indexName("MySimpleRecord$num_value_unique")))), - inValuesJoinPlan( - coveringIndexPlan().where( - indexPlanOf(indexPlan().where(indexName("MySimpleRecord$num_value_unique")) - .and(scanComparisons(equalities(exactly(anyValueComparison()))))))) - .where(inValuesList(equalsObject(Arrays.asList(901, 903, 905))))) - .where(comparisonKeyValues(exactly(fieldValueWithFieldNames("num_value_unique"), fieldValueWithFieldNames("rec_no")))))); - assertEquals(1341091029, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-29700805, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + // Index(MySimpleRecord$num_value_unique [EQUALS $__in_num_value_unique__0]) WHERE __in_num_value_unique__0 IN [901, 903, 905] SORTED ∪[Field { 'num_value_unique' None}, Field { 'rec_no' None}] Index(MySimpleRecord$num_value_unique ([950],>) + assertMatchesExactly(plan, + RecordQueryPlanMatchers.unionOnExpressionPlan( + indexPlan().where(indexName("MySimpleRecord$num_value_unique")), + inValuesJoinPlan( + indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("[EQUALS $__in_num_value_unique__0]"))) + ).where(inValuesList(equalsObject(Arrays.asList(901, 903, 905))))) + .where(comparisonKey(concat(field("num_value_unique"), primaryKey("MySimpleRecord"))))); + assertEquals(1116661716, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(-924293640, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); assertEquals(53, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> assertThat(rec.getNumValueUnique(), anyOf(is(901), is(903), is(905), greaterThan(950))), @@ -2210,7 +1948,9 @@ void testInQueryOr() throws Exception { * Verify that IN queries can be planned using index scans, then used in a UNION to implement an OR with IN whose * elements overlap, and that the union with that comparison key deduplicates the records in the overlap. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryOrOverlap() throws Exception { complexQuerySetup(NO_HOOK); RecordQuery query = RecordQuery.newBuilder() @@ -2222,38 +1962,20 @@ void testInQueryOrOverlap() throws Exception { RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - // Index(MySimpleRecord$num_value_unique [EQUALS $__in_num_value_unique__0]) WHERE __in_num_value_unique__0 IN [901, 903, 905] SORTED ∪[Field { 'num_value_unique' None}, Field { 'rec_no' None}] Index(MySimpleRecord$num_value_unique [EQUALS $__in_num_value_unique__0]) WHERE __in_num_value_unique__0 IN [904, 905, 906] SORTED - // Ordinary equality comparisons would be ordered just by the primary key so that would be the union comparison key. - // Must compare the IN field here; they are ordered, but not trivially (same value for each). - assertMatchesExactly(plan, - RecordQueryPlanMatchers.unionOnExpressionPlan( - inValuesJoinPlan( - indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("[EQUALS $__in_num_value_unique__0]"))) - ).where(inValuesList(equalsObject(Arrays.asList(901, 903, 905)))), - inValuesJoinPlan( - indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("[EQUALS $__in_num_value_unique__0]"))) - ).where(inValuesList(equalsObject(Arrays.asList(904, 905, 906)))))); - assertEquals(218263868, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(468995802, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - RecordQueryPlanMatchers.unionOnValuesPlan( - inValuesJoinPlan( - coveringIndexPlan().where( - indexPlanOf(indexPlan().where(indexName("MySimpleRecord$num_value_unique")) - .and(scanComparisons(equalities(exactly(anyValueComparison()))))))) - .where(inValuesList(equalsObject(Arrays.asList(901, 903, 905)))), - inValuesJoinPlan( - coveringIndexPlan().where( - indexPlanOf(indexPlan().where(indexName("MySimpleRecord$num_value_unique")) - .and(scanComparisons(equalities(exactly(anyValueComparison()))))))) - .where(inValuesList(equalsObject(Arrays.asList(904, 905, 906))))) - .where(comparisonKeyValues(exactly(fieldValueWithFieldNames("num_value_unique"), fieldValueWithFieldNames("rec_no")))))); - assertEquals(1914194235, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-1777494852, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + // Index(MySimpleRecord$num_value_unique [EQUALS $__in_num_value_unique__0]) WHERE __in_num_value_unique__0 IN [901, 903, 905] SORTED ∪[Field { 'num_value_unique' None}, Field { 'rec_no' None}] Index(MySimpleRecord$num_value_unique [EQUALS $__in_num_value_unique__0]) WHERE __in_num_value_unique__0 IN [904, 905, 906] SORTED + // Ordinary equality comparisons would be ordered just by the primary key so that would be the union comparison key. + // Must compare the IN field here; they are ordered, but not trivially (same value for each). + assertMatchesExactly(plan, + RecordQueryPlanMatchers.unionOnExpressionPlan( + inValuesJoinPlan( + indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("[EQUALS $__in_num_value_unique__0]"))) + ).where(inValuesList(equalsObject(Arrays.asList(901, 903, 905)))), + inValuesJoinPlan( + indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("[EQUALS $__in_num_value_unique__0]"))) + ).where(inValuesList(equalsObject(Arrays.asList(904, 905, 906)))))); + assertEquals(218263868, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(468995802, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + Set dupes = new HashSet<>(); assertEquals(5, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> { @@ -2265,7 +1987,9 @@ void testInQueryOrOverlap() throws Exception { /** * Verify that an IN requires an unordered union due to incompatible ordering. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryOrDifferentCondition() throws Exception { complexQuerySetup(NO_HOOK); planner.setConfiguration(planner.getConfiguration().asBuilder().setAttemptFailedInJoinAsUnionMaxSize(10).build()); @@ -2280,26 +2004,15 @@ void testInQueryOrDifferentCondition() throws Exception { .build(); RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - unionOnExpressionPlan( - indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("([null],[910])"))), - filterPlan( - indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("([990],>")))) - .where(queryComponents(only(equalsObject(Query.field("num_value_2").in(Arrays.asList(2, 0)))))))); - assertEquals(224679143, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(2117979117, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - RecordQueryPlanMatchers.unionOnValuesPlan( - indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("([null],[910])"))), - inUnionOnValuesPlan( - predicatesFilterPlan(indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("([990],>")))) - .where(predicates(valuePredicate(fieldValueWithFieldNames("num_value_2"), anyValueComparison())))) - .where(comparisonKeyValues(exactly(fieldValueWithFieldNames("num_value_unique"), fieldValueWithFieldNames("rec_no"), fieldValueWithFieldNames("num_value_2")))))); - assertEquals(1521186153, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-455093906, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + unionOnExpressionPlan( + indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("([null],[910])"))), + filterPlan( + indexPlan().where(indexName("MySimpleRecord$num_value_unique")).and(scanComparisons(range("([990],>")))) + .where(queryComponents(only(equalsObject(Query.field("num_value_2").in(Arrays.asList(2, 0)))))))); + assertEquals(224679143, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(2117979117, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(16, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> { assertThat(rec.getNumValueUnique(), anyOf(lessThan(910), greaterThan(990))); @@ -2314,7 +2027,9 @@ void testInQueryOrDifferentCondition() throws Exception { * multi-field index, where the left subset has equality and the final field has an IN plus inequality on that same * field. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryOrCompound() throws Exception { RecordMetaDataHook hook = complexQuerySetupHook(); complexQuerySetup(hook); @@ -2331,32 +2046,15 @@ void testInQueryOrCompound() throws Exception { // Index(multi_index [EQUALS odd, EQUALS 0, EQUALS $__in_num_value_3_indexed__0]) WHERE __in_num_value_3_indexed__0 IN [1, 3] SORTED ∪[Field { 'num_value_3_indexed' None}, Field { 'rec_no' None}] Index(multi_index [[odd, 0, 4],[odd, 0]]) RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - RecordQueryPlanMatchers.unionOnExpressionPlan( - inValuesJoinPlan( - indexPlan().where(indexName("multi_index")).and(scanComparisons(range("[EQUALS odd, EQUALS 0, EQUALS $__in_num_value_3_indexed__0]"))) - ).where(inValuesList(equalsObject(Arrays.asList(1, 3)))), - indexPlan().where(indexName("multi_index")).and(scanComparisons(range("[[odd, 0, 4],[odd, 0]]"))))); - assertEquals(468569345, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1312398381, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - RecordQueryPlanMatchers.unionOnValuesPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan().where(indexName("multi_index")))), - inValuesJoinPlan( - coveringIndexPlan().where( - indexPlanOf(indexPlan().where(indexName("multi_index")) - .and(scanComparisons(equalities(exactly( - equalsObject(new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, "odd")), - equalsObject(new Comparisons.SimpleComparison(Comparisons.Type.EQUALS, 0)), - anyValueComparison()))))))) - .where(inValuesList(equalsObject(Arrays.asList(1, 3))))) - .where(comparisonKeyValues(exactly(fieldValueWithFieldNames("num_value_3_indexed"), fieldValueWithFieldNames("rec_no")))))); - assertEquals(-761192240, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(876045548, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + RecordQueryPlanMatchers.unionOnExpressionPlan( + inValuesJoinPlan( + indexPlan().where(indexName("multi_index")).and(scanComparisons(range("[EQUALS odd, EQUALS 0, EQUALS $__in_num_value_3_indexed__0]"))) + ).where(inValuesList(equalsObject(Arrays.asList(1, 3)))), + indexPlan().where(indexName("multi_index")).and(scanComparisons(range("[[odd, 0, 4],[odd, 0]]"))))); + assertEquals(468569345, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(1312398381, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + assertEquals(3 + 4 + 4, querySimpleRecordStore(hook, plan, EvaluationContext::empty, rec -> { assertThat(rec.getStrValueIndexed(), is("odd")); @@ -2369,7 +2067,9 @@ void testInQueryOrCompound() throws Exception { * Verify an IN clause prevents index usage because the IN loop is not compatible with index ordering in the old * planner but causes an IN-UNION to be created in the new planner. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryOrMultipleIndexes() throws Exception { complexQuerySetup(NO_HOOK); planner.setConfiguration(InAsOrUnionMode.AS_UNION.configure(planner.getConfiguration().asBuilder()) @@ -2384,32 +2084,18 @@ void testInQueryOrMultipleIndexes() throws Exception { RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - // Two ordinary equals single-column index scans would be compatible on the following primary key, but - // the IN loop inside one branch prevents that here. A regular filter would not. - assertMatchesExactly(plan, - unorderedPrimaryKeyDistinctPlan( - unorderedUnionPlan( - indexPlan().where(indexName("MySimpleRecord$str_value_indexed")).and(scanComparisons(range("[[odd],[odd]]"))), - inValuesJoinPlan( - indexPlan().where(indexName("MySimpleRecord$num_value_3_indexed")).and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) - ).where(inValuesList(equalsObject(Arrays.asList(1, 3))))))); - assertEquals(-1310248168, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1826025907, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - RecordQueryPlanMatchers.unionOnValuesPlan( - coveringIndexPlan().where(indexPlanOf(indexPlan().where(indexName("MySimpleRecord$str_value_indexed")))), - inUnionOnValuesPlan( - coveringIndexPlan().where( - indexPlanOf(indexPlan().where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(equalities(exactly(anyValueComparison()))))))) - .where(comparisonKeyValues(exactly(fieldValueWithFieldNames("rec_no"), fieldValueWithFieldNames("num_value_3_indexed")))) - .and(inUnionValuesSources(exactly(inUnionInValues(equalsObject(ImmutableList.of(1, 3))))))))); - assertEquals(2084726425, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-865068999, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + // Two ordinary equals single-column index scans would be compatible on the following primary key, but + // the IN loop inside one branch prevents that here. A regular filter would not. + assertMatchesExactly(plan, + unorderedPrimaryKeyDistinctPlan( + unorderedUnionPlan( + indexPlan().where(indexName("MySimpleRecord$str_value_indexed")).and(scanComparisons(range("[[odd],[odd]]"))), + inValuesJoinPlan( + indexPlan().where(indexName("MySimpleRecord$num_value_3_indexed")).and(scanComparisons(range("[EQUALS $__in_num_value_3_indexed__0]"))) + ).where(inValuesList(equalsObject(Arrays.asList(1, 3))))))); + assertEquals(-1310248168, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(1826025907, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + Set dupes = new HashSet<>(); assertEquals(50 + 10 + 10, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, rec -> { @@ -2423,7 +2109,9 @@ void testInQueryOrMultipleIndexes() throws Exception { /** * Verify that enum field indexes are used to implement IN clauses. */ - @DualPlannerTest + // SQL Port status: (#4843) deferred + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void enumIn() throws Exception { RecordMetaDataHook hook = metaData -> { final RecordTypeBuilder type = metaData.getRecordType("MyShapeRecord"); @@ -2443,25 +2131,11 @@ void enumIn() throws Exception { // Index(color [EQUALS $__in_color__0]) WHERE __in_color__0 IN [RED, BLUE] RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - descendantPlans(indexPlan().where(indexName("color")))); - assertFalse(plan.hasRecordScan(), "should not use record scan"); - assertEquals(-520431454, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1447363737, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - fetchFromPartialRecordPlan( - inValuesJoinPlan( - coveringIndexPlan().where( - indexPlanOf(indexPlan().where(indexName("color")) - .and(scanComparisons(equalities(exactly( - anyValueComparison()))))))) - .where(inValuesList(equalsInList(redBlue))))); - assertFalse(plan.hasRecordScan(), "should not use record scan"); - assertEquals(-349856378, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(421925187, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + descendantPlans(indexPlan().where(indexName("color")))); + assertFalse(plan.hasRecordScan(), "should not use record scan"); + assertEquals(-520431454, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(1447363737, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); try (FDBRecordContext context = openContext()) { openEnumRecordStore(context, hook); @@ -2483,7 +2157,9 @@ void enumIn() throws Exception { /** * Verify that an IN with an empty list returns nothing. */ - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) void testInQueryEmptyList() throws Exception { complexQuerySetup(NO_HOOK); List ls = Collections.emptyList(); @@ -2496,18 +2172,10 @@ void testInQueryEmptyList() throws Exception { // Scan(<,>) | [MySimpleRecord] | num_value_2 IN [] RecordQueryPlan plan = planQuery(query); - if (planner instanceof RecordQueryPlanner) { - assertMatchesExactly(plan, - filterPlan(selfOrDescendantPlans(scanPlan().where(scanComparisons(unbounded())))).where(queryComponents(only(equalsObject(filter))))); - assertEquals(-1139440895, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1691830390, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - predicatesFilterPlan(selfOrDescendantPlans(scanPlan().and(scanComparisons(unbounded())))) - .where(predicates(valuePredicate(fieldValueWithFieldNames("num_value_2"), new Comparisons.ListComparison(Comparisons.Type.IN, ImmutableList.of()))))); - assertEquals(738017460, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-2063024914, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + filterPlan(selfOrDescendantPlans(scanPlan().where(scanComparisons(unbounded())))).where(queryComponents(only(equalsObject(filter))))); + assertEquals(-1139440895, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(1691830390, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); assertEquals(0, querySimpleRecordStore(NO_HOOK, plan, EvaluationContext::empty, (rec) -> { })); @@ -2600,7 +2268,9 @@ void testInQueryWithNonSargableReplan(final int maxReplans) throws Exception { } } - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInUnionWithNonSargable[replans={0}]") @ValueSource(ints = {-1, 0, 1}) void testInUnionWithNonSargable(int replans) throws Exception { @@ -2622,28 +2292,20 @@ void testInUnionWithNonSargable(int replans) throws Exception { // Make sure we do not plan the IN as a union and that we don't fall back to a primary scan but use an // index. - if (useCascadesPlanner) { - assertMatchesExactly(plan, - predicatesFilterPlan( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(unbounded()))) - .where(predicates(only(valuePredicate(anyValue(), new Comparisons.ListComparison(Comparisons.Type.IN, List.of(1, 3, 5, 7, 9))))))); - assertEquals(-441605751, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1552275015, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else { - assertMatchesExactly(plan, - filterPlan( - indexPlan() - .where(indexName("MySimpleRecord$num_value_3_indexed")) - .and(scanComparisons(unbounded()))) - .where(queryComponents(only(equalsObject(inFilter))))); - assertEquals(1975903190, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1012163023, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } + assertMatchesExactly(plan, + filterPlan( + indexPlan() + .where(indexName("MySimpleRecord$num_value_3_indexed")) + .and(scanComparisons(unbounded()))) + .where(queryComponents(only(equalsObject(inFilter))))); + assertEquals(1975903190, plan.planHash(PlanHashable.CURRENT_LEGACY)); + assertEquals(1012163023, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); + } - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInUnionWithSomeSargable[replans={0}]") @ValueSource(ints = {-2, -1, 0, 1}) void testInUnionWithSomeSargable(int replans) throws Exception { @@ -2666,21 +2328,7 @@ void testInUnionWithSomeSargable(int replans) throws Exception { RecordQueryPlan plan = planQuery(query); - if (useCascadesPlanner) { - // This is a suboptimal plan! - // In iterates over both IN-values even though only one of the associated predicate is only sargable - // See: https://github.com/FoundationDB/fdb-record-layer/issues/2950 - assertMatchesExactly(plan, - inUnionOnValuesPlan( - predicatesFilterPlan( - indexPlan() - .where(indexName(numValue2Then3Index.getName())) - .and(scanComparisons(equalities(only(anyComparison()))))) - .where(predicates(only(anyObject())))) - .where(inUnionValuesSources(exactly(inUnionInParameter(equalsObject("nv2_list")), inUnionInParameter(equalsObject("str_list")))))); - assertEquals(1054186198, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(1446801539, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - } else if (replans < 0) { + if (replans < 0) { // This is a suboptimal plan! // In iterates over both IN-values even though only one of the associated predicate is only sargable assertMatchesExactly(plan, @@ -2730,7 +2378,7 @@ void testInUnionWithSomeSargable(int replans) throws Exception { assertEquals("even", simpleRecord.getStrValueIndexed()); }, sortKey, false, - context -> TestHelpers.assertDiscardedAtMost(!useCascadesPlanner && replans == 0 ? 66 : 33, context)) + context -> TestHelpers.assertDiscardedAtMost(replans == 0 ? 66 : 33, context)) ); assertEquals(33, querySimpleRecordStore(hook, plan, @@ -2740,14 +2388,14 @@ void testInUnionWithSomeSargable(int replans) throws Exception { assertEquals("odd", simpleRecord.getStrValueIndexed()); }, sortKey, false, - context -> TestHelpers.assertDiscardedAtMost(!useCascadesPlanner && replans == 0 ? 68 : 34, context)) + context -> TestHelpers.assertDiscardedAtMost(replans == 0 ? 68 : 34, context)) ); assertEquals(67, querySimpleRecordStore(hook, plan, () -> EvaluationContext.forBindingsAndTypeRepository(base.childBuilder().set("str_list", List.of("even", "odd", "other")).build(), typeRepository), numValue2Check::accept, sortKey, false, - context -> TestHelpers.assertDiscardedAtMost(!useCascadesPlanner && replans > 0 ? 0 : 134, context)) + context -> TestHelpers.assertDiscardedAtMost(replans > 0 ? 0 : 134, context)) ); } @@ -2758,7 +2406,9 @@ static Stream testInUnionWithIntersectionOnTwoPredicates() { Arguments.of(replans, dropNumValue3Index))); } - @DualPlannerTest + // SQL Port status: (#4843) ported, plan generated + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + @DualPlannerTest(planner = DualPlannerTest.Planner.OLD) @ParameterizedTest(name = "testInUnionWithSomeSargable[replans={0}, dropNumValue3Index={1}]") @MethodSource void testInUnionWithIntersectionOnTwoPredicates(int replans, boolean dropNumValue3Index) throws Exception { @@ -2789,28 +2439,7 @@ void testInUnionWithIntersectionOnTwoPredicates(int replans, boolean dropNumValu RecordQueryPlan plan = planQuery(query); boolean singleIndexScan; - if (useCascadesPlanner) { - // The Cascades planner always goes with the union. Note that it splits the in-union in twain rather than - // - assertMatchesExactly(plan, - inUnionOnValuesPlan( - inUnionOnValuesPlan( - fetchFromPartialRecordPlan( - intersectionOnValuesPlan( - coveringIndexPlan().where(indexPlanOf( - indexPlan().where(indexName(numValue2Then3Index.getName())).and(scanComparisons(equalities(only(anyComparison())))) - )), - coveringIndexPlan().where(indexPlanOf( - indexPlan().where(indexName(strValueThen3Index.getName())).and(scanComparisons(equalities(only(anyComparison())))) - )) - ) - ) - ).where(inUnionValuesSources(only(inUnionInParameter(equalsObject("str_list"))))) - ).where(inUnionValuesSources(only(inUnionInParameter(equalsObject("nv2_list")))))); - assertEquals(-1515112545, plan.planHash(PlanHashable.CURRENT_LEGACY)); - assertEquals(-1918440177, plan.planHash(PlanHashable.CURRENT_FOR_CONTINUATION)); - singleIndexScan = false; - } else if (replans < 0 || dropNumValue3Index) { + if (replans < 0 || dropNumValue3Index) { // Before replanning, we always end up with an intersection. Neither index alone is enough to satisfy both // predicates, so both IN comparisons are marked as not sargable. // If the num_value_3 index has been dropped, then we do not have any good way of satisfying, so we have diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBPermutedMinMaxQueryTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBPermutedMinMaxQueryTest.java index cfcd5e69d91..093c6f081ac 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBPermutedMinMaxQueryTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBPermutedMinMaxQueryTest.java @@ -426,11 +426,16 @@ static Stream selectMaxWithInOrderByMax() { ); } + // SQL Port status: (#4843) ported, plan generated + // SQL Port: cascades-query-tests/permuted-min-max.yamsql @DualPlannerTest(planner = DualPlannerTest.Planner.CASCADES) @ParameterizedTest @MethodSource void selectMaxWithInOrderByMax(InComparisonCase inComparisonCase) throws Exception { Assumptions.assumeTrue(useCascadesPlanner); + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + Assumptions.assumeFalse(inComparisonCase.getComparison() instanceof Comparisons.ListComparison || + inComparisonCase.getComparison() instanceof Comparisons.ParameterComparison); final RecordMetaDataHook hook = metaData -> metaData.addIndex(metaData.getRecordType("MySimpleRecord"), maxUniqueBy2And3()); complexQuerySetup(hook); try (FDBRecordContext context = openContext()) { @@ -508,11 +513,15 @@ static Stream testMaxWithInAndDupes() { ); } + // SQL Port status: (#4843) ported, plan generated @DualPlannerTest(planner = DualPlannerTest.Planner.CASCADES) @ParameterizedTest @MethodSource void testMaxWithInAndDupes(InComparisonCase inComparisonCase) throws Exception { Assumptions.assumeTrue(useCascadesPlanner); + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + Assumptions.assumeFalse(inComparisonCase.getComparison() instanceof Comparisons.ListComparison || + inComparisonCase.getComparison() instanceof Comparisons.ParameterComparison); final RecordMetaDataHook hook = metaData -> metaData.addIndex(metaData.getRecordType("MySimpleRecord"), max2ByStrValueAnd3()); complexQuerySetup(hook); @@ -650,11 +659,15 @@ static Stream testSortedMaxWithInOnRepeater() { ); } + // SQL Port status: (#4843) ported, plan generated @DualPlannerTest(planner = DualPlannerTest.Planner.CASCADES) @ParameterizedTest @MethodSource void testSortedMaxWithInOnRepeater(InComparisonCase inComparisonCase) { Assumptions.assumeTrue(useCascadesPlanner); + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + Assumptions.assumeFalse(inComparisonCase.getComparison() instanceof Comparisons.ListComparison || + inComparisonCase.getComparison() instanceof Comparisons.ParameterComparison); final RecordMetaDataHook hook = metaData -> metaData.addIndex(metaData.getRecordType("MySimpleRecord"), max3ByStrValueRepeaterAnd2()); setUpWithRepeaters(hook); @@ -760,11 +773,15 @@ static Stream testMaxUniqueByStr2And3WithDifferentOrderingKeys ); } + // SQL Port status: (#4843) ported, plan generated @DualPlannerTest(planner = DualPlannerTest.Planner.CASCADES) @ParameterizedTest(name = "testMaxUniqueByStr2And3WithDifferentOrderingKeys[inComparisonCase={0}]") @MethodSource void testMaxUniqueByStr2And3WithDifferentOrderingKeys(InComparisonCase inComparisonCase) throws Exception { Assumptions.assumeTrue(useCascadesPlanner); + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + Assumptions.assumeFalse(inComparisonCase.getComparison() instanceof Comparisons.ListComparison || + inComparisonCase.getComparison() instanceof Comparisons.ParameterComparison); final RecordMetaDataHook hook = metaData -> { metaData.addIndex(metaData.getRecordType("MySimpleRecord"), maxUniqueByStrValueOrderBy2And3()); metaData.removeIndex("MySimpleRecord$num_value_unique"); // get rid of unique index as we don't want uniqueness constraint diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java index c3ea6d25406..6a4510d3f6a 100644 --- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java +++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java @@ -704,11 +704,14 @@ void testSubselectHasNullOnEmptyAndIsNullPredicate() { * * @param inComparison whether the inner existential predicate should be an IN or EQUALS predicate */ + // SQL Port status: (#4843) ported, plan generated @DualPlannerTest(planner = DualPlannerTest.Planner.CASCADES) @ParameterizedTest @BooleanSource void testSimpleExistentialPredicateOnSimpleIndex(boolean inComparison) { Assumptions.assumeTrue(useCascadesPlanner); + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + Assumptions.assumeFalse(inComparison); final Index index = new Index("Restaurant$tag.value", Key.Expressions.field("tags").nest(Key.Expressions.field("values", KeyExpression.FanType.FanOut).nest("value"))); CascadesPlanner cascadesPlanner = setUpWithNullableArray(metaDataBuilder -> metaDataBuilder.addIndex("RestaurantRecord", index)); final var tagValueParam = "t"; @@ -762,9 +765,11 @@ void testSimpleExistentialPredicateOnSimpleIndex(boolean inComparison) { } } + // SQL Port status: (#4843) ported, plan generated @DualPlannerTest(planner = DualPlannerTest.Planner.CASCADES) void testEqualityAndSimpleExistentialPredicate() { - Assumptions.assumeTrue(useCascadesPlanner); + // LiteralValue/Parameter IN Source is not compatible with Cascades (#4816) + Assumptions.abort(); final Index index = new Index("Restaurant$name-tagValue", Key.Expressions.concat( Key.Expressions.field("name"), Key.Expressions.field("tags").nest(Key.Expressions.field("values", KeyExpression.FanType.FanOut).nest("value")) diff --git a/fdb-record-layer-core/src/testFixtures/java/com/apple/foundationdb/record/provider/foundationdb/query/DualPlannerExtension.java b/fdb-record-layer-core/src/testFixtures/java/com/apple/foundationdb/record/provider/foundationdb/query/DualPlannerExtension.java index 28ad1a06e97..5c73c7476bd 100644 --- a/fdb-record-layer-core/src/testFixtures/java/com/apple/foundationdb/record/provider/foundationdb/query/DualPlannerExtension.java +++ b/fdb-record-layer-core/src/testFixtures/java/com/apple/foundationdb/record/provider/foundationdb/query/DualPlannerExtension.java @@ -56,6 +56,18 @@ public boolean supportsTestTemplate(ExtensionContext context) { FDBRecordStoreQueryTestBase.class.isAssignableFrom(context.getRequiredTestClass()); } + /** + * Allows a parameterized test that only runs the old planner to provide no invocation contexts of its own, as the + * {@link ParameterizedTest} extension already runs it with the old planner. + */ + @Override + public boolean mayReturnZeroTestTemplateInvocationContexts(ExtensionContext context) { + return AnnotationUtils.isAnnotated(context.getTestMethod(), ParameterizedTest.class) && + AnnotationUtils.findAnnotation(context.getTestMethod(), DualPlannerTest.class) + .map(annotation -> annotation.planner() == DualPlannerTest.Planner.OLD) + .orElse(false); + } + @Override public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { final Optional annotationOptional = @@ -79,7 +91,8 @@ public Stream provideTestTemplateInvocationContex // Create a Cascades invocation context. The non-Cascades planner will be run via the ParameterizedTest // extension running the test on its own, which will run the old planner by default. // Note that, as a consequence, this means that CASCADES (only) mode does not work with parameterized tests, - // and the old planner will always be run. + // and the old planner will always be run. OLD (only) mode provides no contexts here, which + // mayReturnZeroTestTemplateInvocationContexts allows. // See: https://github.com/FoundationDB/fdb-record-layer/issues/2354 if (annotation.planner() != DualPlannerTest.Planner.OLD) { return nestedProvider.provideTestTemplateInvocationContexts(context).map(existingContext -> diff --git a/yaml-tests/src/test/java/CascadesQueryTests.java b/yaml-tests/src/test/java/CascadesQueryTests.java new file mode 100644 index 00000000000..b942b415a88 --- /dev/null +++ b/yaml-tests/src/test/java/CascadesQueryTests.java @@ -0,0 +1,127 @@ +/* + * CascadesQueryTests.java + * + * This source file is part of the FoundationDB open source project + * + * Copyright 2015-2026 Apple Inc. and the FoundationDB project authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.apple.foundationdb.relational.yamltests.YamlTest; +import org.junit.jupiter.api.TestTemplate; + +/** + * Query-planning coverage ported from the Cascades tests in {@code fdb-record-layer-core}'s + * {@code com.apple.foundationdb.record.provider.foundationdb.query} package. + * + *

Those tests drive the Cascades planner through the {@code RecordQuery} builder API; the SQL layer + * drives the same planner from SQL text, so most of them can be expressed declaratively here + * instead. Each yamsql file corresponds to one source test class, and every query carries a comment + * naming the source test method it came from. Where a source class needs more than one index + * configuration the file is split, with the variant named in a suffix — the planner has to be forced + * into a plan by the schema it is given, and {@code USE INDEX} cannot stand in for a missing index + * because a hinted query drops the primary-scan candidate entirely. + * + *

See {@code cascades-query-tests/README.md} for the source-class mapping, the authoring + * conventions, and the list of tests that cannot be ported. + */ +@YamlTest +class CascadesQueryTests { + private static final String PREFIX = "cascades-query-tests"; + + @TestTemplate + void inQueryCompoundIndex(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-compound-index.yamsql"); + } + + @TestTemplate + void inQueryHeader(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-header.yamsql"); + } + + @TestTemplate + void inQueryMultiIndex(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-multi-index.yamsql"); + } + + @TestTemplate + void inQuery(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query.yamsql"); + } + + @TestTemplate + void inQueryOrCompound(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-or-compound.yamsql"); + } + + @TestTemplate + void inQueryOr(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-or.yamsql"); + } + + @TestTemplate + void inQueryUnionIntersectionNoNv3(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-union-intersection-no-nv3.yamsql"); + } + + @TestTemplate + void inQueryUnionIntersection(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-union-intersection.yamsql"); + } + + @TestTemplate + void inQueryUnionSargable(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-union-sargable.yamsql"); + } + + @TestTemplate + void inQueryOrOverlap(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-or-overlap.yamsql"); + } + + @TestTemplate + void permutedMinMaxNumValue2(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/permuted-min-max-num-value-2.yamsql"); + } + + @TestTemplate + void permutedMinMaxOrderingKeys(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/permuted-min-max-ordering-keys.yamsql"); + } + + @TestTemplate + void permutedMinMaxRepeater(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/permuted-min-max-repeater.yamsql"); + } + + @TestTemplate + void permutedMinMax(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/permuted-min-max.yamsql"); + } + + @TestTemplate + void simpleQueryGraphNameTagIndex(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/simple-query-graph-name-tag-index.yamsql"); + } + + @TestTemplate + void simpleQueryGraphTagIndex(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/simple-query-graph-tag-index.yamsql"); + } + + @TestTemplate + void inQuerySortedExtraUnorderedColumns(YamlTest.Runner runner) throws Exception { + runner.runYamsql(PREFIX + "/in-query-sorted-extra-unordered-columns.yamsql"); + } +} diff --git a/yaml-tests/src/test/resources/cascades-query-tests/MIGRATION.md b/yaml-tests/src/test/resources/cascades-query-tests/MIGRATION.md new file mode 100644 index 00000000000..465a9e6eb9f --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/MIGRATION.md @@ -0,0 +1,82 @@ +# Migration map: `fdb-record-layer-core` query tests to `cascades-query-tests` + +Generated as part of moving the Cascades-planner query tests out of `fdb-record-layer-core` and into +the declarative yamsql suite. **The core tests are untouched** — retiring them is a separate, later +step, so every source method listed here still exists and still runs. + +New tests live in `yaml-tests/src/test/resources/cascades-query-tests/.yamsql`, registered by +`yaml-tests/src/test/java/CascadesQueryTests.java`. Authoring conventions are in `README.md` in the +same directory. + +## Legend + +| state | meaning | +|---|---| +| ported, plan generated | in a yamsql file; has run and its `explain:` is filled in | +| ported, plan generated | in a yamsql file; `explain:` still `""` because the file has not yet completed a run | +| **ported, FAILING** | in a yamsql file, expectation believed correct, product does not match. The query is parked: a `supported_version: "!max_version"` sentinel where the file has other working queries, `@Disabled` on the suite method where it does not — see *Parking a query that a defect breaks* in `README.md` | +| covered indirectly | no dedicated query; exercised by another entry or by a test configuration | +| deferred | portable in principle, not done yet | +| not portable | cannot be expressed in SQL; stays a JUnit test permanently | +| out of scope | plain `@Test` / `@ParameterizedTest` without `@DualPlannerTest`, i.e. old planner only | + +`annotation` records how the source method is annotated: `dual` = `@DualPlannerTest`, +`cascades` = `@DualPlannerTest(planner = CASCADES)`, `old` = `@DualPlannerTest(planner = OLD)`. + +## `FDBInQueryTest` + +`fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBInQueryTest.java` + +| source method | annotation | new file | state | note | +|---|---|---|---|---| +| `testInQueryNoIndex` | dual | `in-query.yamsql` | ported, plan generated | expected 67 rows | +| `testInQueryNoIndexWithParameter` | dual | `in-query.yamsql` | ported, plan generated | `valuesThree` is a parameter in the source, so it is `!! !in {[1,3]} !!` in the `in-query-prepared` block (`statement_type: prepared`); the IN stays a residual filter. 33 rows | +| `testInQueryIndex` | dual | `in-query.yamsql` | ported, plan generated | 2 queries (forward and reverse), IN list already in order: `(1, 2, 4)` | +| `testInQueryCoveringIndex` | dual | `in-query.yamsql` | ported, plan generated | 2 queries (forward and reverse); only REC_NO projected | +| `testInQueryParameter` | dual | `in-query.yamsql` | ported, plan generated | own query, no ORDER BY; the IN list is a parameter (`!! !in {[1, 3, 4]} !!`) in the `in-query-prepared` block | +| `testInQuerySortedByParameter` | dual | `in-query.yamsql` | ported, plan generated | own queries, forward and reverse, sorted on the IN column; parameter-injected in the `in-query-prepared` block | +| `testInQueryParameter2` | dual | `in-query-multi-index.yamsql` | ported, plan generated | 10 queries: p in 0..4 x forward/reverse; `p` and the `valueThrees` list are parameter-injected, block is `statement_type: prepared` | +| `testInQueryParameter2UsingCoveringIndexScans` | dual | `in-query-multi-index.yamsql` | ported, plan generated | 10 queries: p in 0..4 x forward/reverse, covering variant; parameter-injected, block is `statement_type: prepared` | +| `testInQueryParameterBad` | dual | — | not portable | binds a non-list to an IN parameter; a SQL IN list is always a list | +| `testInQueryIndexSorted` | dual | `in-query.yamsql` | ported, plan generated | 2 queries (forward and reverse) with the IN list out of order, `(1, 4, 2)`, so the SORTED flag does work; ordered `result` | +| `testInQueryIndexSortedDifferently` | dual | `in-query.yamsql` | ported, plan generated | 2 queries, forward and reverse; the sort is on a different column than the IN | +| `testParameterizedInQuerySortedWithExtraUnorderedColumns` | dual | `in-query-sorted-extra-unordered-columns.yamsql` | ported, plan generated | own queries, forward and reverse, in the `-prepared` block: `num_value_2` and the IN list are parameter-injected. See the file header for the comparison-key divergence | +| `inQueryWithSortBySecondFieldOfCompoundIndex` | dual | `in-query-compound-index.yamsql` | ported, plan generated | | +| `inQueryWithSortAndRangePredicateOnSecondFieldOfCompoundIndex` | dual | `in-query-compound-index.yamsql` | ported, plan generated | | +| `testListInQuerySortedWithExtraUnorderedColumns` | dual | `in-query-sorted-extra-unordered-columns.yamsql` | ported, plan generated | own queries, forward and reverse, with a literal IN list. See the file header for the comparison-key divergence | +| `inQueryWithSortAndParameter` | dual | `in-query-compound-index.yamsql` | ported, plan generated | the IN list is a parameter, so it is in the `-prepared` block (`!! !in {[1, 4, 2]} !!`) | +| `testInWithNesting` | dual | `in-query-header.yamsql` | ported, plan generated | | +| `testMultipleInQueryIndex` | dual | `in-query-header.yamsql` | ported, plan generated | | +| `testMultipleInQueryIndexSorted` | dual | `in-query-header.yamsql` | ported, plan generated | | +| `testInWithLimit` | dual | `in-query-header.yamsql` | ported, plan generated | `maxRows: 3` pages (SQL has no LIMIT clause); the source `normalizeNestedFields` parameterization is old-planner only | +| `testInWithContinuation` | dual | `in-query-header.yamsql` | ported, plan generated | `maxRows: 1` pagination across the whole result, so page boundaries fall inside and between IN elements | +| `testInQueryOr` | dual | `in-query-or.yamsql` | ported, plan generated | | +| `testInQueryOrOverlap` | dual | `in-query-or-overlap.yamsql` | ported, plan generated | OR of two IN lists sharing a value (905): a union of two in-joins, deduplicated. Previously raised a ClassCastException; it passes now | +| `testInQueryOrDifferentCondition` | dual | `in-query-or.yamsql` | ported, plan generated | one leg is a range, the other an AND whose IN stays a residual filter | +| `testInQueryOrCompound` | dual | `in-query-or-compound.yamsql` | ported, plan generated | IN inside an OR inside an AND, all bound by MULTI_INDEX | +| `testInQueryOrMultipleIndexes` | dual | `in-query-or.yamsql` | ported, plan generated | the two legs use two different indexes | +| `enumIn` | dual | — | deferred | needs CREATE TYPE AS ENUM | +| `testInQueryEmptyList` | dual | `in-query-or.yamsql` | ported, plan generated | an empty IN list cannot be a SQL literal; `!! !in {[]} !!` in the `in-query-or-prepared` block (`statement_type: prepared`) | +| `testInUnionWithNonSargable` | dual | `in-query-or.yamsql` | ported, plan generated | NUM_VALUE_2 has no index, so the IN cannot become an in-union; the `maxNumReplansForInToJoin` parameterization collapses | +| `testInUnionWithSomeSargable` | dual | `in-query-union-sargable.yamsql` | ported, plan generated | 3 queries, one per `str_list` binding; the replan and index parameterizations collapse Both IN lists are parameters in the source, so they are parameter-injected and the block is `statement_type: prepared`. | +| `testInUnionWithIntersectionOnTwoPredicates` | dual | `in-query-union-intersection.yamsql`, `in-query-union-intersection-no-nv3.yamsql` | ported, plan generated | 3 queries per index variant; the `dropNumValue3Index = true` variant is the `-no-nv3` file, where MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED is absent so the sort must come from one of the two composite indexes Both IN lists are parameters in the source, so they are parameter-injected and the blocks are `statement_type: prepared`. | + +## `FDBPermutedMinMaxQueryTest` + +`fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBPermutedMinMaxQueryTest.java` + +| source method | annotation | new file | state | note | +|---|---|---|---|---| +| `selectMaxWithInOrderByMax` | cascades + `@MethodSource` | `permuted-min-max.yamsql` | ported, plan generated | in-union, comparison key (m, num_value_2, num_value_3_indexed). Three representative IN lists including a repeated value, since the source deduplicates its expectation, and an empty one `byLiteral` and `byConstantObjectValue` are the literal block; `byParameter` is the `-prepared` block. | +| `testMaxWithInAndDupes` | cascades + `@MethodSource` | `permuted-min-max-num-value-2.yamsql` | ported, plan generated | "dupes" is literal: (str_value_indexed, num_value_3_indexed) fixes i mod 10 and those ten records cover every residue mod 3, so max(num_value_2) is 2 in *every* group. Unordered — the whole sort key is tied `byLiteral` and `byConstantObjectValue` are the literal block; `byParameter` is the `-prepared` block. | +| `testSortedMaxWithInOnRepeater` | cascades + `@MethodSource` | `permuted-min-max-repeater.yamsql` | ported, plan generated | in-union over repeater values; representative lists from the source's 81 pairs `byLiteral` and `byConstantObjectValue` are the literal block; `byParameter` is the `-prepared` block. | +| `testMaxUniqueByStr2And3WithDifferentOrderingKeys` | cascades + `@MethodSource` | `permuted-min-max-ordering-keys.yamsql` | ported, plan generated | four ORDER BY prefixes, each with a different expected plan shape: in-**join** with no ordering constraint, in-**union** with one. Own file — the hook removes the unique index because the six extra records duplicate NUM_VALUE_UNIQUE. Unordered: the source uses `containsInAnyOrder` plus a separate monotonicity check that yamsql cannot express `byLiteral` and `byConstantObjectValue` are the literal block; `byParameter` is the `-prepared` block. | + +## `FDBSimpleQueryGraphTest` + +`fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/provider/foundationdb/query/FDBSimpleQueryGraphTest.java` + +| source method | annotation | new file | state | note | +|---|---|---|---|---| +| `testSimpleExistentialPredicateOnSimpleIndex` | cascades + `@BooleanSource` | `simple-query-graph-tag-index.yamsql` | ported, plan generated | both the EQUALS and IN variants The tag value is a parameter in the source, so both variants are parameter-injected in a `statement_type: prepared` block. | +| `testEqualityAndSimpleExistentialPredicate` | cascades | `simple-query-graph-name-tag-index.yamsql` | ported, plan generated | IN on a scalar column plus an existential over the array, one index over concat(name, tags.value) Both `name` and the tag value are parameters in the source, so they are parameter-injected in a `statement_type: prepared` block. | diff --git a/yaml-tests/src/test/resources/cascades-query-tests/README.md b/yaml-tests/src/test/resources/cascades-query-tests/README.md new file mode 100644 index 00000000000..7c49a5fd32f --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/README.md @@ -0,0 +1,418 @@ +# `cascades-query-tests` + +Query-planning coverage ported from the Cascades tests in `fdb-record-layer-core`'s +`com.apple.foundationdb.record.provider.foundationdb.query` package. + +Those tests drive the Cascades planner through the `RecordQuery` builder API. The SQL layer drives +the *same* planner from SQL text, so most of that coverage can live here instead, where plans, +planner metrics and result metadata are machine-maintained. + +Registered by `yaml-tests/src/test/java/CascadesQueryTests.java`. **The suite class is the only +registry** — a `.yamsql` file nobody references is silently never run. + +`MIGRATION.md` in this directory maps every source test method to its new location and current state, +and records the product divergences found while porting. + +## Status + +17 files. `MIGRATION.md` records the state of every source method. The core tests are untouched; retiring them is a +separate, later step. + +## Authoring conventions + +### One file per source index configuration — and no `USE INDEX` + +The core tests keep a deliberately minimal per-test index set (via `RecordMetaDataHook`) so the +planner is forced into one plan. **Reproduce that set exactly, in its own file when it differs.** + +Read the whole hook before writing the template. Many hooks are not additive — they call +`complexQuerySetupHook()` and then *remove* indexes, e.g. +`FDBAndQueryToIntersectionTest.testComplexQueryAndWithTwoChildren3` removes `multi_index`, +`MySimpleRecord$str_value_indexed` and `MySimpleRecord$num_value_3_indexed` before adding its own two. +Missing a `removeIndex` leaves an extra candidate in play and silently changes the plan. + +**Do not use `USE INDEX` to stand in for a smaller index set.** None of the source tests hint; the +ones that restrict access paths do it with `setAllowedIndexes(...)` or +`planGraph(..., allowedIndexes)`, which are recorded as not portable. A hint also changes behaviour +in its own right: `AccessHints.satisfies` +(`fdb-record-layer-core/.../cascades/AccessHints.java`) returns `false` when the query carries hints +and the candidate carries none, so a hinted query cannot fall back to a scan — it fails with +`UNSUPPORTED_QUERY`. + +Variant files are named with a suffix describing the index configuration, e.g. +`in-query-union-intersection-no-nv3.yamsql`. + +### One `schema_template` per file when the file uses `include:` + +Multiple `schema_template` blocks per file are supported and get connection indexes 1, 2, 3…, but +an included `setup:` with no explicit `connect:` resolves to *the* single registered connection URI +and fails outright when several exist +(`YamlExecutionContext.getConnectionFromConnectionURIList`). If a source class needs two index +configurations, make two files rather than two templates. + +### Index naming mirrors the core names + +Index names appear verbatim in explain strings, so pick them once and never rename: + +| Core | Here | +|---|---| +| `MySimpleRecord$str_value_indexed` | `MY_SIMPLE_RECORD_STR_VALUE_INDEXED` | +| `MySimpleRecord$num_value_unique` | `MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE` (unique) | +| `MySimpleRecord$num_value_3_indexed` | `MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED` | +| `multi_index` | `MULTI_INDEX` | +| `repeater$fanout` | `REPEATER_FANOUT` | + +### Copy the source's expected plan as a comment + +The core classes already carry the expected plan in a comment, e.g. +`// Index(multi_index [[even, 0, 3],[even, 0, 3]])`. Copy it above the query. That is what the +reviewer diffs the generated `explain:` against. + +### Mirror the source test + +These ports must be as truthful to the record-layer-core test they came from as possible. In +particular: + +- The `ORDER BY` mirrors the source's `setSort(...)` **exactly** — the same keys, the same + directions, nothing added and nothing dropped. Do **not** append the primary key as a tie-break to + make an ordered result deterministic, and do not drop a key because it happens to be + equality-bound. If the source declares no sort, there is no `ORDER BY`. +- The index set for each test matches the source's `RecordMetaDataHook`. +- Predicates and literals are as the source has them. +- Where SQL cannot express something, it is left out and the reason is recorded in the file header + and in the mapping table below — never approximated. + +Expected rows use ordered `result:` only where the row *sequence* is actually determined, namely: + +- the block has at most one row; or +- the query's `ORDER BY` keys are unique across the expected rows; or +- rows that tie on those keys are identical, so the sequence is invariant anyway (e.g. a query that + projects only its sort column); or +- the query has no `ORDER BY` but the source asserts a specific order and the plan the run produced + determines it; or +- the block is paged with `maxRows:`, where page membership depends on order regardless. + +Everywhere else use `unorderedResult:`. **Tie order within equal sort keys is not derivable by +reasoning** — an in-union under `ORDER BY col DESC` iterates the IN list descending but still scans +each leg forward, giving `(col desc, pk asc)`, whereas a reverse index scan gives `(col desc, pk +desc)`. Asserting a guessed tie order is stronger than the source and produces failures that say +nothing about the product. + +Ordering by a column outside the projection is fine — see `orderby.yamsql:256`, +`select c from t1 order by b`. + +### PERMUTED_MIN / PERMUTED_MAX: the permuted size is derived, not declared + +There is no option to set `IndexOptions.PERMUTED_SIZE_OPTION`. It is computed from where the aggregate +sits in the index's `ORDER BY` — `permutedSize = fieldValues.size() - aggregateOrderIndex` +(`MaterializedViewIndexGenerator:238-240`), where `fieldValues` are the GROUP BY values: + +| Core index | yamsql | +|---|---| +| `field("num_value_unique").groupBy(concatenateFields("num_value_2", "num_value_3_indexed"))`, PERMUTED_SIZE 1 | `select num_value_2, max(num_value_unique), num_value_3_indexed from t group by num_value_2, num_value_3_indexed order by num_value_2, max(num_value_unique), num_value_3_indexed` | +| the same over three grouping columns with PERMUTED_SIZE 2 | `... group by a, b, c order by a, max(x), b, c` | + +The ORDER BY must list the grouping values in their GROUP BY order with the aggregate inserted exactly +once, or the generator rejects it as "an attempt to create a covering aggregate index" +(`MaterializedViewIndexGenerator:229-231`). Everything after the aggregate is permuted behind it in the +index key, which is what makes `ORDER BY max(...)` free — and what turns an equality on one of those +trailing columns into a residual filter rather than a scan bound. + +### Nested messages, fan-out indexes and `oneOfThem()` + +A `repeated` nested message becomes a `STRUCT ARRAY`, and every construct the core tests build over it +has a SQL spelling: + +| Core key expression / filter | yamsql | +|---|---| +| `field("reviews", FanOut).nest("rating")` | `create index review_rating as select sq.rating from restaurant_record as r, (select rating from r.reviews) sq order by sq.rating` | +| `field("tags", FanOut).nest(concatenateFields("value", "weight"))` | `select sq.value, sq.weight from restaurant_record as r, (select value, weight from r.tags) sq order by sq.value, sq.weight` | +| `field("customer", FanOut)` (repeated scalar) | `select sq.c from restaurant_record as r, (select c from r.customer as c) sq order by sq.c` | +| `concat(field("customer", FanOut), field("name"))` | `select sq.c, r.name from restaurant_record as r, (select c from r.customer as c) sq order by sq.c, r.name` | +| `concat(field("other_id"), field("map").nest(field("entry", FanOut).nest("key")))` | `select r.other_id, sq.key from outer_record as r, (select key from r.map.entry) sq order by r.other_id, sq.key` | +| `field("stats").nest(concatenateFields("school_name", "start_date"))` | `select stats.school_name, stats.start_date from restaurant_reviewer order by stats.school_name, stats.start_date` | +| `Query.field("reviews").oneOfThem().matches(Query.field("rating").greaterThan(5))` | `where exists (select 1 from r.reviews as rv where rv.rating > 5)` | +| `setSort(field("stats").nest("start_date"))` | `order by stats.start_date` | +| `setPrimaryKey(concat(field("header").nest("path"), field("header").nest("rec_no")))` | `primary key(header.path, header.rec_no)` | + +`MaterializedViewIndexGenerator` builds a trie over the projected field paths, marking the accessor a +lateral explodes over (`AnnotatedAccessor`); only a marked array accessor gets `FanType.FanOut` +(`MaterializedViewIndexGenerator.java:810-826`). So a plain nested path stays a `nest`, a projection +can mix parent columns with an exploded one in either order, and the ORDER BY fixes the key order. + +Two naming traps: + +- **A projection cannot produce two columns with the same name.** `concat(alarmIndex.version, + eventIndex.version)` has to alias them apart; the ORDER BY still names the paths, which is what + determines the key. +- **`UUID` is a primitive type name in the grammar**, so a column called `uuid` needs quoting. + `key`, `value`, `start`, `end`, `name` and `version` do not — they are in `keywordsCanBeId` or + `functionNameBase`, both of which `simpleId` accepts. + +### One table per file, even when the source store has two + +`nestedMetaData` puts `RestaurantRecord` and `RestaurantReviewer` in one store, but every query pins a +`setRecordType`, so splitting them across files loses nothing — and it is what lets both files keep +`INTERMINGLE_TABLES=true` (see below): both tables key on a `bigint`, so without the record-type prefix +their primary keys would collide. + +### Schema templates use `INTERMINGLE_TABLES=true` + +Every `schema_template` here ends with `WITH OPTIONS(INTERMINGLE_TABLES=true)`. Without it the +Relational layer prefixes each table's primary key with the record-type key +(`RecordLayerSchemaTemplate.Builder.addTable` asserts +`Key.Expressions.recordType().isPrefixKey(...)`), which puts that designator *inside* every index +key. The core tests use a bare primary key, so intermingling both matches the source schema and +keeps index orderings contiguous — with the type key in the middle, an ordering like +`(num_value_3_indexed, rec_no)` is not reachable and the query fails to plan. Every file here +declares exactly one table, so sharing the primary-key space is harmless. + +### Parameters are injected, in a `statement_type: prepared` block + +A value the source writes as a literal is written as a literal. **`AstNormalizer` lifts every literal into +a `ConstantObjectValue`** regardless of statement type, which is why every generated explain shows +`promote(@c7 AS INT)` rather than an inline constant. + +A value the source binds as a parameter — `equalsParameter(...)`, `in("name")`, a list passed through the +`EvaluationContext` — is written as an explicit injection: `!! 0 !!` or `!! 'les aliments' !!` for a +scalar, `!! !in {[1, 3]} !!` for an `IN` list. In prepared mode `QueryInterpreter.getInjections` turns each +injection into a `?` and binds the value, so the query reaches the planner as a parameter, as in the source. + +Those queries go in a test block of their own, named `-prepared`, or in the file's only block when +every query in the file is parameterized. Either way the block sets + +```yaml + options: + statement_type: prepared +``` + +The option has to be pinned. `statement_type` defaults to `BOTH`, but `preset: single_repetition_ordered` +sets `repetition = 1`, and `TestBlock.getRunAsPreparedMix` (`block/TestBlock.java:517-521`) then returns a +single `random.nextBoolean()` — so exactly *one* of simple or prepared runs, and which one varies per run +because the seed defaults to `System.currentTimeMillis()` (`TestBlock.java:195`). In simple mode an +injection is spliced back into the SQL text as a literal, so a `BOTH` block would test the parameter only +half the time. Literal queries stay in the main block: a source test that has both a literal and a +parameter variant becomes one query in each block. + +### `preset: single_repetition_ordered` on every block + +These are deterministic plan-shape tests; the default repetition of 5 buys only plan-cache churn. + +## Shared fixtures + +`includes/simple-record-100.yamsql` is the SQL equivalent of +`FDBRecordStoreQueryTestBase.complexQuerySetup` — 100 `MY_SIMPLE_RECORD` rows with + +``` +rec_no = i, str_value_indexed = (i & 1) == 1 ? 'odd' : 'even', num_value_unique = 1000 - i, +num_value_2 = i % 3, num_value_3_indexed = i % 5, repeater = [0 .. i % 10) +``` + +Include it after the `schema_template` and before the `test_block`: + +```yaml +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +``` + +The includer must declare exactly one `schema_template` containing a `MY_SIMPLE_RECORD` table with +those six columns. + +## Known differences from the core tests + +- **Record-type prefix — eliminated.** `WITH OPTIONS(INTERMINGLE_TABLES=true)` on every schema + template gives each table the bare primary key the core tests use, so this is no longer a + divergence. See *Schema templates use `INTERMINGLE_TABLES=true`* above for why it is required. +- **Unset `ARRAY` columns are `NULL` here, empty lists in the record layer.** A `repeated` proto + field that was never set reads back as an empty list; a SQL `ARRAY` column omitted from an + `INSERT` reads back as `NULL`. +- **The record layer's blanket duplicate removal is gone.** `RecordQuery.removeDuplicates` defaults to + `true`, so `RelationalExpression.fromRecordQuery:163` wraps every record-layer graph in a + `LogicalDistinctExpression`. The SQL front end adds no such thing — correctly, since SQL does not + deduplicate rows. So an `unorderedPrimaryKeyDistinctPlan` in a source assertion is usually *not* a + planner decision to reproduce. +- **A SQL `ARRAY` is always the *wrapped* proto form.** `NullableArrayUtils.wrapArray` is applied to + every generated index key, so a fan-out index ported from a plain `repeated` field is structurally + the `…List { repeated X values }` variant. Behaviour is identical and the key expression gains a + `values` hop. It also means a core test that exists to contrast the wrapped and unwrapped shapes — + `FDBSimpleQueryGraphTest.testSimplePlanGraphWithNullableArray` — has no independent SQL content. +- **`setRequiredResults` is not a projection.** It only steers the covering-index decision; the record + layer still returns whole records. So a source method that sets it still wants `select *`, and the + covering difference is unavoidable. Contrast the rule above: it is the *absence* of + `setRequiredResults` that makes a narrow SQL projection wrong, because that changes the plan. +- **A source test that asserts a full record scan will get an index scan instead.** The relational layer + hard-wires `IndexScanPreference.PREFER_INDEX` (`PlannerConfiguration.java:160`), and the Cascades cost + model consults it when comparing an index scan against a primary scan — `PREFER_SCAN` prefers the scan, + anything else prefers the index (`PlanningCostModel.java:497-501`). Nothing on the SQL surface sets that + preference. So `typeFilterPlan(scanPlan(unbounded))` and `typeFilter(anything(), scan(unbounded()))` + assertions are unreachable whenever *any* index could serve the query, even a worse one: an unfiltered + `select *` picks an arbitrary index plus a fetch, and a filter on an unindexed column scans an unrelated + index with the predicate residual. Affects `in-query.yamsql` (`testInQueryNoIndex`), where the IN on the + unindexed `NUM_VALUE_2` is a residual filter over a full scan of `MY_SIMPLE_RECORD_STR_VALUE_INDEXED`; the + load-bearing half — that no index serves the IN — survives. Watch for it as a row-order surprise: the + expected order becomes the index key order, not the primary key order. + `select * from my_simple_record` with no filter plans as `ISCAN(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED <,>)` + and emits rows in `(num_value_3_indexed, rec_no)` order. +- **Store-counter assertions are lost.** `assertDiscardedNone` / `assertDiscardedAtMost` / + `assertLoadRecord` have no yamsql equivalent. +- **Structural plan matchers become explain strings.** `assertMatchesExactly(plan, unionOnValuesPlan(...))` + degrades to a string comparison, which is weaker and drifts with unrelated planner work. Prefer + `explainContains:` on the load-bearing fragment where the point is only "index X is used". +- **Plan hashes are weaker.** `planHash:` exists but is skipped in multi-server configs; + `QueryPlanHashTest` remains the home for hash stability. + +## Gotchas + +- **`SELECT DISTINCT` parses but is silently ignored** — no visitor reads `DISTINCT()`. Do not use + it to stand in for `setRemoveDuplicates(true)`; it produces wrong results with no error. +- **`USE INDEX` excludes the primary-scan candidate** (see above). +- **`LIMIT`/`OFFSET` are rejected.** Row limits are expressed with `maxRows:` plus one `result:` + block per page. +- The runner **reads** from `build/resources/test/` but **writes** corrections to + `src/test/resources/`. Re-run after a correction pass before comparing. + +### Index states: `set schema state`, and its one-shot limitation + +An index can be put in `WRITE_ONLY` or `DISABLED` state, which is how `markIndexWriteOnly` / +`markIndexDisabled` port. The directive needs the `database_id` and schema `name` in its JSON +(`Command.java:189-190`), so the file cannot use an implicit `schema_template` — create the +template, database and schema by hand and give the test block an explicit `connect:`. `store_info.formatVersion` is required (`Command.java:186`); +pass 2, as every existing user does. Every `setup:` block in such a file also needs +`connect: "jdbc:embed:/__SYS?schema=CATALOG"` — with no `schema_template` there is no default +connection URI and `SetupBlock$ManualSetupBlock.parse:115` resolves one eagerly. + +**It is setup-only.** All seven pre-existing users call it exactly once, before any query, and there +is no per-query form — so a source test that changes an index state *mid-test* (restrict, assert, +re-enable, assert again) can only have its first phase ported. + +### Parking a query that a defect breaks + +**Do not `@Disabled` the whole suite method** — that switches off every working query in the file as +collateral, and twice it hid the very entries that would have narrowed the defect. There is no +"disabled" directive, but `supported_version` is accepted per query and gives exactly the right +granularity: `QueryConfig.getSupportedVersionConfig` returns a `SkipConfig` when the check fails, and +`QueryCommand.parse:101-108` turns that command into a `SkippedCommand` — skipping **that query only**. + +Use a sentinel version no server will ever reach: + +```yaml + - query: select … where rest_no > 1 and true + # ── SKIPPED, NOT PASSING ───────────────────────────────────────────────────────────────────── + # + # + # `supported_version: "!max_version"` is a *sentinel*, not a real version floor … + - supported_version: "!max_version" + - explain: "" + - result: [ … the correct answer, left as it is … ] +``` + +**It must be the quoted MAX singleton.** A numeric floor does *not* work, and this cost a wasted run: +`SemanticVersionType` orders `MIN < NORMAL < CURRENT < MAX` and `SemanticVersion.compareTo` compares **by +type first** (`SemanticVersion.java:110-115, 295-301`), so `!current_version` outranks *every* numeric +version. `SupportedVersionCheck.parse` asks `supportedVersion.lesserVersions(versionsUnderTest)`, which +comes back empty for a numeric sentinel against `[!current_version]` — reported supported, nothing +skipped. `"!max_version"` outranks `CURRENT`, so it always skips. Quote it: only `!current_version` has a +registered YAML tag (`tags/CurrentVersionTag.java`), so a bare `!max_version` fails construction, while +the quoted string reaches `SemanticVersion.parse`, which resolves singleton texts before the numeric +pattern (`:219-223`). + +Place it after the `query:` and before the first result config — a non-result config cannot follow a +result one (`QueryConfig.parseConfigs`). + +**The sentinel only works if the block keeps at least one runnable query.** A `SkippedCommand` +contributes no executable, and `TestBlock.parse:409` asserts +`!executables.isEmpty()` — a block whose every query is skipped fails to parse outright with "have no +tests to execute". So: + +| the file has | use | +|---|---| +| other working queries | the per-query `supported_version` sentinel | +| only the failing query | `@Disabled` on the suite method — there is no collateral to protect | + +**Verify the skip actually happened.** A sentinel that silently fails to skip looks exactly like a normal +failing test, and the file will report the real defect as a fresh failure. After adding one, confirm the +run logs the query as skipped rather than executed. + +Rules: + +- **Keep the correct expectation.** Never rewrite it to match the defect, and never weaken the query + (dropping a sort, narrowing a projection) to make it plan — that produces a file which passes while no + longer testing what its source tests. +- **Say what breaks and why, next to the query**, not in the Java driver: the reader is in the yamsql. +- Mark the method **ported, FAILING** in `MIGRATION.md` and add the analysis under *Known product + divergences found while porting*. +- Delete the sentinel and the note when the defect is fixed; the `explain: ""` then fills in on the next + correction pass. + +The two things `supported_version` costs: the query never runs, so its `explain:` stays empty and the +file reports as passing. Neither is visible from a test report — `MIGRATION.md`'s FAILING count is the +only place the total is tracked. + +## Verifying a generated plan against the source assertion + +Run after each correction pass. **Compare against the source's `assertMatchesExactly` for the *Cascades* +branch, never against its `//` comment.** Tests parameterized on deferred fetch carry two plan comments — +`FDBAndQueryToIntersectionTest.testComplexQueryAndWithTwoChildren` has `Index(…) ∩ Index(…)` on one line +and `Fetch(Covering(…) ∩ Covering(…))` on the next — and the Cascades assertion is the second. Reading the +first produces spurious "divergences" in every covering plan. + +Plan-hash assertions are out of scope: `planHash:` is skipped in multi-server configs, and +`QueryPlanHashTest` is the home for hash stability. + +What to compare: the operator set (scan kind, covering, fetch, union/intersection kind, in-join vs +in-union, distinct, aggregate), the index names, and whether each scan bound is an equality or a range. +Ignore `MAP` — it is a projection artefact, never an access-path decision. + +### Systematic divergences — expected, do not re-investigate per file + +| divergence | why | +|---|---| +| SQL plans a full **index** scan where the source plans a full **record** scan | any index whose entries cover the projection is cheaper than reading records; seen in `in-query.yamsql` (`testInQueryNoIndex`) and `in-query-or.yamsql` (`testInQueryEmptyList`). The load-bearing assertion in those tests — that a *particular* index is not used — still holds | +| covering + fetch where the source has a plain index scan, or the reverse | follows the projection. A source query with no `setRequiredResults` returns whole records; write `select *`, not a narrow column list, or the plan legitimately changes | +| in-join where the source has an in-union, or the reverse | follows `attemptFailedInJoinAsUnionMaxSize`. The SQL layer pins it at 24; the core tests set it per test (10, 20, …). Not exposed, so not reproducible | +| the source has `unorderedPrimaryKeyDistinctPlan` and the SQL plan has no distinct | `RecordQuery.removeDuplicates` defaults to `true`, so `RelationalExpression.fromRecordQuery:163` wraps **every** record-layer graph in a `LogicalDistinctExpression`. It is not a planner decision. Sound wherever the fan-out component is equality-bound; where it is range-bounded the rows can differ | +| unordered-union + distinct where the SQL plan has an ordered union | same root cause as the row above | + +## Filling in and updating explains + +Every query has an exact `explain:`, and every file has its `.metrics.binpb` and +`.metrics.yaml` companions, because an exact `explain:` also enforces the planner metrics. A new +query starts with an `- explain: ""` placeholder; a planner change that moves a plan or its metrics shows +up as a failure on the existing expectation. Either way, use **`CORRECT_EXPECTATIONS`** — not +`ADD_EXPLAINS`, which only inserts into blocks that have no explain at all, and does not correct metrics. + +Locally only — CI rejects a committed `@MaintainYamlTestConfig`: + +1. Add `@MaintainYamlTestConfig(YamlTestConfigFilters.CORRECT_EXPECTATIONS)` to the `@TestTemplate` + method (or to the class, to do the whole suite at once). +2. `./gradlew :yaml-tests:quickTest --tests 'CascadesQueryTests'` +3. The framework rewrites each `explain:` that differs (or is empty) in place with the actual plan, and + rewrites `.metrics.binpb` and `.metrics.yaml`. +4. Review each changed explain against the `#` plan comment above the query — that comment is the + plan the source test asserted, and the two should describe the same shape. +5. **Remove the annotation** before committing. + +Note that the runner reads from `build/resources/test/` but writes corrections to +`src/test/resources/`, so re-run before comparing. + +## Source-class mapping + +Per-method detail — every source method, the file it landed in, and its current state — is in +`MIGRATION.md`. This table is the summary. + +| Source class | File(s) | +|---|---| +| `FDBInQueryTest` | `in-query`, `-compound-index`, `-multi-index`, `-header`, `-or`, `-or-overlap`, `-or-compound`, `-sorted-extra-unordered-columns`, `-union-sargable`, `-union-intersection`, `-union-intersection-no-nv3` | +| `FDBPermutedMinMaxQueryTest` | `permuted-min-max`, `-num-value-2`, `-repeater`, `-ordering-keys` | +| `FDBSimpleQueryGraphTest` | `simple-query-graph-tag-index`, `-name-tag-index` | + +## Stays in JUnit, and why + +No source class moves wholesale; the core tests stay where they are (see *Status*). Of the methods +`MIGRATION.md` maps, one is **not portable** and stays a JUnit test permanently: + +| Method | Why | +|---|---| +| `FDBInQueryTest.testInQueryParameterBad` | binds a non-list to an `IN` parameter; a SQL `IN` list is always a list | + diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.metrics.binpb new file mode 100644 index 00000000000..f08d449fdcf --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.metrics.binpb @@ -0,0 +1,65 @@ +à +ƒ +in-query-compound-indexhEXPLAIN select * from my_simple_record where num_value_3_indexed in (1, 4, 2) order by str_value_indexedº +››Š› +É Ý’½(I0õßG8H@ç[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[1]]) } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) | FETCH±digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
In Union
COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Values
VALUES([__corr_q51 IN arrayDistinct(@c7)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS q51]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
COMPOUND_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q86> label="q86" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +}¤ +À +in-query-compound-index¤EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, 4, 2) and str_value_indexed > 'bar' and str_value_indexed < 'foo' order by str_value_indexedÞ +ª¥¾® ‚ ¥³¬(_0‡¼_8k@´[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS q0, [GREATER_THAN promote(@c17 AS STRING) && LESS_THAN promote(@c21 AS STRING)]] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[1]]) } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) | FETCHˆdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
In Union
COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS q58, [GREATER_THAN promote(@c17 AS STRING) && LESS_THAN promote(@c21 AS STRING)]]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Values
VALUES([__corr_q58 IN arrayDistinct(@c7)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
COMPOUND_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q114> label="q114" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 4 -> 3 [ color="red" style="invis" ]; + } +}¥ +Á + in-query-compound-index-preparedœEXPLAIN select * from my_simple_record where num_value_3_indexed in ? and str_value_indexed > 'bar' and str_value_indexed < 'foo' order by str_value_indexedÞ +ª�‹Ž ‚ Äî‹(_0 Î[8k@´[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS q0, [GREATER_THAN promote(@c11 AS STRING) && LESS_THAN promote(@c15 AS STRING)]] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[1]]) } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) | FETCHˆdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
In Union
COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Values
VALUES([__corr_q58 IN arrayDistinct(@c7)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS q58, [GREATER_THAN promote(@c11 AS STRING) && LESS_THAN promote(@c15 AS STRING)]]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
COMPOUND_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q114> label="q114" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.metrics.yaml new file mode 100644 index 00000000000..d16be0e7a3b --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.metrics.yaml @@ -0,0 +1,48 @@ +in-query-compound-index: +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, + 4, 2) order by str_value_indexed + ref: in-query-compound-index.yamsql:59 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS + q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: + KEY:[1]]) } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) + | FETCH' + task_count: 667 + task_total_time_ms: 21 + transform_count: 201 + transform_time_ms: 11 + transform_yield_count: 73 + insert_time_ms: 1 + insert_new_count: 72 + insert_reused_count: 3 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, + 4, 2) and str_value_indexed > 'bar' and str_value_indexed < 'foo' order by + str_value_indexed + ref: in-query-compound-index.yamsql:127 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS + q0, [GREATER_THAN promote(@c17 AS STRING) && LESS_THAN promote(@c21 AS STRING)]] + -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[1]]) + } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 938 + task_total_time_ms: 25 + transform_count: 258 + transform_time_ms: 13 + transform_yield_count: 95 + insert_time_ms: 1 + insert_new_count: 107 + insert_reused_count: 3 +in-query-compound-index-prepared: +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in ? and + str_value_indexed > 'bar' and str_value_indexed < 'foo' order by str_value_indexed + ref: in-query-compound-index.yamsql:172 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS + q0, [GREATER_THAN promote(@c11 AS STRING) && LESS_THAN promote(@c15 AS STRING)]] + -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[1]]) + } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 938 + task_total_time_ms: 25 + transform_count: 258 + transform_time_ms: 12 + transform_yield_count: 95 + insert_time_ms: 1 + insert_new_count: 107 + insert_reused_count: 3 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.yamsql new file mode 100644 index 00000000000..f5c2c322c06 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-compound-index.yamsql @@ -0,0 +1,203 @@ +# +# in-query-compound-index.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest` — the IN tests that need COMPOUND_INDEX over +# (num_value_3_indexed, str_value_indexed), where the IN is on the *first* index column and the sort +# is on the second. +# +# `inQueryWithSortAndParameter` takes its IN list as a parameter, so it is a parameter-injected query in a +# second block that runs as `statement_type: prepared`. +# +# The source's `InAsOrUnionMode` parameterization is not reproduced: the SQL layer pins +# `attemptFailedInJoinAsUnionMaxSize = 24` and exposes no way to choose between in-join, in-union and +# OR-of-equalities. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create index compound_index as select num_value_3_indexed, str_value_indexed from my_simple_record + order by num_value_3_indexed, str_value_indexed + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-compound-index + preset: single_repetition_ordered + tests: + - + # inQueryWithSortBySecondFieldOfCompoundIndex — the in-union's comparison key is + # (str_value_indexed, rec_no), which is what supplies the requested order. + - query: select * from my_simple_record where num_value_3_indexed in (1, 4, 2) + order by str_value_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[1]]) } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) | FETCH" + - unorderedResult: [{REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # inQueryWithSortAndRangePredicateOnSecondFieldOfCompoundIndex — a range on the sort column + # narrows each in-union leg. 'even' falls inside ('bar', 'foo'); 'odd' does not. + - query: select * from my_simple_record + where num_value_3_indexed in (1, 4, 2) + and str_value_indexed > 'bar' and str_value_indexed < 'foo' + order by str_value_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS q0, [GREATER_THAN promote(@c17 AS STRING) && LESS_THAN promote(@c21 AS STRING)]] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[1]]) } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) | FETCH" + - unorderedResult: [{REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}] +--- +test_block: + name: in-query-compound-index-prepared + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # inQueryWithSortAndParameter — the same query with the IN list supplied as a parameter (a prepared + # statement binding an array to the IN). + - query: select * from my_simple_record + where num_value_3_indexed in !! !in {[1, 4, 2]} !! + and str_value_indexed > 'bar' and str_value_indexed < 'foo' + order by str_value_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { COVERING(COMPOUND_INDEX [EQUALS q0, [GREATER_THAN promote(@c11 AS STRING) && LESS_THAN promote(@c15 AS STRING)]] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[1]]) } COMPARE BY (_.STR_VALUE_INDEXED, _.REC_NO, _.NUM_VALUE_3_INDEXED) | FETCH" + - unorderedResult: [{REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.metrics.binpb new file mode 100644 index 00000000000..876cde99ad9 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.metrics.binpb @@ -0,0 +1,103 @@ +‘ +Œ +in-query-headeryEXPLAIN select * from my_record where header.rec_no = 1 and header.path in ('String6', 'String1', 'String25', 'String11')ÿ +›¶°Žo� ¼…“A(I0¦´�8…@¬[IN arrayDistinct(@c15)] | INJOIN q0 -> { COVERING(IND [EQUALS promote(@c9 AS LONG), EQUALS q0] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } | FETCH¯digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 3 [ label=<
Table Function
EXPLODE(arrayDistinct(@c15))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c9 AS LONG), EQUALS q45]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 5 [ label=<
Index
IND
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q108> label="q108" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +}— +’ +in-query-headerEXPLAIN select * from my_record where header.rec_no in (1, 4) and header.path in ('String6', 'String25', 'String1', 'String34')ÿ +û´Ö¥cÕ ½’• (Ÿ0»ì� 8•@(Ý[IN arrayDistinct(promote(@c9 AS ARRAY(LONG)))] | INJOIN q0 -> { [IN arrayDistinct(@c19)] | INJOIN q1 -> { COVERING(IND [EQUALS q0, EQUALS q1] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } } | FETCHýdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 3 [ label=<
Table Function
EXPLODE(arrayDistinct(promote(@c9 AS ARRAY(LONG))))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 4 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 5 [ label=<
Table Function
EXPLODE(arrayDistinct(@c19))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 6 [ label=<
Covering Index Scan
comparisons: [EQUALS q45, EQUALS q47]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 7 [ label=<
Index
IND
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 4 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 6 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q138> label="q138" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } + { + rank=same; + rankDir=LR; + 5 -> 6 [ color="red" style="invis" ]; + } +}Ê +· +in-query-header£EXPLAIN select * from my_record where header.rec_no in (1, 4) and header.path in ('String6', 'String25', 'String1', 'String34') order by header.rec_no, header.path� +¼Ù€ÿK— ±•†(­0‹ãÂ8…@(ë[IN arrayDistinct(promote(@c9 AS ARRAY(LONG))) SORTED] | INJOIN q0 -> { [IN arrayDistinct(@c19) SORTED] | INJOIN q1 -> { COVERING(IND [EQUALS q0, EQUALS q1] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } } | FETCHýdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 3 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 4 [ label=<
Table Function
EXPLODE(arrayDistinct(promote(@c9 AS ARRAY(LONG))))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 5 [ label=<
Table Function
EXPLODE(arrayDistinct(@c19))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 6 [ label=<
Covering Index Scan
comparisons: [EQUALS q36, EQUALS q38]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 7 [ label=<
Index
IND
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 6 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q140> label="q140" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 4 -> 3 [ color="red" style="invis" ]; + } + { + rank=same; + rankDir=LR; + 5 -> 6 [ color="red" style="invis" ]; + } +}‘ +Œ +in-query-headeryEXPLAIN select * from my_record where header.rec_no = 1 and header.path in ('String1', 'String6', 'String25', 'String11')ÿ +›¶°Žo� ¼…“A(I0¦´�8…@¬[IN arrayDistinct(@c15)] | INJOIN q0 -> { COVERING(IND [EQUALS promote(@c9 AS LONG), EQUALS q0] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } | FETCH¯digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 3 [ label=<
Table Function
EXPLODE(arrayDistinct(@c15))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c9 AS LONG), EQUALS q45]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 5 [ label=<
Index
IND
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS PATH, INT AS NUM AS HEADER, STRING AS STR_VALUE, ARRAY(LONG) AS REPEATED_INT)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q108> label="q108" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.metrics.yaml new file mode 100644 index 00000000000..9be2037e7cc --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.metrics.yaml @@ -0,0 +1,73 @@ +in-query-header: +- query: EXPLAIN select * from my_record where header.rec_no = 1 and header.path + in ('String6', 'String1', 'String25', 'String11') + ref: in-query-header.yamsql:159 + explain: '[IN arrayDistinct(@c15)] | INJOIN q0 -> { COVERING(IND [EQUALS promote(@c9 + AS LONG), EQUALS q0] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: + KEY:[0]]]) } | FETCH' + task_count: 1051 + task_total_time_ms: 233 + transform_count: 257 + transform_time_ms: 136 + transform_yield_count: 73 + insert_time_ms: 12 + insert_new_count: 133 + insert_reused_count: 7 +- query: EXPLAIN select * from my_record where header.rec_no in (1, 4) and header.path + in ('String6', 'String25', 'String1', 'String34') + ref: in-query-header.yamsql:171 + explain: '[IN arrayDistinct(promote(@c9 AS ARRAY(LONG)))] | INJOIN q0 -> { [IN + arrayDistinct(@c19)] | INJOIN q1 -> { COVERING(IND [EQUALS q0, EQUALS q1] + -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } } | FETCH' + task_count: 2811 + task_total_time_ms: 208 + transform_count: 853 + transform_time_ms: 67 + transform_yield_count: 159 + insert_time_ms: 25 + insert_new_count: 405 + insert_reused_count: 40 +- query: EXPLAIN select * from my_record where header.rec_no in (1, 4) and header.path + in ('String6', 'String25', 'String1', 'String34') order by header.rec_no, + header.path + ref: in-query-header.yamsql:185 + explain: '[IN arrayDistinct(promote(@c9 AS ARRAY(LONG))) SORTED] | INJOIN q0 -> + { [IN arrayDistinct(@c19) SORTED] | INJOIN q1 -> { COVERING(IND [EQUALS q0, + EQUALS q1] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) + } } | FETCH' + task_count: 3132 + task_total_time_ms: 159 + transform_count: 919 + transform_time_ms: 63 + transform_yield_count: 173 + insert_time_ms: 17 + insert_new_count: 389 + insert_reused_count: 40 +- query: EXPLAIN select * from my_record where header.rec_no = 1 and header.path + in ('String6', 'String1', 'String25', 'String11') + ref: in-query-header.yamsql:198 + explain: '[IN arrayDistinct(@c15)] | INJOIN q0 -> { COVERING(IND [EQUALS promote(@c9 + AS LONG), EQUALS q0] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: + KEY:[0]]]) } | FETCH' + task_count: 1051 + task_total_time_ms: 233 + transform_count: 257 + transform_time_ms: 136 + transform_yield_count: 73 + insert_time_ms: 12 + insert_new_count: 133 + insert_reused_count: 7 +- query: EXPLAIN select * from my_record where header.rec_no = 1 and header.path + in ('String1', 'String6', 'String25', 'String11') + ref: in-query-header.yamsql:213 + explain: '[IN arrayDistinct(@c15)] | INJOIN q0 -> { COVERING(IND [EQUALS promote(@c9 + AS LONG), EQUALS q0] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: + KEY:[0]]]) } | FETCH' + task_count: 1051 + task_total_time_ms: 233 + transform_count: 257 + transform_time_ms: 136 + transform_yield_count: 73 + insert_time_ms: 12 + insert_new_count: 133 + insert_reused_count: 7 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.yamsql new file mode 100644 index 00000000000..b055a2e126d --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-header.yamsql @@ -0,0 +1,222 @@ +# +# in-query-header.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest` — the IN tests built on `test_records_with_header.proto`, where the IN +# predicates apply to columns inside a nested `header` struct and the index is over +# (header.rec_no, header.path). +# +# The fixture mirrors `setupRecordsWithHeader`: 100 rows with +# str_value = '_' + i, header.rec_no = i % 5, header.path = 'String' + (i % 50), header.num = i +# and the primary key is STR_VALUE. +# +# The source's `normalizeNestedFields` parameterization is not reproduced — it is read only by +# `BooleanNormalizer` on the old-planner path. +# +# `testInWithLimit` and `testInWithContinuation` have their own queries: a `maxRows: 3` run (SQL has no +# LIMIT clause) and a `maxRows: 1` pagination run. The `ForceContinuations` test configuration additionally re-runs every query here through +# continuations. +--- +schema_template: + create type as struct header_record(rec_no bigint, path string, num integer) + create table my_record(header header_record, str_value string, repeated_int bigint array, + primary key(str_value)) + create index ind as select header.rec_no, header.path from my_record + order by header.rec_no, header.path + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +setup: + steps: + - query: INSERT INTO MY_RECORD (HEADER, STR_VALUE) + VALUES + ((0, 'String0', 0), '_0'), + ((1, 'String1', 1), '_1'), + ((2, 'String2', 2), '_2'), + ((3, 'String3', 3), '_3'), + ((4, 'String4', 4), '_4'), + ((0, 'String5', 5), '_5'), + ((1, 'String6', 6), '_6'), + ((2, 'String7', 7), '_7'), + ((3, 'String8', 8), '_8'), + ((4, 'String9', 9), '_9'), + ((0, 'String10', 10), '_10'), + ((1, 'String11', 11), '_11'), + ((2, 'String12', 12), '_12'), + ((3, 'String13', 13), '_13'), + ((4, 'String14', 14), '_14'), + ((0, 'String15', 15), '_15'), + ((1, 'String16', 16), '_16'), + ((2, 'String17', 17), '_17'), + ((3, 'String18', 18), '_18'), + ((4, 'String19', 19), '_19'), + ((0, 'String20', 20), '_20'), + ((1, 'String21', 21), '_21'), + ((2, 'String22', 22), '_22'), + ((3, 'String23', 23), '_23'), + ((4, 'String24', 24), '_24'), + ((0, 'String25', 25), '_25'), + ((1, 'String26', 26), '_26'), + ((2, 'String27', 27), '_27'), + ((3, 'String28', 28), '_28'), + ((4, 'String29', 29), '_29'), + ((0, 'String30', 30), '_30'), + ((1, 'String31', 31), '_31'), + ((2, 'String32', 32), '_32'), + ((3, 'String33', 33), '_33'), + ((4, 'String34', 34), '_34'), + ((0, 'String35', 35), '_35'), + ((1, 'String36', 36), '_36'), + ((2, 'String37', 37), '_37'), + ((3, 'String38', 38), '_38'), + ((4, 'String39', 39), '_39'), + ((0, 'String40', 40), '_40'), + ((1, 'String41', 41), '_41'), + ((2, 'String42', 42), '_42'), + ((3, 'String43', 43), '_43'), + ((4, 'String44', 44), '_44'), + ((0, 'String45', 45), '_45'), + ((1, 'String46', 46), '_46'), + ((2, 'String47', 47), '_47'), + ((3, 'String48', 48), '_48'), + ((4, 'String49', 49), '_49'), + ((0, 'String0', 50), '_50'), + ((1, 'String1', 51), '_51'), + ((2, 'String2', 52), '_52'), + ((3, 'String3', 53), '_53'), + ((4, 'String4', 54), '_54'), + ((0, 'String5', 55), '_55'), + ((1, 'String6', 56), '_56'), + ((2, 'String7', 57), '_57'), + ((3, 'String8', 58), '_58'), + ((4, 'String9', 59), '_59'), + ((0, 'String10', 60), '_60'), + ((1, 'String11', 61), '_61'), + ((2, 'String12', 62), '_62'), + ((3, 'String13', 63), '_63'), + ((4, 'String14', 64), '_64'), + ((0, 'String15', 65), '_65'), + ((1, 'String16', 66), '_66'), + ((2, 'String17', 67), '_67'), + ((3, 'String18', 68), '_68'), + ((4, 'String19', 69), '_69'), + ((0, 'String20', 70), '_70'), + ((1, 'String21', 71), '_71'), + ((2, 'String22', 72), '_72'), + ((3, 'String23', 73), '_73'), + ((4, 'String24', 74), '_74'), + ((0, 'String25', 75), '_75'), + ((1, 'String26', 76), '_76'), + ((2, 'String27', 77), '_77'), + ((3, 'String28', 78), '_78'), + ((4, 'String29', 79), '_79'), + ((0, 'String30', 80), '_80'), + ((1, 'String31', 81), '_81'), + ((2, 'String32', 82), '_82'), + ((3, 'String33', 83), '_83'), + ((4, 'String34', 84), '_84'), + ((0, 'String35', 85), '_85'), + ((1, 'String36', 86), '_86'), + ((2, 'String37', 87), '_87'), + ((3, 'String38', 88), '_88'), + ((4, 'String39', 89), '_89'), + ((0, 'String40', 90), '_90'), + ((1, 'String41', 91), '_91'), + ((2, 'String42', 92), '_92'), + ((3, 'String43', 93), '_93'), + ((4, 'String44', 94), '_94'), + ((0, 'String45', 95), '_95'), + ((1, 'String46', 96), '_96'), + ((2, 'String47', 97), '_97'), + ((3, 'String48', 98), '_98'), + ((4, 'String49', 99), '_99') +--- +test_block: + name: in-query-header + preset: single_repetition_ordered + tests: + - + # testInWithNesting — an equality on the first index column and an IN on the second. Only + # 'String6', 'String1' and 'String11' can coexist with header.rec_no = 1; 'String25' matches + # rows whose rec_no is 0. + - query: select * from my_record + where header.rec_no = 1 + and header.path in ('String6', 'String1', 'String25', 'String11') + - explain: "[IN arrayDistinct(@c15)] | INJOIN q0 -> { COVERING(IND [EQUALS promote(@c9 AS LONG), EQUALS q0] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } | FETCH" + - unorderedResult: [{HEADER: {REC_NO: 1, PATH: 'String1', NUM: 1}, STR_VALUE: '_1', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String6', NUM: 6}, STR_VALUE: '_6', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String11', NUM: 11}, STR_VALUE: '_11', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String1', NUM: 51}, STR_VALUE: '_51', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String6', NUM: 56}, STR_VALUE: '_56', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String11', NUM: 61}, STR_VALUE: '_61', REPEATED_INT: !null _}] + - + # testMultipleInQueryIndex — an IN on *both* index columns, so the plan nests two in-unions. + - query: select * from my_record + where header.rec_no in (1, 4) + and header.path in ('String6', 'String25', 'String1', 'String34') + - explain: "[IN arrayDistinct(promote(@c9 AS ARRAY(LONG)))] | INJOIN q0 -> { [IN arrayDistinct(@c19)] | INJOIN q1 -> { COVERING(IND [EQUALS q0, EQUALS q1] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } } | FETCH" + - unorderedResult: [{HEADER: {REC_NO: 1, PATH: 'String1', NUM: 1}, STR_VALUE: '_1', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String6', NUM: 6}, STR_VALUE: '_6', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 4, PATH: 'String34', NUM: 34}, STR_VALUE: '_34', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String1', NUM: 51}, STR_VALUE: '_51', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String6', NUM: 56}, STR_VALUE: '_56', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 4, PATH: 'String34', NUM: 84}, STR_VALUE: '_84', REPEATED_INT: !null _}] + - + # testMultipleInQueryIndexSorted — the same query with the index order requested explicitly, so + # the nested in-unions have to produce (header.rec_no, header.path, str_value) order. + - query: select * from my_record + where header.rec_no in (1, 4) + and header.path in ('String6', 'String25', 'String1', 'String34') + order by header.rec_no, header.path + - explain: "[IN arrayDistinct(promote(@c9 AS ARRAY(LONG))) SORTED] | INJOIN q0 -> { [IN arrayDistinct(@c19) SORTED] | INJOIN q1 -> { COVERING(IND [EQUALS q0, EQUALS q1] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } } | FETCH" + - unorderedResult: [{HEADER: {REC_NO: 1, PATH: 'String1', NUM: 1}, STR_VALUE: '_1', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String1', NUM: 51}, STR_VALUE: '_51', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String6', NUM: 56}, STR_VALUE: '_56', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String6', NUM: 6}, STR_VALUE: '_6', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 4, PATH: 'String34', NUM: 34}, STR_VALUE: '_34', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 4, PATH: 'String34', NUM: 84}, STR_VALUE: '_84', REPEATED_INT: !null _}] + - + # testInWithLimit — the same IN join cut off by a returned-row limit (`maxRows: 3`; SQL has no LIMIT clause). The IN list is walked in the order given, + # so 'String6' (_56, _6) comes before 'String1' (_1) and the first page of three rows stops there. + - query: select * from my_record + where header.rec_no = 1 + and header.path in ('String6', 'String1', 'String25', 'String11') + - explain: "[IN arrayDistinct(@c15)] | INJOIN q0 -> { COVERING(IND [EQUALS promote(@c9 AS LONG), EQUALS q0] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } | FETCH" + - maxRows: 3 + - result: [{HEADER: {REC_NO: 1, PATH: 'String6', NUM: 56}, STR_VALUE: '_56', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String6', NUM: 6}, STR_VALUE: '_6', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String1', NUM: 1}, STR_VALUE: '_1', REPEATED_INT: !null _}] + - result: [{HEADER: {REC_NO: 1, PATH: 'String1', NUM: 51}, STR_VALUE: '_51', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String11', NUM: 11}, STR_VALUE: '_11', REPEATED_INT: !null _}, + {HEADER: {REC_NO: 1, PATH: 'String11', NUM: 61}, STR_VALUE: '_61', REPEATED_INT: !null _}] + - result: [] + - + # testInWithContinuation — the IN join read one row at a time, so every page boundary falls either + # inside one IN element or between two of them. Rows come out in list order, 'String1' first. + - query: select * from my_record + where header.rec_no = 1 + and header.path in ('String1', 'String6', 'String25', 'String11') + - explain: "[IN arrayDistinct(@c15)] | INJOIN q0 -> { COVERING(IND [EQUALS promote(@c9 AS LONG), EQUALS q0] -> [STR_VALUE: KEY:[2], HEADER: [PATH: KEY:[1], REC_NO: KEY:[0]]]) } | FETCH" + - maxRows: 1 + - result: [{HEADER: {REC_NO: 1, PATH: 'String1', NUM: 1}, STR_VALUE: '_1', REPEATED_INT: !null _}] + - result: [{HEADER: {REC_NO: 1, PATH: 'String1', NUM: 51}, STR_VALUE: '_51', REPEATED_INT: !null _}] + - result: [{HEADER: {REC_NO: 1, PATH: 'String6', NUM: 56}, STR_VALUE: '_56', REPEATED_INT: !null _}] + - result: [{HEADER: {REC_NO: 1, PATH: 'String6', NUM: 6}, STR_VALUE: '_6', REPEATED_INT: !null _}] + - result: [{HEADER: {REC_NO: 1, PATH: 'String11', NUM: 11}, STR_VALUE: '_11', REPEATED_INT: !null _}] + - result: [{HEADER: {REC_NO: 1, PATH: 'String11', NUM: 61}, STR_VALUE: '_61', REPEATED_INT: !null _}] + - result: [] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.metrics.binpb new file mode 100644 index 00000000000..a5d0b97ab39 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.metrics.binpb @@ -0,0 +1,86 @@ +± +‹ +in-query-multi-indexsEXPLAIN select * from my_simple_record where num_value_3_indexed = ? and num_value_2 in ? order by num_value_unique  +ÐíôÝZÎ оð3(C0›õñ8H@¤[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCHÙdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Values
VALUES([__corr_q30 IN arrayDistinct(@c11)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c7 AS INT), EQUALS q30]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MULTI_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q80> label="q80" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +}ð +� +in-query-multi-indexxEXPLAIN select * from my_simple_record where num_value_3_indexed = ? and num_value_2 in ? order by num_value_unique descÚ +Ðð§ÌÎ £ˆû (C0ž•Ô8H@¬[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH‹digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Values
VALUES([__corr_q30 IN arrayDistinct(@c11)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c7 AS INT), EQUALS q30]
direction: reversed
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MULTI_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q80> label="q80" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +}ø +› +in-query-multi-index‚EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed = ? and num_value_2 in ? order by num_value_unique× +�©Ý¦Ì ·ôä (80íø|8(@›[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)šdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE)" ]; + 2 [ label=<
Value Computation
MAP (q48.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c7 AS INT), EQUALS q30]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Index
MULTI_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q30 IN arrayDistinct(@c11)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE)" ]; + 3 -> 2 [ label=< q48> label="q48" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 2 [ color="red" style="invis" ]; + } +}¸ +  +in-query-multi-index‡EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed = ? and num_value_2 in ? order by num_value_unique desc’ +�„‹˜Ì ü¦Ü +(80Á¹ˆ8(@£[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)Ìdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE)" ]; + 2 [ label=<
Value Computation
MAP (q48.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c7 AS INT), EQUALS q30]
direction: reversed
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Index
MULTI_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q30 IN arrayDistinct(@c11)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE)" ]; + 3 -> 2 [ label=< q48> label="q48" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 2 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.metrics.yaml new file mode 100644 index 00000000000..e5379837379 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.metrics.yaml @@ -0,0 +1,301 @@ +in-query-multi-index: +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:61 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 190 + transform_count: 206 + transform_time_ms: 108 + transform_yield_count: 67 + insert_time_ms: 10 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:81 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 190 + transform_count: 206 + transform_time_ms: 108 + transform_yield_count: 67 + insert_time_ms: 10 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:100 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 190 + transform_count: 206 + transform_time_ms: 108 + transform_yield_count: 67 + insert_time_ms: 10 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:119 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 190 + transform_count: 206 + transform_time_ms: 108 + transform_yield_count: 67 + insert_time_ms: 10 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:139 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 190 + transform_count: 206 + transform_time_ms: 108 + transform_yield_count: 67 + insert_time_ms: 10 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:158 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 59 + transform_count: 206 + transform_time_ms: 25 + transform_yield_count: 67 + insert_time_ms: 3 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:178 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 59 + transform_count: 206 + transform_time_ms: 25 + transform_yield_count: 67 + insert_time_ms: 3 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:197 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 59 + transform_count: 206 + transform_time_ms: 25 + transform_yield_count: 67 + insert_time_ms: 3 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:216 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 59 + transform_count: 206 + transform_time_ms: 25 + transform_yield_count: 67 + insert_time_ms: 3 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed = ? and + num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:236 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH' + task_count: 720 + task_total_time_ms: 59 + transform_count: 206 + transform_time_ms: 25 + transform_yield_count: 67 + insert_time_ms: 3 + insert_new_count: 72 + insert_reused_count: 2 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:255 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 46 + transform_count: 204 + transform_time_ms: 26 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:275 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 46 + transform_count: 204 + transform_time_ms: 26 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:294 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 46 + transform_count: 204 + transform_time_ms: 26 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:313 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 46 + transform_count: 204 + transform_time_ms: 26 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique + ref: in-query-multi-index.yamsql:333 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 46 + transform_count: 204 + transform_time_ms: 26 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:352 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 42 + transform_count: 204 + transform_time_ms: 22 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:372 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 42 + transform_count: 204 + transform_time_ms: 22 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:391 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 42 + transform_count: 204 + transform_time_ms: 22 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:410 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 42 + transform_count: 204 + transform_time_ms: 22 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 +- query: EXPLAIN select num_value_unique from my_simple_record where num_value_3_indexed + = ? and num_value_2 in ? order by num_value_unique desc + ref: in-query-multi-index.yamsql:430 + explain: '[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS + promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: + KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE + AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)' + task_count: 528 + task_total_time_ms: 42 + transform_count: 204 + transform_time_ms: 22 + transform_yield_count: 56 + insert_time_ms: 2 + insert_new_count: 40 + insert_reused_count: 1 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.yamsql new file mode 100644 index 00000000000..9b82448aa10 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-multi-index.yamsql @@ -0,0 +1,444 @@ +# +# in-query-multi-index.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest.testInQueryParameter2` and +# `.testInQueryParameter2UsingCoveringIndexScans` — an equality on the first column of a three-column +# index, an IN on the second, and a sort on the third. +# +# Mirrors the source hook: MySimpleRecord$num_value_3_indexed and MySimpleRecord$num_value_unique are +# removed and replaced by MULTI_INDEX over +# (num_value_3_indexed, num_value_2, num_value_unique). +# +# The source loops the `p` binding over 0..4 and `reverse` over both values; each combination appears as its +# own query below. The `valueThrees` list and `p` are parameters in the source, so they are parameter-injected +# here and the block runs as `statement_type: prepared`. The covering group projects only NUM_VALUE_UNIQUE, +# which the index carries, so the in-union legs should be covering scans. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create index multi_index as select num_value_3_indexed, num_value_2, num_value_unique from my_simple_record + order by num_value_3_indexed, num_value_2, num_value_unique + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-multi-index + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testInQueryParameter2, p = 0 + - query: select * from my_simple_record + where num_value_3_indexed = !! 0 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}] + - + # testInQueryParameter2, p = 1 + - query: select * from my_simple_record + where num_value_3_indexed = !! 1 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}] + - + # testInQueryParameter2, p = 2 + - query: select * from my_simple_record + where num_value_3_indexed = !! 2 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}] + - + # testInQueryParameter2, p = 3 + - query: select * from my_simple_record + where num_value_3_indexed = !! 3 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}] + - + # testInQueryParameter2, p = 4 + - query: select * from my_simple_record + where num_value_3_indexed = !! 4 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQueryParameter2, p = 0, reverse + - query: select * from my_simple_record + where num_value_3_indexed = !! 0 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}] + - + # testInQueryParameter2, p = 1, reverse + - query: select * from my_simple_record + where num_value_3_indexed = !! 1 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}] + - + # testInQueryParameter2, p = 2, reverse + - query: select * from my_simple_record + where num_value_3_indexed = !! 2 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}] + - + # testInQueryParameter2, p = 3, reverse + - query: select * from my_simple_record + where num_value_3_indexed = !! 3 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}] + - + # testInQueryParameter2, p = 4, reverse + - query: select * from my_simple_record + where num_value_3_indexed = !! 4 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED) | FETCH" + - result: [{REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 0 + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 0 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 905}, + {NUM_VALUE_UNIQUE: 910}, + {NUM_VALUE_UNIQUE: 920}, + {NUM_VALUE_UNIQUE: 925}, + {NUM_VALUE_UNIQUE: 935}, + {NUM_VALUE_UNIQUE: 940}, + {NUM_VALUE_UNIQUE: 950}, + {NUM_VALUE_UNIQUE: 955}, + {NUM_VALUE_UNIQUE: 965}, + {NUM_VALUE_UNIQUE: 970}, + {NUM_VALUE_UNIQUE: 980}, + {NUM_VALUE_UNIQUE: 985}, + {NUM_VALUE_UNIQUE: 995}, + {NUM_VALUE_UNIQUE: 1000}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 1 + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 1 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 904}, + {NUM_VALUE_UNIQUE: 914}, + {NUM_VALUE_UNIQUE: 919}, + {NUM_VALUE_UNIQUE: 929}, + {NUM_VALUE_UNIQUE: 934}, + {NUM_VALUE_UNIQUE: 944}, + {NUM_VALUE_UNIQUE: 949}, + {NUM_VALUE_UNIQUE: 959}, + {NUM_VALUE_UNIQUE: 964}, + {NUM_VALUE_UNIQUE: 974}, + {NUM_VALUE_UNIQUE: 979}, + {NUM_VALUE_UNIQUE: 989}, + {NUM_VALUE_UNIQUE: 994}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 2 + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 2 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 908}, + {NUM_VALUE_UNIQUE: 913}, + {NUM_VALUE_UNIQUE: 923}, + {NUM_VALUE_UNIQUE: 928}, + {NUM_VALUE_UNIQUE: 938}, + {NUM_VALUE_UNIQUE: 943}, + {NUM_VALUE_UNIQUE: 953}, + {NUM_VALUE_UNIQUE: 958}, + {NUM_VALUE_UNIQUE: 968}, + {NUM_VALUE_UNIQUE: 973}, + {NUM_VALUE_UNIQUE: 983}, + {NUM_VALUE_UNIQUE: 988}, + {NUM_VALUE_UNIQUE: 998}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 3 + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 3 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 902}, + {NUM_VALUE_UNIQUE: 907}, + {NUM_VALUE_UNIQUE: 917}, + {NUM_VALUE_UNIQUE: 922}, + {NUM_VALUE_UNIQUE: 932}, + {NUM_VALUE_UNIQUE: 937}, + {NUM_VALUE_UNIQUE: 947}, + {NUM_VALUE_UNIQUE: 952}, + {NUM_VALUE_UNIQUE: 962}, + {NUM_VALUE_UNIQUE: 967}, + {NUM_VALUE_UNIQUE: 977}, + {NUM_VALUE_UNIQUE: 982}, + {NUM_VALUE_UNIQUE: 992}, + {NUM_VALUE_UNIQUE: 997}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 4 + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 4 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 901}, + {NUM_VALUE_UNIQUE: 911}, + {NUM_VALUE_UNIQUE: 916}, + {NUM_VALUE_UNIQUE: 926}, + {NUM_VALUE_UNIQUE: 931}, + {NUM_VALUE_UNIQUE: 941}, + {NUM_VALUE_UNIQUE: 946}, + {NUM_VALUE_UNIQUE: 956}, + {NUM_VALUE_UNIQUE: 961}, + {NUM_VALUE_UNIQUE: 971}, + {NUM_VALUE_UNIQUE: 976}, + {NUM_VALUE_UNIQUE: 986}, + {NUM_VALUE_UNIQUE: 991}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 0, reverse + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 0 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 1000}, + {NUM_VALUE_UNIQUE: 995}, + {NUM_VALUE_UNIQUE: 985}, + {NUM_VALUE_UNIQUE: 980}, + {NUM_VALUE_UNIQUE: 970}, + {NUM_VALUE_UNIQUE: 965}, + {NUM_VALUE_UNIQUE: 955}, + {NUM_VALUE_UNIQUE: 950}, + {NUM_VALUE_UNIQUE: 940}, + {NUM_VALUE_UNIQUE: 935}, + {NUM_VALUE_UNIQUE: 925}, + {NUM_VALUE_UNIQUE: 920}, + {NUM_VALUE_UNIQUE: 910}, + {NUM_VALUE_UNIQUE: 905}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 1, reverse + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 1 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 994}, + {NUM_VALUE_UNIQUE: 989}, + {NUM_VALUE_UNIQUE: 979}, + {NUM_VALUE_UNIQUE: 974}, + {NUM_VALUE_UNIQUE: 964}, + {NUM_VALUE_UNIQUE: 959}, + {NUM_VALUE_UNIQUE: 949}, + {NUM_VALUE_UNIQUE: 944}, + {NUM_VALUE_UNIQUE: 934}, + {NUM_VALUE_UNIQUE: 929}, + {NUM_VALUE_UNIQUE: 919}, + {NUM_VALUE_UNIQUE: 914}, + {NUM_VALUE_UNIQUE: 904}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 2, reverse + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 2 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 998}, + {NUM_VALUE_UNIQUE: 988}, + {NUM_VALUE_UNIQUE: 983}, + {NUM_VALUE_UNIQUE: 973}, + {NUM_VALUE_UNIQUE: 968}, + {NUM_VALUE_UNIQUE: 958}, + {NUM_VALUE_UNIQUE: 953}, + {NUM_VALUE_UNIQUE: 943}, + {NUM_VALUE_UNIQUE: 938}, + {NUM_VALUE_UNIQUE: 928}, + {NUM_VALUE_UNIQUE: 923}, + {NUM_VALUE_UNIQUE: 913}, + {NUM_VALUE_UNIQUE: 908}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 3, reverse + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 3 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 997}, + {NUM_VALUE_UNIQUE: 992}, + {NUM_VALUE_UNIQUE: 982}, + {NUM_VALUE_UNIQUE: 977}, + {NUM_VALUE_UNIQUE: 967}, + {NUM_VALUE_UNIQUE: 962}, + {NUM_VALUE_UNIQUE: 952}, + {NUM_VALUE_UNIQUE: 947}, + {NUM_VALUE_UNIQUE: 937}, + {NUM_VALUE_UNIQUE: 932}, + {NUM_VALUE_UNIQUE: 922}, + {NUM_VALUE_UNIQUE: 917}, + {NUM_VALUE_UNIQUE: 907}, + {NUM_VALUE_UNIQUE: 902}] + - + # testInQueryParameter2UsingCoveringIndexScans, p = 4, reverse + - query: select num_value_unique from my_simple_record + where num_value_3_indexed = !! 4 !! and num_value_2 in !! !in {[0, 2]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[0], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE) } COMPARE BY (_.NUM_VALUE_UNIQUE)" + - result: [{NUM_VALUE_UNIQUE: 991}, + {NUM_VALUE_UNIQUE: 986}, + {NUM_VALUE_UNIQUE: 976}, + {NUM_VALUE_UNIQUE: 971}, + {NUM_VALUE_UNIQUE: 961}, + {NUM_VALUE_UNIQUE: 956}, + {NUM_VALUE_UNIQUE: 946}, + {NUM_VALUE_UNIQUE: 941}, + {NUM_VALUE_UNIQUE: 931}, + {NUM_VALUE_UNIQUE: 926}, + {NUM_VALUE_UNIQUE: 916}, + {NUM_VALUE_UNIQUE: 911}, + {NUM_VALUE_UNIQUE: 901}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.metrics.binpb new file mode 100644 index 00000000000..d38b27c7505 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.metrics.binpb @@ -0,0 +1,28 @@ +Ð* +³ +in-query-or-compoundšEXPLAIN select * from my_simple_record where str_value_indexed = 'odd' and num_value_2 = 0 and (num_value_3_indexed in (1, 3) or num_value_3_indexed >= 4)—) +ü)öîí6â þµ(Á0øó“8ü@/•[IN arrayDistinct(@c16) SORTED] | INJOIN q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS STRING), EQUALS promote(@c11 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[2], REC_NO: KEY:[3], STR_VALUE_INDEXED: KEY:[0]]) } ∪ COVERING(MULTI_INDEX [EQUALS promote(@c7 AS STRING), EQUALS promote(@c11 AS INT), [GREATER_THAN_OR_EQUALS promote(@c25 AS INT)]] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[2], REC_NO: KEY:[3], STR_VALUE_INDEXED: KEY:[0]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCHÝ$digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Union Distinct
COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c7 AS STRING), EQUALS promote(@c11 AS INT), [GREATER_THAN_OR_EQUALS promote(@c25 AS INT)]]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Table Function
EXPLODE(arrayDistinct(@c16))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c7 AS STRING), EQUALS promote(@c11 AS INT), EQUALS q229]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 7 [ label=<
Index
MULTI_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 8 [ label=<
Index
MULTI_INDEX
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ label=< q731> label="q731" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ label=< q733> label="q733" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 6 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q735> label="q735" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 6 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.metrics.yaml new file mode 100644 index 00000000000..0407ed81200 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.metrics.yaml @@ -0,0 +1,20 @@ +in-query-or-compound: +- query: EXPLAIN select * from my_simple_record where str_value_indexed = 'odd' + and num_value_2 = 0 and (num_value_3_indexed in (1, 3) or num_value_3_indexed + >= 4) + ref: in-query-or-compound.yamsql:55 + explain: '[IN arrayDistinct(@c16) SORTED] | INJOIN q0 -> { COVERING(MULTI_INDEX + [EQUALS promote(@c7 AS STRING), EQUALS promote(@c11 AS INT), EQUALS q0] -> + [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[2], REC_NO: KEY:[3], STR_VALUE_INDEXED: + KEY:[0]]) } ∪ COVERING(MULTI_INDEX [EQUALS promote(@c7 AS STRING), EQUALS + promote(@c11 AS INT), [GREATER_THAN_OR_EQUALS promote(@c25 AS INT)]] -> [NUM_VALUE_2: + KEY:[1], NUM_VALUE_3_INDEXED: KEY:[2], REC_NO: KEY:[3], STR_VALUE_INDEXED: + KEY:[0]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH' + task_count: 5372 + task_total_time_ms: 115 + transform_count: 994 + transform_time_ms: 51 + transform_yield_count: 321 + insert_time_ms: 8 + insert_new_count: 764 + insert_reused_count: 47 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.yamsql new file mode 100644 index 00000000000..0f3f7808348 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-compound.yamsql @@ -0,0 +1,67 @@ +# +# in-query-or-compound.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest.testInQueryOrCompound` — an `IN` nested inside an `OR` inside an `AND`, +# where MULTI_INDEX supplies the equality prefix and both OR legs bind its trailing column. +# +# Index set is `complexQuerySetupHook()`. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create index multi_index as select str_value_indexed, num_value_2, num_value_3_indexed from my_simple_record + order by str_value_indexed, num_value_2, num_value_3_indexed + create index repeater_fanout as + select sq.v from my_simple_record as r, (select v from r.repeater as v) as sq order by sq.v + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-or-compound + preset: single_repetition_ordered + tests: + - + # testInQueryOrCompound + - query: select * from my_simple_record + where str_value_indexed = 'odd' and num_value_2 = 0 + and (num_value_3_indexed in (1, 3) or num_value_3_indexed >= 4) + - explain: "[IN arrayDistinct(@c16) SORTED] | INJOIN q0 -> { COVERING(MULTI_INDEX [EQUALS promote(@c7 AS STRING), EQUALS promote(@c11 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[2], REC_NO: KEY:[3], STR_VALUE_INDEXED: KEY:[0]]) } ∪ COVERING(MULTI_INDEX [EQUALS promote(@c7 AS STRING), EQUALS promote(@c11 AS INT), [GREATER_THAN_OR_EQUALS promote(@c25 AS INT)]] -> [NUM_VALUE_2: KEY:[1], NUM_VALUE_3_INDEXED: KEY:[2], REC_NO: KEY:[3], STR_VALUE_INDEXED: KEY:[0]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH" + - unorderedResult: [{REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.metrics.binpb new file mode 100644 index 00000000000..4ab409597aa --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.metrics.binpb @@ -0,0 +1,39 @@ +¬1 +Ž +in-query-or-overlapwEXPLAIN select * from my_simple_record where num_value_unique in (903, 905, 901) or num_value_unique in (906, 905, 904)˜0 + Ä=ìѯÕ§ + »Èû¬(—0šªç8š +@¢ê[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [EQUALS q0] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } ∪ [IN arrayDistinct(@c17) SORTED] | INJOIN q1 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [EQUALS q1] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO) | FETCH†-digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Union Distinct
COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Covering Index Scan
comparisons: [EQUALS q224]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 7 [ label=<
Table Function
EXPLODE(arrayDistinct(@c17))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 8 [ label=<
Covering Index Scan
comparisons: [EQUALS q99]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 9 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 10 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ label=< q1232> label="q1232" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ label=< q1234> label="q1234" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 4 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 4 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 9 -> 6 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 10 -> 8 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q1236> label="q1236" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 6 [ color="red" style="invis" ]; + } + { + rank=same; + rankDir=LR; + 7 -> 8 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.metrics.yaml new file mode 100644 index 00000000000..6c621ea9652 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.metrics.yaml @@ -0,0 +1,17 @@ +in-query-or-overlap: +- query: EXPLAIN select * from my_simple_record where num_value_unique in (903, + 905, 901) or num_value_unique in (906, 905, 904) + ref: in-query-or-overlap.yamsql:83 + explain: '[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE + [EQUALS q0] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } ∪ [IN arrayDistinct(@c17) + SORTED] | INJOIN q1 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [EQUALS + q1] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO) | FETCH' + task_count: 7876 + task_total_time_ms: 715 + transform_count: 1319 + transform_time_ms: 362 + transform_yield_count: 407 + insert_time_ms: 58 + insert_new_count: 1306 + insert_reused_count: 162 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.yamsql new file mode 100644 index 00000000000..4e1aa096017 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or-overlap.yamsql @@ -0,0 +1,59 @@ +# +# in-query-or-overlap.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest.testInQueryOrOverlap` — an OR of two `IN` lists on the same indexed +# column that share a value (905), so the union has to deduplicate. +# +# The plan is the one the source asserts for Cascades: a union of two in-joins over the same covering +# index, deduplicated on the primary key, returning exactly these five records. The query used to fail at +# execution with `ClassCastException: ByteString$LiteralByteString cannot be cast to Comparable`; it no +# longer does, and the suite method is enabled. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-or-overlap + preset: single_repetition_ordered + tests: + - + # testInQueryOrOverlap + - query: select * from my_simple_record + where num_value_unique in (903, 905, 901) or num_value_unique in (906, 905, 904) + - explain: "[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [EQUALS q0] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } ∪ [IN arrayDistinct(@c17) SORTED] | INJOIN q1 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [EQUALS q1] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO) | FETCH" + - unorderedResult: [{REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.metrics.binpb new file mode 100644 index 00000000000..f5f46482eaf --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.metrics.binpb @@ -0,0 +1,107 @@ +÷' +y + in-query-orjEXPLAIN select * from my_simple_record where num_value_unique in (903, 905, 901) or num_value_unique > 950ù& +©ÎéšK¯ ·‰ "(Ü0ßÙý8î@%Ñ[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [EQUALS q0] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } ∪ COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [[GREATER_THAN promote(@c17 AS INT)]] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO) | FETCHƒ$digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Union Distinct
COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [[GREATER_THAN promote(@c17 AS INT)]]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Covering Index Scan
comparisons: [EQUALS q150]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 7 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 8 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ label=< q496> label="q496" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ label=< q498> label="q498" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 5 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q500> label="q500" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 6 -> 5 [ color="red" style="invis" ]; + } +}œ) +ˆ + in-query-oryEXPLAIN select * from my_simple_record where num_value_unique < 910 or (num_value_unique > 990 and num_value_2 in (2, 0))Ž( + ùX‘äÌùƒ î«‘„(Ü0®‘Ÿ8Ð@þÌISCAN(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [[LESS_THAN promote(@c7 AS INT)]]) ∪ [IN arrayDistinct(@c16)] INUNION q0 -> { ISCAN(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [[GREATER_THAN promote(@c12 AS INT)]]) | FILTER _.NUM_VALUE_2 EQUALS q0 } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2) COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO)š%digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Union Distinct
COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Index Scan
comparisons: [[LESS_THAN promote(@c7 AS INT)]]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q1146 IN arrayDistinct(@c16)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Predicate Filter
WHERE q2.NUM_VALUE_2 EQUALS q1146
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 7 [ label=<
Index Scan
comparisons: [[GREATER_THAN promote(@c12 AS INT)]]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 8 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q1506> label="q1506" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 4 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 6 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 7 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 1 [ label=< q1508> label="q1508" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 6 [ color="red" style="invis" ]; + } +}½( +v + in-query-orgEXPLAIN select * from my_simple_record where str_value_indexed = 'odd' or num_value_3_indexed in (1, 3)Â' +ã,Š—†hž š�‘7(“0™¢Û8Û@êåCOVERING(MY_SIMPLE_RECORD_STR_VALUE_INDEXED [EQUALS promote(@c7 AS STRING)] -> [REC_NO: KEY:[1], STR_VALUE_INDEXED: KEY:[0]]) ∪ [IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } COMPARE BY (_.REC_NO, _.NUM_VALUE_3_INDEXED) COMPARE BY (_.REC_NO) | FETCH·$digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Union Distinct
COMPARE BY (_.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
In Union
COMPARE BY (_.REC_NO, _.NUM_VALUE_3_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c7 AS STRING)]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q97 IN arrayDistinct(@c11)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Covering Index Scan
comparisons: [EQUALS q97]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 7 [ label=<
Index
MY_SIMPLE_RECORD_STR_VALUE_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 8 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ label=< q1042> label="q1042" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ label=< q1040> label="q1040" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 6 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q1044> label="q1044" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 6 [ color="red" style="invis" ]; + } +}õ +w + in-query-orhEXPLAIN select * from my_simple_record where num_value_2 in (1, 3, 5, 7, 9) order by num_value_3_indexedù +¤ÂÑ� +² ÷¶�(;0οH89@MISCAN(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED <,>) | FILTER _.NUM_VALUE_2 IN @c7‹ digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Predicate Filter
WHERE q2.NUM_VALUE_2 IN @c7
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +}Ñ +U +in-query-or-prepared=EXPLAIN select * from my_simple_record where num_value_2 in ?÷ +§ôÚߘ ‚µþ(_0ƒ¥�8�@KISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 IN @c7‰ digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Predicate Filter
WHERE q2.NUM_VALUE_2 IN @c7
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Index
MY_SIMPLE_RECORD_STR_VALUE_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.metrics.yaml new file mode 100644 index 00000000000..4564b9f7edd --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.metrics.yaml @@ -0,0 +1,74 @@ +in-query-or: +- query: EXPLAIN select * from my_simple_record where num_value_unique in (903, + 905, 901) or num_value_unique > 950 + ref: in-query-or.yamsql:55 + explain: '[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE + [EQUALS q0] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } ∪ COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE + [[GREATER_THAN promote(@c17 AS INT)]] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: + KEY:[1]]) COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO) | FETCH' + task_count: 3625 + task_total_time_ms: 157 + transform_count: 687 + transform_time_ms: 71 + transform_yield_count: 220 + insert_time_ms: 12 + insert_new_count: 494 + insert_reused_count: 37 +- query: EXPLAIN select * from my_simple_record where num_value_unique < 910 or + (num_value_unique > 990 and num_value_2 in (2, 0)) + ref: in-query-or.yamsql:115 + explain: ISCAN(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [[LESS_THAN promote(@c7 AS INT)]]) + ∪ [IN arrayDistinct(@c16)] INUNION q0 -> { ISCAN(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE + [[GREATER_THAN promote(@c12 AS INT)]]) | FILTER _.NUM_VALUE_2 EQUALS q0 } + COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2) COMPARE BY (_.NUM_VALUE_UNIQUE, + _.REC_NO) + task_count: 11385 + task_total_time_ms: 523 + transform_count: 1923 + transform_time_ms: 277 + transform_yield_count: 604 + insert_time_ms: 46 + insert_new_count: 2128 + insert_reused_count: 382 +- query: EXPLAIN select * from my_simple_record where str_value_indexed = 'odd' + or num_value_3_indexed in (1, 3) + ref: in-query-or.yamsql:137 + explain: 'COVERING(MY_SIMPLE_RECORD_STR_VALUE_INDEXED [EQUALS promote(@c7 AS STRING)] + -> [REC_NO: KEY:[1], STR_VALUE_INDEXED: KEY:[0]]) ∪ [IN arrayDistinct(@c11)] + INUNION q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] + -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } COMPARE BY (_.REC_NO, + _.NUM_VALUE_3_INDEXED) COMPARE BY (_.REC_NO) | FETCH' + task_count: 5731 + task_total_time_ms: 218 + transform_count: 926 + transform_time_ms: 115 + transform_yield_count: 275 + insert_time_ms: 18 + insert_new_count: 1115 + insert_reused_count: 234 +- query: EXPLAIN select * from my_simple_record where num_value_2 in (1, 3, 5, 7, + 9) order by num_value_3_indexed + ref: in-query-or.yamsql:213 + explain: ISCAN(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED <,>) | FILTER _.NUM_VALUE_2 + IN @c7 + task_count: 548 + task_total_time_ms: 21 + transform_count: 178 + transform_time_ms: 10 + transform_yield_count: 59 + insert_time_ms: 1 + insert_new_count: 57 + insert_reused_count: 3 +in-query-or-prepared: +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? + ref: in-query-or.yamsql:258 + explain: ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 + IN @c7 + task_count: 1063 + task_total_time_ms: 33 + transform_count: 280 + transform_time_ms: 14 + transform_yield_count: 95 + insert_time_ms: 2 + insert_new_count: 157 + insert_reused_count: 8 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.yamsql new file mode 100644 index 00000000000..32d151ad033 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-or.yamsql @@ -0,0 +1,260 @@ +# +# in-query-or.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest` — the tests that combine `IN` with `OR`, plus the two edge cases +# (empty list, and an IN on a column with no index). All run against the unmodified +# `test_records_1.proto` index set (`complexQuerySetup(NO_HOOK)`). +# +# `testInUnionWithNonSargable`'s source is `@ParameterizedTest` over a `maxNumReplansForInToJoin` +# value; the SQL layer does not expose that knob, so it collapses to a single case. +# +# `testInQueryEmptyList` cannot be written as a SQL literal (`in ()` does not parse), so it is a parameter-injected +# empty list in a second block that runs as `statement_type: prepared`; simple statements skip empty lists. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-or + preset: single_repetition_ordered + tests: + - + # testInQueryOr — an IN list unioned with a range on the same indexed column. + - query: select * from my_simple_record + where num_value_unique in (903, 905, 901) or num_value_unique > 950 + - explain: "[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [EQUALS q0] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) } ∪ COVERING(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [[GREATER_THAN promote(@c17 AS INT)]] -> [NUM_VALUE_UNIQUE: KEY:[0], REC_NO: KEY:[1]]) COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO) | FETCH" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 10, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 13, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 25, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 28, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 40, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 960, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 43, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 957, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQueryOrDifferentCondition — one leg is a plain range, the other an AND whose IN cannot be + # pushed into the scan and stays a residual filter. + - query: select * from my_simple_record + where num_value_unique < 910 + or (num_value_unique > 990 and num_value_2 in (2, 0)) + - explain: "ISCAN(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [[LESS_THAN promote(@c7 AS INT)]]) ∪ [IN arrayDistinct(@c16)] INUNION q0 -> { ISCAN(MY_SIMPLE_RECORD_NUM_VALUE_UNIQUE [[GREATER_THAN promote(@c12 AS INT)]]) | FILTER _.NUM_VALUE_2 EQUALS q0 } COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO, _.NUM_VALUE_2) COMPARE BY (_.NUM_VALUE_UNIQUE, _.REC_NO)" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQueryOrMultipleIndexes — the two legs are served by two different indexes, one of them via + # an in-union. + - query: select * from my_simple_record + where str_value_indexed = 'odd' or num_value_3_indexed in (1, 3) + - explain: "COVERING(MY_SIMPLE_RECORD_STR_VALUE_INDEXED [EQUALS promote(@c7 AS STRING)] -> [REC_NO: KEY:[1], STR_VALUE_INDEXED: KEY:[0]]) ∪ [IN arrayDistinct(@c11)] INUNION q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } COMPARE BY (_.REC_NO, _.NUM_VALUE_3_INDEXED) COMPARE BY (_.REC_NO) | FETCH" + - unorderedResult: [{REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 13, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 25, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 28, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 43, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 957, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 55, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 945, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 58, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 942, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 73, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 927, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 85, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 915, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 88, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 912, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInUnionWithNonSargable — NUM_VALUE_2 has no index of its own, so the IN cannot become an + # in-union; the sort is served by the NUM_VALUE_3_INDEXED index and the IN stays a filter. + - query: select * from my_simple_record where num_value_2 in (1, 3, 5, 7, 9) + order by num_value_3_indexed + - explain: "ISCAN(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED <,>) | FILTER _.NUM_VALUE_2 IN @c7" + - unorderedResult: [{REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 10, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 13, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 25, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 28, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 40, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 960, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 43, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 957, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 55, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 945, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 58, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 942, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 70, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 930, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 73, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 927, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 85, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 915, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 88, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 912, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}] +--- +test_block: + name: in-query-or-prepared + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testInQueryEmptyList — an empty IN list matches nothing and cannot be turned into an in-union, so + # it stays a residual filter over a scan. + - query: select * from my_simple_record where num_value_2 in !! !in {[]} !! + - explain: "ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 IN @c7" + - unorderedResult: [] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.metrics.binpb new file mode 100644 index 00000000000..4d9e86a2a83 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.metrics.binpb @@ -0,0 +1,85 @@ +¥ +Ñ +'in-query-sorted-extra-unordered-columns¥EXPLAIN select num_value_unique, str_value_indexed, rec_no from my_simple_record where num_value_2 = 1 and num_value_3_indexed in (1, 2, 4) order by num_value_uniqueÎ +ܨâ¡uÉ Ò°ýG(u0›“É8f@œ[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION [EQUALS promote(@c11 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)�digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 2 [ label=<
Value Computation
MAP (q126.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, q126.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, q126.REC_NO AS REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c11 AS INT), EQUALS q70]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Index
INDEX_FOR_IN_UNION
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q70 IN arrayDistinct(@c15)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 3 -> 2 [ label=< q126> label="q126" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 2 [ color="red" style="invis" ]; + } +}ä +Ö +'in-query-sorted-extra-unordered-columnsªEXPLAIN select num_value_unique, str_value_indexed, rec_no from my_simple_record where num_value_2 = 1 and num_value_3_indexed in (1, 2, 4) order by num_value_unique descˆ +ÜöŽö1É ÂÐÄ(u0  Ñ8f@¤[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION [EQUALS promote(@c11 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)Ádigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 2 [ label=<
Value Computation
MAP (q126.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, q126.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, q126.REC_NO AS REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c11 AS INT), EQUALS q70]
direction: reversed
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Index
INDEX_FOR_IN_UNION
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q70 IN arrayDistinct(@c15)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 3 -> 2 [ label=< q126> label="q126" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 2 [ color="red" style="invis" ]; + } +}¦ +Ò +0in-query-sorted-extra-unordered-columns-prepared�EXPLAIN select num_value_unique, str_value_indexed, rec_no from my_simple_record where num_value_2 = ? and num_value_3_indexed in ? order by num_value_uniqueÎ +ÜïÑÆ(É Ù€¢(u0ýÿž8f@œ[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION [EQUALS promote(@c11 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)�digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 2 [ label=<
Value Computation
MAP (q126.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, q126.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, q126.REC_NO AS REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c11 AS INT), EQUALS q70]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Index
INDEX_FOR_IN_UNION
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q70 IN arrayDistinct(@c15)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 3 -> 2 [ label=< q126> label="q126" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 2 [ color="red" style="invis" ]; + } +}å +× +0in-query-sorted-extra-unordered-columns-prepared¢EXPLAIN select num_value_unique, str_value_indexed, rec_no from my_simple_record where num_value_2 = ? and num_value_3_indexed in ? order by num_value_unique descˆ +ÜîЫ$É ó‹�(u0Ú©•8f@¤[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION [EQUALS promote(@c11 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)Ádigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 2 [ label=<
Value Computation
MAP (q126.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, q126.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, q126.REC_NO AS REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS promote(@c11 AS INT), EQUALS q70]
direction: reversed
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Index
INDEX_FOR_IN_UNION
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q70 IN arrayDistinct(@c15)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(INT AS NUM_VALUE_UNIQUE, STRING AS STR_VALUE_INDEXED, LONG AS REC_NO)" ]; + 3 -> 2 [ label=< q126> label="q126" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 2 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.metrics.yaml new file mode 100644 index 00000000000..240b81ad3d9 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.metrics.yaml @@ -0,0 +1,72 @@ +in-query-sorted-extra-unordered-columns: +- query: EXPLAIN select num_value_unique, str_value_indexed, rec_no from my_simple_record + where num_value_2 = 1 and num_value_3_indexed in (1, 2, 4) order by num_value_unique + ref: in-query-sorted-extra-unordered-columns.yamsql:83 + explain: '[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION + [EQUALS promote(@c11 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: + KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) + | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, + _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, + _.REC_NO)' + task_count: 988 + task_total_time_ms: 245 + transform_count: 329 + transform_time_ms: 150 + transform_yield_count: 117 + insert_time_ms: 11 + insert_new_count: 102 + insert_reused_count: 4 +- query: EXPLAIN select num_value_unique, str_value_indexed, rec_no from my_simple_record + where num_value_2 = 1 and num_value_3_indexed in (1, 2, 4) order by num_value_unique + desc + ref: in-query-sorted-extra-unordered-columns.yamsql:110 + explain: '[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION + [EQUALS promote(@c11 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[0], + NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], + STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, + _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY + (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)' + task_count: 988 + task_total_time_ms: 104 + transform_count: 329 + transform_time_ms: 53 + transform_yield_count: 117 + insert_time_ms: 5 + insert_new_count: 102 + insert_reused_count: 4 +in-query-sorted-extra-unordered-columns-prepared: +- query: EXPLAIN select num_value_unique, str_value_indexed, rec_no from my_simple_record + where num_value_2 = ? and num_value_3_indexed in ? order by num_value_unique + ref: in-query-sorted-extra-unordered-columns.yamsql:144 + explain: '[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION + [EQUALS promote(@c11 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: + KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) + | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, + _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, + _.REC_NO)' + task_count: 988 + task_total_time_ms: 85 + transform_count: 329 + transform_time_ms: 44 + transform_yield_count: 117 + insert_time_ms: 4 + insert_new_count: 102 + insert_reused_count: 4 +- query: EXPLAIN select num_value_unique, str_value_indexed, rec_no from my_simple_record + where num_value_2 = ? and num_value_3_indexed in ? order by num_value_unique + desc + ref: in-query-sorted-extra-unordered-columns.yamsql:171 + explain: '[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION + [EQUALS promote(@c11 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[0], + NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], + STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, + _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY + (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)' + task_count: 988 + task_total_time_ms: 76 + transform_count: 329 + transform_time_ms: 39 + transform_yield_count: 117 + insert_time_ms: 4 + insert_new_count: 102 + insert_reused_count: 4 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.yamsql new file mode 100644 index 00000000000..956adf871e5 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-sorted-extra-unordered-columns.yamsql @@ -0,0 +1,190 @@ +# +# in-query-sorted-extra-unordered-columns.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from two `FDBInQueryTest` methods that share one index, one fixture and one Cascades plan: +# `testListInQuerySortedWithExtraUnorderedColumns` — a literal IN list (first block) +# `testParameterizedInQuerySortedWithExtraUnorderedColumns` — num_value_2 and the IN list are parameters, +# so they are parameter-injected and the second block runs as `statement_type: prepared` +# +# The index is `indexForInUnion` = +# `concatenateFields("num_value_2", "num_value_3_indexed", "num_value_unique", "str_value_indexed")`, +# and the sort is on num_value_unique alone, so STR_VALUE_INDEXED is an "extra unordered column". The +# old planner's ordering-key calculation gives up on that in most configurations and scans +# MySimpleRecord$num_value_unique with a filter; Cascades always plans an in-union. +# +# Both assert `inUnionOnValuesPlan` over a covering scan of the index with two equalities. Each is ported +# forward and reverse, the only surviving parameterization (the other combinations of `InAsOrUnionMode`, +# `omitPrimaryKeyInOrdering` and `attemptFailedInJoinAsUnionMaxSize` are read only by `RecordQueryPlanner`). +# +# `testConstantObjectInQuerySortedWithExtraUnorderedColumns` is not part of this port. +# +# Projection: the source sets `setRequiredResults(num_value_unique, str_value_indexed, rec_no)` and asserts +# covering scans, so the queries project exactly those. Both assert 21 rows; NUM_VALUE_UNIQUE is unique +# over the fixture, so both sorts are total. +# +# Comparison-key divergence. The source asserts four comparison values, +# (num_value_unique, str_value_indexed, rec_no, num_value_3_indexed); the generated plan has three, without +# NUM_VALUE_3_INDEXED. The covering scan does flow all five index columns, but the MAP sits *inside* the +# in-union and the comparison key is computed after it, so a projection narrower than the index shrinks the +# key. Still total, since NUM_VALUE_UNIQUE alone is unique. The source also asserts no `mapPlan` above the +# covering scan; SQL always emits one for a projection, so that part is not reproducible either. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create index multi_index as select str_value_indexed, num_value_2, num_value_3_indexed from my_simple_record + order by str_value_indexed, num_value_2, num_value_3_indexed + create index repeater_fanout as + select sq.v from my_simple_record as r, (select v from r.repeater as v) as sq order by sq.v + create index index_for_in_union as + select num_value_2, num_value_3_indexed, num_value_unique, str_value_indexed from my_simple_record + order by num_value_2, num_value_3_indexed, num_value_unique, str_value_indexed + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-sorted-extra-unordered-columns + preset: single_repetition_ordered + tests: + - + # testListInQuerySortedWithExtraUnorderedColumns — a literal IN list, sorted on NUM_VALUE_UNIQUE alone. + - query: select num_value_unique, str_value_indexed, rec_no from my_simple_record + where num_value_2 = 1 and num_value_3_indexed in (1, 2, 4) + order by num_value_unique + - explain: "[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION [EQUALS promote(@c11 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)" + - result: [{NUM_VALUE_UNIQUE: 903, STR_VALUE_INDEXED: 'odd', REC_NO: 97}, + {NUM_VALUE_UNIQUE: 906, STR_VALUE_INDEXED: 'even', REC_NO: 94}, + {NUM_VALUE_UNIQUE: 909, STR_VALUE_INDEXED: 'odd', REC_NO: 91}, + {NUM_VALUE_UNIQUE: 918, STR_VALUE_INDEXED: 'even', REC_NO: 82}, + {NUM_VALUE_UNIQUE: 921, STR_VALUE_INDEXED: 'odd', REC_NO: 79}, + {NUM_VALUE_UNIQUE: 924, STR_VALUE_INDEXED: 'even', REC_NO: 76}, + {NUM_VALUE_UNIQUE: 933, STR_VALUE_INDEXED: 'odd', REC_NO: 67}, + {NUM_VALUE_UNIQUE: 936, STR_VALUE_INDEXED: 'even', REC_NO: 64}, + {NUM_VALUE_UNIQUE: 939, STR_VALUE_INDEXED: 'odd', REC_NO: 61}, + {NUM_VALUE_UNIQUE: 948, STR_VALUE_INDEXED: 'even', REC_NO: 52}, + {NUM_VALUE_UNIQUE: 951, STR_VALUE_INDEXED: 'odd', REC_NO: 49}, + {NUM_VALUE_UNIQUE: 954, STR_VALUE_INDEXED: 'even', REC_NO: 46}, + {NUM_VALUE_UNIQUE: 963, STR_VALUE_INDEXED: 'odd', REC_NO: 37}, + {NUM_VALUE_UNIQUE: 966, STR_VALUE_INDEXED: 'even', REC_NO: 34}, + {NUM_VALUE_UNIQUE: 969, STR_VALUE_INDEXED: 'odd', REC_NO: 31}, + {NUM_VALUE_UNIQUE: 978, STR_VALUE_INDEXED: 'even', REC_NO: 22}, + {NUM_VALUE_UNIQUE: 981, STR_VALUE_INDEXED: 'odd', REC_NO: 19}, + {NUM_VALUE_UNIQUE: 984, STR_VALUE_INDEXED: 'even', REC_NO: 16}, + {NUM_VALUE_UNIQUE: 993, STR_VALUE_INDEXED: 'odd', REC_NO: 7}, + {NUM_VALUE_UNIQUE: 996, STR_VALUE_INDEXED: 'even', REC_NO: 4}, + {NUM_VALUE_UNIQUE: 999, STR_VALUE_INDEXED: 'odd', REC_NO: 1}] + - + # testListInQuerySortedWithExtraUnorderedColumns, reverse. + - query: select num_value_unique, str_value_indexed, rec_no from my_simple_record + where num_value_2 = 1 and num_value_3_indexed in (1, 2, 4) + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION [EQUALS promote(@c11 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)" + - result: [{NUM_VALUE_UNIQUE: 999, STR_VALUE_INDEXED: 'odd', REC_NO: 1}, + {NUM_VALUE_UNIQUE: 996, STR_VALUE_INDEXED: 'even', REC_NO: 4}, + {NUM_VALUE_UNIQUE: 993, STR_VALUE_INDEXED: 'odd', REC_NO: 7}, + {NUM_VALUE_UNIQUE: 984, STR_VALUE_INDEXED: 'even', REC_NO: 16}, + {NUM_VALUE_UNIQUE: 981, STR_VALUE_INDEXED: 'odd', REC_NO: 19}, + {NUM_VALUE_UNIQUE: 978, STR_VALUE_INDEXED: 'even', REC_NO: 22}, + {NUM_VALUE_UNIQUE: 969, STR_VALUE_INDEXED: 'odd', REC_NO: 31}, + {NUM_VALUE_UNIQUE: 966, STR_VALUE_INDEXED: 'even', REC_NO: 34}, + {NUM_VALUE_UNIQUE: 963, STR_VALUE_INDEXED: 'odd', REC_NO: 37}, + {NUM_VALUE_UNIQUE: 954, STR_VALUE_INDEXED: 'even', REC_NO: 46}, + {NUM_VALUE_UNIQUE: 951, STR_VALUE_INDEXED: 'odd', REC_NO: 49}, + {NUM_VALUE_UNIQUE: 948, STR_VALUE_INDEXED: 'even', REC_NO: 52}, + {NUM_VALUE_UNIQUE: 939, STR_VALUE_INDEXED: 'odd', REC_NO: 61}, + {NUM_VALUE_UNIQUE: 936, STR_VALUE_INDEXED: 'even', REC_NO: 64}, + {NUM_VALUE_UNIQUE: 933, STR_VALUE_INDEXED: 'odd', REC_NO: 67}, + {NUM_VALUE_UNIQUE: 924, STR_VALUE_INDEXED: 'even', REC_NO: 76}, + {NUM_VALUE_UNIQUE: 921, STR_VALUE_INDEXED: 'odd', REC_NO: 79}, + {NUM_VALUE_UNIQUE: 918, STR_VALUE_INDEXED: 'even', REC_NO: 82}, + {NUM_VALUE_UNIQUE: 909, STR_VALUE_INDEXED: 'odd', REC_NO: 91}, + {NUM_VALUE_UNIQUE: 906, STR_VALUE_INDEXED: 'even', REC_NO: 94}, + {NUM_VALUE_UNIQUE: 903, STR_VALUE_INDEXED: 'odd', REC_NO: 97}] +--- +test_block: + name: in-query-sorted-extra-unordered-columns-prepared + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testParameterizedInQuerySortedWithExtraUnorderedColumns — num_value_2 and the IN list are parameters. + - query: select num_value_unique, str_value_indexed, rec_no from my_simple_record + where num_value_2 = !! 1 !! and num_value_3_indexed in !! !in {[1, 2, 4]} !! + order by num_value_unique + - explain: "[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION [EQUALS promote(@c11 AS INT), EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)" + - result: [{NUM_VALUE_UNIQUE: 903, STR_VALUE_INDEXED: 'odd', REC_NO: 97}, + {NUM_VALUE_UNIQUE: 906, STR_VALUE_INDEXED: 'even', REC_NO: 94}, + {NUM_VALUE_UNIQUE: 909, STR_VALUE_INDEXED: 'odd', REC_NO: 91}, + {NUM_VALUE_UNIQUE: 918, STR_VALUE_INDEXED: 'even', REC_NO: 82}, + {NUM_VALUE_UNIQUE: 921, STR_VALUE_INDEXED: 'odd', REC_NO: 79}, + {NUM_VALUE_UNIQUE: 924, STR_VALUE_INDEXED: 'even', REC_NO: 76}, + {NUM_VALUE_UNIQUE: 933, STR_VALUE_INDEXED: 'odd', REC_NO: 67}, + {NUM_VALUE_UNIQUE: 936, STR_VALUE_INDEXED: 'even', REC_NO: 64}, + {NUM_VALUE_UNIQUE: 939, STR_VALUE_INDEXED: 'odd', REC_NO: 61}, + {NUM_VALUE_UNIQUE: 948, STR_VALUE_INDEXED: 'even', REC_NO: 52}, + {NUM_VALUE_UNIQUE: 951, STR_VALUE_INDEXED: 'odd', REC_NO: 49}, + {NUM_VALUE_UNIQUE: 954, STR_VALUE_INDEXED: 'even', REC_NO: 46}, + {NUM_VALUE_UNIQUE: 963, STR_VALUE_INDEXED: 'odd', REC_NO: 37}, + {NUM_VALUE_UNIQUE: 966, STR_VALUE_INDEXED: 'even', REC_NO: 34}, + {NUM_VALUE_UNIQUE: 969, STR_VALUE_INDEXED: 'odd', REC_NO: 31}, + {NUM_VALUE_UNIQUE: 978, STR_VALUE_INDEXED: 'even', REC_NO: 22}, + {NUM_VALUE_UNIQUE: 981, STR_VALUE_INDEXED: 'odd', REC_NO: 19}, + {NUM_VALUE_UNIQUE: 984, STR_VALUE_INDEXED: 'even', REC_NO: 16}, + {NUM_VALUE_UNIQUE: 993, STR_VALUE_INDEXED: 'odd', REC_NO: 7}, + {NUM_VALUE_UNIQUE: 996, STR_VALUE_INDEXED: 'even', REC_NO: 4}, + {NUM_VALUE_UNIQUE: 999, STR_VALUE_INDEXED: 'odd', REC_NO: 1}] + - + # testParameterizedInQuerySortedWithExtraUnorderedColumns, reverse. + - query: select num_value_unique, str_value_indexed, rec_no from my_simple_record + where num_value_2 = !! 1 !! and num_value_3_indexed in !! !in {[1, 2, 4]} !! + order by num_value_unique desc + - explain: "[IN arrayDistinct(@c15)] INUNION q0 -> { COVERING(INDEX_FOR_IN_UNION [EQUALS promote(@c11 AS INT), EQUALS q0] REVERSE -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], NUM_VALUE_UNIQUE: KEY:[2], REC_NO: KEY:[4], STR_VALUE_INDEXED: KEY:[3]]) | MAP (_.NUM_VALUE_UNIQUE AS NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED AS STR_VALUE_INDEXED, _.REC_NO AS REC_NO) } COMPARE BY (_.NUM_VALUE_UNIQUE, _.STR_VALUE_INDEXED, _.REC_NO)" + - result: [{NUM_VALUE_UNIQUE: 999, STR_VALUE_INDEXED: 'odd', REC_NO: 1}, + {NUM_VALUE_UNIQUE: 996, STR_VALUE_INDEXED: 'even', REC_NO: 4}, + {NUM_VALUE_UNIQUE: 993, STR_VALUE_INDEXED: 'odd', REC_NO: 7}, + {NUM_VALUE_UNIQUE: 984, STR_VALUE_INDEXED: 'even', REC_NO: 16}, + {NUM_VALUE_UNIQUE: 981, STR_VALUE_INDEXED: 'odd', REC_NO: 19}, + {NUM_VALUE_UNIQUE: 978, STR_VALUE_INDEXED: 'even', REC_NO: 22}, + {NUM_VALUE_UNIQUE: 969, STR_VALUE_INDEXED: 'odd', REC_NO: 31}, + {NUM_VALUE_UNIQUE: 966, STR_VALUE_INDEXED: 'even', REC_NO: 34}, + {NUM_VALUE_UNIQUE: 963, STR_VALUE_INDEXED: 'odd', REC_NO: 37}, + {NUM_VALUE_UNIQUE: 954, STR_VALUE_INDEXED: 'even', REC_NO: 46}, + {NUM_VALUE_UNIQUE: 951, STR_VALUE_INDEXED: 'odd', REC_NO: 49}, + {NUM_VALUE_UNIQUE: 948, STR_VALUE_INDEXED: 'even', REC_NO: 52}, + {NUM_VALUE_UNIQUE: 939, STR_VALUE_INDEXED: 'odd', REC_NO: 61}, + {NUM_VALUE_UNIQUE: 936, STR_VALUE_INDEXED: 'even', REC_NO: 64}, + {NUM_VALUE_UNIQUE: 933, STR_VALUE_INDEXED: 'odd', REC_NO: 67}, + {NUM_VALUE_UNIQUE: 924, STR_VALUE_INDEXED: 'even', REC_NO: 76}, + {NUM_VALUE_UNIQUE: 921, STR_VALUE_INDEXED: 'odd', REC_NO: 79}, + {NUM_VALUE_UNIQUE: 918, STR_VALUE_INDEXED: 'even', REC_NO: 82}, + {NUM_VALUE_UNIQUE: 909, STR_VALUE_INDEXED: 'odd', REC_NO: 91}, + {NUM_VALUE_UNIQUE: 906, STR_VALUE_INDEXED: 'even', REC_NO: 94}, + {NUM_VALUE_UNIQUE: 903, STR_VALUE_INDEXED: 'odd', REC_NO: 97}] diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.metrics.binpb new file mode 100644 index 00000000000..b62c7672492 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.metrics.binpb @@ -0,0 +1,37 @@ +Î3 +› +"in-query-union-intersection-no-nv3uEXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed in ? order by num_value_3_indexed­2 +‡Ø×ë2Ó Ùßù(’0û Ö8»@ú[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)Ž.digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Values
VALUES([__corr_q43 IN arrayDistinct(@c11)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Intersection
COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Covering Index Scan
comparisons: [EQUALS q43]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 7 [ label=<
Covering Index Scan
comparisons: [EQUALS q41]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 8 [ label=<
Index
STR_VALUE_NUM_VALUE3
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 9 [ label=<
Index
NUM_VALUE2_NUM_VALUE3
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 10 [ label=<
Values
VALUES([__corr_q41 IN arrayDistinct(@c7)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ label=< q87> label="q87" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 5 [ label=< q83> label="q83" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 5 [ label=< q85> label="q85" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 6 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 9 -> 7 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 10 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 10 -> 2 [ color="red" style="invis" ]; + } + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.metrics.yaml new file mode 100644 index 00000000000..54556de3802 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.metrics.yaml @@ -0,0 +1,55 @@ +in-query-union-intersection-no-nv3: +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-intersection-no-nv3.yamsql:62 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION + q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: + KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 + [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: + KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY + (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, + _.REC_NO, _.NUM_VALUE_2)' + task_count: 1799 + task_total_time_ms: 106 + transform_count: 723 + transform_time_ms: 50 + transform_yield_count: 146 + insert_time_ms: 11 + insert_new_count: 187 + insert_reused_count: 28 +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-intersection-no-nv3.yamsql:102 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION + q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: + KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 + [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: + KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY + (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, + _.REC_NO, _.NUM_VALUE_2)' + task_count: 1799 + task_total_time_ms: 106 + transform_count: 723 + transform_time_ms: 50 + transform_yield_count: 146 + insert_time_ms: 11 + insert_new_count: 187 + insert_reused_count: 28 +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-intersection-no-nv3.yamsql:141 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION + q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: + KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 + [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: + KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY + (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, + _.REC_NO, _.NUM_VALUE_2)' + task_count: 1799 + task_total_time_ms: 106 + transform_count: 723 + transform_time_ms: 50 + transform_yield_count: 146 + insert_time_ms: 11 + insert_new_count: 187 + insert_reused_count: 28 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.yamsql new file mode 100644 index 00000000000..d0649ca0ec6 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection-no-nv3.yamsql @@ -0,0 +1,209 @@ +# +# in-query-union-intersection-no-nv3.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest.testInUnionWithIntersectionOnTwoPredicates` — both `IN` predicates are +# sargable, each against a different composite index, so the plan can intersect two in-unions. +# +# This is the `dropNumValue3Index = true` variant: MySimpleRecord$num_value_3_indexed is removed, so +# the sort must come from one of the two composite indexes. +# +# The source's `replans` parameter is planner configuration the SQL layer does not expose, so it +# collapses. The three `str_list` bindings are reproduced as three queries. +# Both IN lists (`nv2_list`, `str_list`) are parameters in the source, so they are parameter-injected and the +# test block runs as `statement_type: prepared`. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index num_value2_num_value3 as select num_value_2, num_value_3_indexed from my_simple_record + order by num_value_2, num_value_3_indexed + create index str_value_num_value3 as select str_value_indexed, num_value_3_indexed from my_simple_record + order by str_value_indexed, num_value_3_indexed + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-union-intersection-no-nv3 + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # str_list = ('even') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['even']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}] + - + # str_list = ('odd') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['odd']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # str_list = ('even', 'odd', 'other') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['even', 'odd', 'other']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.metrics.binpb new file mode 100644 index 00000000000..9755942bae6 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.metrics.binpb @@ -0,0 +1,37 @@ +Î3 +” +in-query-union-intersectionuEXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed in ? order by num_value_3_indexed´2 +ôã©¿½ø ‡Úña(×0ÞçË8Ð@ú[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)”.digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Values
VALUES([__corr_q59 IN arrayDistinct(@c11)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Intersection
COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Covering Index Scan
comparisons: [EQUALS q59]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 7 [ label=<
Covering Index Scan
comparisons: [EQUALS q57]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 8 [ label=<
Index
STR_VALUE_NUM_VALUE3
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 9 [ label=<
Index
NUM_VALUE2_NUM_VALUE3
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 10 [ label=<
Values
VALUES([__corr_q57 IN arrayDistinct(@c7)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ label=< q126> label="q126" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 5 [ label=< q122> label="q122" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 5 [ label=< q124> label="q124" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 6 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 9 -> 7 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 10 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 10 -> 2 [ color="red" style="invis" ]; + } + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.metrics.yaml new file mode 100644 index 00000000000..07edfa8e387 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.metrics.yaml @@ -0,0 +1,55 @@ +in-query-union-intersection: +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-intersection.yamsql:64 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION + q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: + KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 + [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: + KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY + (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, + _.REC_NO, _.NUM_VALUE_2)' + task_count: 2548 + task_total_time_ms: 397 + transform_count: 888 + transform_time_ms: 205 + transform_yield_count: 215 + insert_time_ms: 30 + insert_new_count: 336 + insert_reused_count: 29 +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-intersection.yamsql:105 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION + q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: + KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 + [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: + KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY + (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, + _.REC_NO, _.NUM_VALUE_2)' + task_count: 2548 + task_total_time_ms: 397 + transform_count: 888 + transform_time_ms: 205 + transform_yield_count: 215 + insert_time_ms: 30 + insert_new_count: 336 + insert_reused_count: 29 +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-intersection.yamsql:145 + explain: '[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION + q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: + KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 + [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: + KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY + (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, + _.REC_NO, _.NUM_VALUE_2)' + task_count: 2548 + task_total_time_ms: 397 + transform_count: 888 + transform_time_ms: 205 + transform_yield_count: 215 + insert_time_ms: 30 + insert_new_count: 336 + insert_reused_count: 29 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.yamsql new file mode 100644 index 00000000000..fd9ac25bca8 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-intersection.yamsql @@ -0,0 +1,213 @@ +# +# in-query-union-intersection.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest.testInUnionWithIntersectionOnTwoPredicates` — both `IN` predicates are +# sargable, each against a different composite index, so the plan can intersect two in-unions. +# +# This is the `dropNumValue3Index = false` variant: MySimpleRecord$num_value_3_indexed is still +# present, so the planner also has a single-index option for the sort. +# +# The source's `replans` parameter is planner configuration the SQL layer does not expose, so it +# collapses. The three `str_list` bindings are reproduced as three queries. +# Both IN lists (`nv2_list`, `str_list`) are parameters in the source, so they are parameter-injected and the +# test block runs as `statement_type: prepared`. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create index num_value2_num_value3 as select num_value_2, num_value_3_indexed from my_simple_record + order by num_value_2, num_value_3_indexed + create index str_value_num_value3 as select str_value_indexed, num_value_3_indexed from my_simple_record + order by str_value_indexed, num_value_3_indexed + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-union-intersection + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testInUnionWithIntersectionOnTwoPredicates + # str_list = ('even') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['even']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}] + - + # testInUnionWithIntersectionOnTwoPredicates + # str_list = ('odd') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['odd']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInUnionWithIntersectionOnTwoPredicates + # str_list = ('even', 'odd', 'other') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['even', 'odd', 'other']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7)] INUNION q0 -> { [IN arrayDistinct(@c11)] INUNION q1 -> { COVERING(STR_VALUE_NUM_VALUE3 [EQUALS q1] -> [NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2], STR_VALUE_INDEXED: KEY:[0]]) ∩ COVERING(NUM_VALUE2_NUM_VALUE3 [EQUALS q0] -> [NUM_VALUE_2: KEY:[0], NUM_VALUE_3_INDEXED: KEY:[1], REC_NO: KEY:[2]]) COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO) | FETCH } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED) } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.metrics.binpb new file mode 100644 index 00000000000..df5f4283ae3 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.metrics.binpb @@ -0,0 +1,22 @@ +É +� +in-query-union-sargableuEXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed in ? order by num_value_3_indexed³ +ƒ±ÐÉ2® ¼à¤(®0âüš8“@é[IN arrayDistinct(@c7) ⋈ IN arrayDistinct(@c11)] INUNION q0, q1 -> { ISCAN(NUM_VALUE2_NUM_VALUE3 [EQUALS q0]) | FILTER _.STR_VALUE_INDEXED EQUALS q1 } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED, _.NUM_VALUE_2)¥digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
In Union
COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED, _.NUM_VALUE_2)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Predicate Filter
WHERE q2.STR_VALUE_INDEXED EQUALS q53
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Index Scan
comparisons: [EQUALS q51]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Index
NUM_VALUE2_NUM_VALUE3
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Values
VALUES([__corr_q51 IN arrayDistinct(@c7), __corr_q53 IN arrayDistinct(@c11)]
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 5 -> 2 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.metrics.yaml new file mode 100644 index 00000000000..9b588ce573a --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.metrics.yaml @@ -0,0 +1,43 @@ +in-query-union-sargable: +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-sargable.yamsql:62 + explain: '[IN arrayDistinct(@c7) ⋈ IN arrayDistinct(@c11)] INUNION q0, q1 -> { + ISCAN(NUM_VALUE2_NUM_VALUE3 [EQUALS q0]) | FILTER _.STR_VALUE_INDEXED EQUALS + q1 } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED, _.NUM_VALUE_2)' + task_count: 2179 + task_total_time_ms: 106 + transform_count: 814 + transform_time_ms: 46 + transform_yield_count: 174 + insert_time_ms: 10 + insert_new_count: 275 + insert_reused_count: 31 +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-sargable.yamsql:103 + explain: '[IN arrayDistinct(@c7) ⋈ IN arrayDistinct(@c11)] INUNION q0, q1 -> { + ISCAN(NUM_VALUE2_NUM_VALUE3 [EQUALS q0]) | FILTER _.STR_VALUE_INDEXED EQUALS + q1 } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED, _.NUM_VALUE_2)' + task_count: 2179 + task_total_time_ms: 106 + transform_count: 814 + transform_time_ms: 46 + transform_yield_count: 174 + insert_time_ms: 10 + insert_new_count: 275 + insert_reused_count: 31 +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? and str_value_indexed + in ? order by num_value_3_indexed + ref: in-query-union-sargable.yamsql:143 + explain: '[IN arrayDistinct(@c7) ⋈ IN arrayDistinct(@c11)] INUNION q0, q1 -> { + ISCAN(NUM_VALUE2_NUM_VALUE3 [EQUALS q0]) | FILTER _.STR_VALUE_INDEXED EQUALS + q1 } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED, _.NUM_VALUE_2)' + task_count: 2179 + task_total_time_ms: 106 + transform_count: 814 + transform_time_ms: 46 + transform_yield_count: 174 + insert_time_ms: 10 + insert_new_count: 275 + insert_reused_count: 31 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.yamsql new file mode 100644 index 00000000000..b636560e627 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query-union-sargable.yamsql @@ -0,0 +1,211 @@ +# +# in-query-union-sargable.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest.testInUnionWithSomeSargable` — two `IN` predicates where only one is +# sargable against NUM_VALUE2_NUM_VALUE3, so one becomes an in-union and the other a residual filter, +# with the sort on NUM_VALUE_3_INDEXED supplied by the index's trailing column. +# +# The source is `@ParameterizedTest` over `maxNumReplansForInToJoin` and a `dropNumValue3Index` flag. +# The replan count is planner configuration the SQL layer does not expose, so it collapses; the index +# variant is covered by in-query-union-intersection{,-no-nv3}.yamsql. The source runs the query with +# three different `str_list` bindings, reproduced as three queries below. +# Both IN lists (`nv2_list`, `str_list`) are parameters in the source, so they are parameter-injected and the +# test block runs as `statement_type: prepared`. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create index num_value2_num_value3 as select num_value_2, num_value_3_indexed from my_simple_record + order by num_value_2, num_value_3_indexed + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query-union-sargable + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testInUnionWithSomeSargable + # str_list = ('even') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['even']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7) ⋈ IN arrayDistinct(@c11)] INUNION q0, q1 -> { ISCAN(NUM_VALUE2_NUM_VALUE3 [EQUALS q0]) | FILTER _.STR_VALUE_INDEXED EQUALS q1 } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}] + - + # testInUnionWithSomeSargable + # str_list = ('odd') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['odd']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7) ⋈ IN arrayDistinct(@c11)] INUNION q0, q1 -> { ISCAN(NUM_VALUE2_NUM_VALUE3 [EQUALS q0]) | FILTER _.STR_VALUE_INDEXED EQUALS q1 } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInUnionWithSomeSargable + # str_list = ('even', 'odd', 'other') + - query: select * from my_simple_record + where num_value_2 in !! !in {[0, 2]} !! and str_value_indexed in !! !in {['even', 'odd', 'other']} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7) ⋈ IN arrayDistinct(@c11)] INUNION q0, q1 -> { ISCAN(NUM_VALUE2_NUM_VALUE3 [EQUALS q0]) | FILTER _.STR_VALUE_INDEXED EQUALS q1 } COMPARE BY (_.NUM_VALUE_3_INDEXED, _.REC_NO, _.STR_VALUE_INDEXED, _.NUM_VALUE_2)" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/in-query.metrics.binpb new file mode 100644 index 00000000000..ebf06dc0806 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query.metrics.binpb @@ -0,0 +1,242 @@ +Ê +N +in-queryBEXPLAIN select * from my_simple_record where num_value_2 in (0, 2)÷ +§šË¹{˜ íÓ„H(_0�šØ8�@KISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 IN @c7‰ digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Predicate Filter
WHERE q2.NUM_VALUE_2 IN @c7
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Index
MY_SIMPLE_RECORD_STR_VALUE_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +}³ +v +in-queryjEXPLAIN select * from my_simple_record where num_value_3_indexed in (1, 2, 4) order by num_value_3_indexed¸ +ö‚��Ë Áþ�(E0�»õ8T@§[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCHîdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS q48]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q85> label="q85" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +}½ +{ +in-queryoEXPLAIN select * from my_simple_record where num_value_3_indexed in (1, 2, 4) order by num_value_3_indexed desc½ +÷ÇáÀÍ ¸ðÓ (G0óêî8X@¬[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCHîdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS q48]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q83> label="q83" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 4 -> 3 [ color="red" style="invis" ]; + } +}³ +v +in-queryjEXPLAIN select * from my_simple_record where num_value_3_indexed in (1, 4, 2) order by num_value_3_indexed¸ +ö‚��Ë Áþ�(E0�»õ8T@§[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCHîdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS q48]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q85> label="q85" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 4 -> 3 [ color="red" style="invis" ]; + } +}½ +{ +in-queryoEXPLAIN select * from my_simple_record where num_value_3_indexed in (1, 4, 2) order by num_value_3_indexed desc½ +÷ÇáÀÍ ¸ðÓ (G0óêî8X@¬[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCHîdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS q48]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q83> label="q83" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 4 -> 3 [ color="red" style="invis" ]; + } +}± +{ +in-queryoEXPLAIN select rec_no from my_simple_record where num_value_3_indexed in (1, 2, 4) order by num_value_3_indexed± +éûïç#Ÿ ÁþÐ(T0¼’Í8l@ƒ[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) | MAP (_.REC_NO AS REC_NO, _.NUM_VALUE_3_INDEXED AS NUM_VALUE_3_INDEXED) } | MAP (_.REC_NO AS REC_NO)‹digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q6.REC_NO AS REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, INT AS NUM_VALUE_3_INDEXED)" ]; + 3 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, INT AS NUM_VALUE_3_INDEXED)" ]; + 4 [ label=<
Value Computation
MAP (q91.REC_NO AS REC_NO, q91.NUM_VALUE_3_INDEXED AS NUM_VALUE_3_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, INT AS NUM_VALUE_3_INDEXED)" ]; + 5 [ label=<
Covering Index Scan
comparisons: [EQUALS q55]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ label=< q91> label="q91" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 5 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q6> label="q6" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +}¼ +€ +in-querytEXPLAIN select rec_no from my_simple_record where num_value_3_indexed in (1, 2, 4) order by num_value_3_indexed desc¶ +±Åê²!«  ÍÓ (Z0¼¾Ã8t@ˆ[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) | MAP (_.REC_NO AS REC_NO, _.NUM_VALUE_3_INDEXED AS NUM_VALUE_3_INDEXED) } | MAP (_.REC_NO AS REC_NO)‹digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Value Computation
MAP (q6.REC_NO AS REC_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, INT AS NUM_VALUE_3_INDEXED)" ]; + 3 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, INT AS NUM_VALUE_3_INDEXED)" ]; + 4 [ label=<
Value Computation
MAP (q91.REC_NO AS REC_NO, q91.NUM_VALUE_3_INDEXED AS NUM_VALUE_3_INDEXED)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, INT AS NUM_VALUE_3_INDEXED)" ]; + 5 [ label=<
Covering Index Scan
comparisons: [EQUALS q55]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 6 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ label=< q91> label="q91" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 5 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q6> label="q6" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +}þ +t +in-queryhEXPLAIN select * from my_simple_record where num_value_3_indexed in (1, 4, 2) order by str_value_indexed… +¤Œ¾Ê² Ý£½(;0ÿÖn89@SISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_3_INDEXED IN @c7‘ digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Predicate Filter
WHERE q2.NUM_VALUE_3_INDEXED IN @c7
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Index
MY_SIMPLE_RECORD_STR_VALUE_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +}½ +y +in-querymEXPLAIN select * from my_simple_record where num_value_3_indexed in (1, 4, 2) order by str_value_indexed desc¿ +¤½Äñ ² �²ö(;0ÚÇW89@[ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,> REVERSE) | FILTER _.NUM_VALUE_3_INDEXED IN @c7à digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Predicate Filter
WHERE q2.NUM_VALUE_3_INDEXED IN @c7
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Index Scan
range: <-∞, ∞>
direction: reversed
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Index
MY_SIMPLE_RECORD_STR_VALUE_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +}Î +R +in-query-prepared=EXPLAIN select * from my_simple_record where num_value_2 in ?÷ +§±ÙÖ˜ º‰³ (_0Šèò8�@KISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 IN @c7‰ digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Predicate Filter
WHERE q2.NUM_VALUE_2 IN @c7
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Index Scan
range: <-∞, ∞>
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Index
MY_SIMPLE_RECORD_STR_VALUE_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q2> label="q2" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; +}“ +Z +in-query-preparedEEXPLAIN select * from my_simple_record where num_value_3_indexed in ?´ +± Ü¡®© ‡÷Ô (d0ú„Þ8±@  [IN arrayDistinct(@c7)] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCHðdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS q77]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q142> label="q142" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +}³ +w +in-query-preparedbEXPLAIN select * from my_simple_record where num_value_3_indexed in ? order by num_value_3_indexed· +öР÷Ë ™¢‚(E0¤¡|8T@§[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCHîdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Covering Index Scan
comparisons: [EQUALS q48]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q85> label="q85" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 4 -> 3 [ color="red" style="invis" ]; + } +}½ +| +in-query-preparedgEXPLAIN select * from my_simple_record where num_value_3_indexed in ? order by num_value_3_indexed desc¼ +÷’ªÿ Í Ò€Ó(G0�Œ{8X@¬[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCHîdigraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 2 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 [ label=<
Table Function
EXPLODE(arrayDistinct(@c7))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 4 [ label=<
Covering Index Scan
comparisons: [EQUALS q48]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 5 [ label=<
Index
MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REC_NO, STRING AS STR_VALUE_INDEXED, INT AS NUM_VALUE_UNIQUE, INT AS NUM_VALUE_2, INT AS NUM_VALUE_3_INDEXED, ARRAY(INT) AS REPEATER)" ]; + 3 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 2 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 4 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q83> label="q83" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + rank=same; + rankDir=LR; + 3 -> 4 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/in-query.metrics.yaml new file mode 100644 index 00000000000..0bd5ef5a9ea --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query.metrics.yaml @@ -0,0 +1,172 @@ +in-query: +- query: EXPLAIN select * from my_simple_record where num_value_2 in (0, 2) + ref: in-query.yamsql:64 + explain: ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 + IN @c7 + task_count: 1063 + task_total_time_ms: 258 + transform_count: 280 + transform_time_ms: 151 + transform_yield_count: 95 + insert_time_ms: 14 + insert_new_count: 157 + insert_reused_count: 8 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, + 2, 4) order by num_value_3_indexed + ref: in-query.yamsql:137 + explain: '[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH' + task_count: 758 + task_total_time_ms: 60 + transform_count: 203 + transform_time_ms: 29 + transform_yield_count: 69 + insert_time_ms: 4 + insert_new_count: 84 + insert_reused_count: 4 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, + 2, 4) order by num_value_3_indexed desc + ref: in-query.yamsql:205 + explain: '[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH' + task_count: 759 + task_total_time_ms: 47 + transform_count: 205 + transform_time_ms: 20 + transform_yield_count: 71 + insert_time_ms: 3 + insert_new_count: 88 + insert_reused_count: 6 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, + 4, 2) order by num_value_3_indexed + ref: in-query.yamsql:272 + explain: '[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH' + task_count: 758 + task_total_time_ms: 60 + transform_count: 203 + transform_time_ms: 29 + transform_yield_count: 69 + insert_time_ms: 4 + insert_new_count: 84 + insert_reused_count: 4 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, + 4, 2) order by num_value_3_indexed desc + ref: in-query.yamsql:338 + explain: '[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH' + task_count: 759 + task_total_time_ms: 47 + transform_count: 205 + transform_time_ms: 20 + transform_yield_count: 71 + insert_time_ms: 3 + insert_new_count: 88 + insert_reused_count: 6 +- query: EXPLAIN select rec_no from my_simple_record where num_value_3_indexed in + (1, 2, 4) order by num_value_3_indexed + ref: in-query.yamsql:406 + explain: '[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) | MAP (_.REC_NO + AS REC_NO, _.NUM_VALUE_3_INDEXED AS NUM_VALUE_3_INDEXED) } | MAP (_.REC_NO + AS REC_NO)' + task_count: 1001 + task_total_time_ms: 75 + transform_count: 287 + transform_time_ms: 30 + transform_yield_count: 84 + insert_time_ms: 3 + insert_new_count: 108 + insert_reused_count: 3 +- query: EXPLAIN select rec_no from my_simple_record where num_value_3_indexed in + (1, 2, 4) order by num_value_3_indexed desc + ref: in-query.yamsql:473 + explain: '[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) | MAP (_.REC_NO + AS REC_NO, _.NUM_VALUE_3_INDEXED AS NUM_VALUE_3_INDEXED) } | MAP (_.REC_NO + AS REC_NO)' + task_count: 1073 + task_total_time_ms: 70 + transform_count: 299 + transform_time_ms: 26 + transform_yield_count: 90 + insert_time_ms: 3 + insert_new_count: 116 + insert_reused_count: 3 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, + 4, 2) order by str_value_indexed + ref: in-query.yamsql:539 + explain: ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_3_INDEXED + IN @c7 + task_count: 548 + task_total_time_ms: 34 + transform_count: 178 + transform_time_ms: 17 + transform_yield_count: 59 + insert_time_ms: 1 + insert_new_count: 57 + insert_reused_count: 3 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in (1, + 4, 2) order by str_value_indexed desc + ref: in-query.yamsql:604 + explain: ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,> REVERSE) | FILTER _.NUM_VALUE_3_INDEXED + IN @c7 + task_count: 548 + task_total_time_ms: 29 + transform_count: 178 + transform_time_ms: 14 + transform_yield_count: 59 + insert_time_ms: 1 + insert_new_count: 57 + insert_reused_count: 3 +in-query-prepared: +- query: EXPLAIN select * from my_simple_record where num_value_2 in ? + ref: in-query.yamsql:676 + explain: ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 + IN @c7 + task_count: 1063 + task_total_time_ms: 60 + transform_count: 280 + transform_time_ms: 26 + transform_yield_count: 95 + insert_time_ms: 3 + insert_new_count: 157 + insert_reused_count: 8 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in ? + ref: in-query.yamsql:713 + explain: '[IN arrayDistinct(@c7)] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH' + task_count: 1201 + task_total_time_ms: 57 + transform_count: 297 + transform_time_ms: 24 + transform_yield_count: 100 + insert_time_ms: 3 + insert_new_count: 177 + insert_reused_count: 9 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in ? order + by num_value_3_indexed + ref: in-query.yamsql:779 + explain: '[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH' + task_count: 758 + task_total_time_ms: 31 + transform_count: 203 + transform_time_ms: 14 + transform_yield_count: 69 + insert_time_ms: 2 + insert_new_count: 84 + insert_reused_count: 4 +- query: EXPLAIN select * from my_simple_record where num_value_3_indexed in ? order + by num_value_3_indexed desc + ref: in-query.yamsql:844 + explain: '[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED + [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH' + task_count: 759 + task_total_time_ms: 29 + transform_count: 205 + transform_time_ms: 13 + transform_yield_count: 71 + insert_time_ms: 2 + insert_new_count: 88 + insert_reused_count: 6 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/in-query.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/in-query.yamsql new file mode 100644 index 00000000000..dc1487d2fc6 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/in-query.yamsql @@ -0,0 +1,904 @@ +# +# in-query.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ported from `FDBInQueryTest` — planning an `IN` predicate as an in-join or in-union over an index, +# or as a residual filter when no index can serve it. +# +# This file holds the tests that run against the unmodified `test_records_1.proto` index set +# (`complexQuerySetup(NO_HOOK)`). Tests needing extra indexes or a different base record type live in +# the other in-query-* siblings. +# +# The source parameterizes many of these on `reverse`, `normalizeNestedFields`, +# `omitPrimaryKeyInOrdering` and `InAsOrUnionMode`. Only `reverse` survives the port: the other three +# are old-planner configuration or planner knobs the SQL layer does not expose (it pins +# `attemptFailedInJoinAsUnionMaxSize = 24`). `reverse` becomes `ORDER BY ... DESC`. +# +# `IN` lists are written as literals. `AstNormalizer` lifts them into `ConstantObjectValue`s, so the +# plan is the same one the source's `Query.field(...).in("bindingName")` variants produce. +# +# Not ported from this class: +# testInQueryParameterBad — binds a non-list value to an IN parameter; an IN list in SQL is always +# a list, so there is nothing to express +# testOneOfThemIn, cnfAsInQuery, and the other plain `@Test` methods — old planner only +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: in-query + preset: single_repetition_ordered + tests: + - + # testInQueryNoIndex — NUM_VALUE_2 has no index, so the IN stays a residual predicate. + - query: select * from my_simple_record where num_value_2 in (0, 2) + - explain: "ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 IN @c7" + - unorderedResult: [{REC_NO: 0, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 5, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 15, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 20, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 30, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 970, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 35, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 965, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 45, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 955, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 50, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 950, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 60, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 940, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 65, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 935, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 75, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 925, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 80, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 920, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 90, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 910, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 95, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 905, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQueryIndex — an IN over an indexed column, with the list already in order, sorted on that + # column, becomes an in-join over index ranges. + - query: select * from my_simple_record where num_value_3_indexed in (1, 2, 4) + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH" + - unorderedResult: [{REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # The reverse-sorted variant of the same query. The in-join iterates the IN list in + # descending order but still scans each leg forward, so the emission order is + # NUM_VALUE_3_INDEXED descending with REC_NO ascending inside each group. Only the + # NUM_VALUE_3_INDEXED ordering is actually required by the query. + - query: select * from my_simple_record where num_value_3_indexed in (1, 2, 4) + order by num_value_3_indexed desc + - explain: "[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH" + - unorderedResult: [{REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}] + - + # testInQueryIndexSorted — the same query with the IN list given out of order. The list must be + # sorted to match the ORDER BY, which the SORTED flag in the explain records. Ordered `result`, + # unlike the sorted-list case above, so that the order itself is checked. + - query: select * from my_simple_record where num_value_3_indexed in (1, 4, 2) + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH" + - result: [{REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQueryIndexSorted reverse — the out-of-order list sorted descending. Each leg is still scanned + # forward, so rows come out NUM_VALUE_3_INDEXED descending with REC_NO ascending inside each group. + - query: select * from my_simple_record where num_value_3_indexed in (1, 4, 2) + order by num_value_3_indexed desc + - explain: "[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH" + - result: [{REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}] + - + # testInQueryCoveringIndex — only REC_NO is projected, which the index carries, so the in-union + # legs should be covering scans. The in-union's comparison key is + # (num_value_3_indexed, rec_no), which is the order asserted below; only the leading column is + # named in the ORDER BY because the primary key cannot be used as a tie-break here. + - query: select rec_no from my_simple_record where num_value_3_indexed in (1, 2, 4) + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) | MAP (_.REC_NO AS REC_NO, _.NUM_VALUE_3_INDEXED AS NUM_VALUE_3_INDEXED) } | MAP (_.REC_NO AS REC_NO)" + - unorderedResult: [{REC_NO: 1}, + {REC_NO: 6}, + {REC_NO: 11}, + {REC_NO: 16}, + {REC_NO: 21}, + {REC_NO: 26}, + {REC_NO: 31}, + {REC_NO: 36}, + {REC_NO: 41}, + {REC_NO: 46}, + {REC_NO: 51}, + {REC_NO: 56}, + {REC_NO: 61}, + {REC_NO: 66}, + {REC_NO: 71}, + {REC_NO: 76}, + {REC_NO: 81}, + {REC_NO: 86}, + {REC_NO: 91}, + {REC_NO: 96}, + {REC_NO: 2}, + {REC_NO: 7}, + {REC_NO: 12}, + {REC_NO: 17}, + {REC_NO: 22}, + {REC_NO: 27}, + {REC_NO: 32}, + {REC_NO: 37}, + {REC_NO: 42}, + {REC_NO: 47}, + {REC_NO: 52}, + {REC_NO: 57}, + {REC_NO: 62}, + {REC_NO: 67}, + {REC_NO: 72}, + {REC_NO: 77}, + {REC_NO: 82}, + {REC_NO: 87}, + {REC_NO: 92}, + {REC_NO: 97}, + {REC_NO: 4}, + {REC_NO: 9}, + {REC_NO: 14}, + {REC_NO: 19}, + {REC_NO: 24}, + {REC_NO: 29}, + {REC_NO: 34}, + {REC_NO: 39}, + {REC_NO: 44}, + {REC_NO: 49}, + {REC_NO: 54}, + {REC_NO: 59}, + {REC_NO: 64}, + {REC_NO: 69}, + {REC_NO: 74}, + {REC_NO: 79}, + {REC_NO: 84}, + {REC_NO: 89}, + {REC_NO: 94}, + {REC_NO: 99}] + - + # testInQueryCoveringIndex reverse — the same covering query sorted descending. The list is iterated + # in descending order and each leg is still scanned forward, so rows come out NUM_VALUE_3_INDEXED + # descending with REC_NO ascending inside each group; only REC_NO is projected. + - query: select rec_no from my_simple_record where num_value_3_indexed in (1, 2, 4) + order by num_value_3_indexed desc + - explain: "[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) | MAP (_.REC_NO AS REC_NO, _.NUM_VALUE_3_INDEXED AS NUM_VALUE_3_INDEXED) } | MAP (_.REC_NO AS REC_NO)" + - result: [{REC_NO: 4}, + {REC_NO: 9}, + {REC_NO: 14}, + {REC_NO: 19}, + {REC_NO: 24}, + {REC_NO: 29}, + {REC_NO: 34}, + {REC_NO: 39}, + {REC_NO: 44}, + {REC_NO: 49}, + {REC_NO: 54}, + {REC_NO: 59}, + {REC_NO: 64}, + {REC_NO: 69}, + {REC_NO: 74}, + {REC_NO: 79}, + {REC_NO: 84}, + {REC_NO: 89}, + {REC_NO: 94}, + {REC_NO: 99}, + {REC_NO: 2}, + {REC_NO: 7}, + {REC_NO: 12}, + {REC_NO: 17}, + {REC_NO: 22}, + {REC_NO: 27}, + {REC_NO: 32}, + {REC_NO: 37}, + {REC_NO: 42}, + {REC_NO: 47}, + {REC_NO: 52}, + {REC_NO: 57}, + {REC_NO: 62}, + {REC_NO: 67}, + {REC_NO: 72}, + {REC_NO: 77}, + {REC_NO: 82}, + {REC_NO: 87}, + {REC_NO: 92}, + {REC_NO: 97}, + {REC_NO: 1}, + {REC_NO: 6}, + {REC_NO: 11}, + {REC_NO: 16}, + {REC_NO: 21}, + {REC_NO: 26}, + {REC_NO: 31}, + {REC_NO: 36}, + {REC_NO: 41}, + {REC_NO: 46}, + {REC_NO: 51}, + {REC_NO: 56}, + {REC_NO: 61}, + {REC_NO: 66}, + {REC_NO: 71}, + {REC_NO: 76}, + {REC_NO: 81}, + {REC_NO: 86}, + {REC_NO: 91}, + {REC_NO: 96}] + - + # testInQueryIndexSortedDifferently — the sort is on a *different* column than the IN, so the + # in-union cannot supply the order and the plan needs a separate sort or a different index. + - query: select * from my_simple_record where num_value_3_indexed in (1, 4, 2) + order by str_value_indexed + - explain: "ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_3_INDEXED IN @c7" + - unorderedResult: [{REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQueryIndexSortedDifferently reverse — the same query sorted descending on the other column. + - query: select * from my_simple_record where num_value_3_indexed in (1, 4, 2) + order by str_value_indexed desc + - explain: "ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,> REVERSE) | FILTER _.NUM_VALUE_3_INDEXED IN @c7" + - unorderedResult: [{REC_NO: 2, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 12, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 32, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 968, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 42, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 958, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 62, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 938, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 72, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 928, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 92, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 908, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 17, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 27, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 47, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 953, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 57, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 943, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 77, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 923, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 87, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 913, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] +--- +test_block: + name: in-query-prepared + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testInQueryNoIndexWithParameter — the same shape with values that the fixture only partly + # covers: NUM_VALUE_2 is i % 3, so 3 never occurs. + - query: select * from my_simple_record where num_value_2 in !! !in {[1,3]} !! + - explain: "ISCAN(MY_SIMPLE_RECORD_STR_VALUE_INDEXED <,>) | FILTER _.NUM_VALUE_2 IN @c7" + - unorderedResult: [{REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 7, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 10, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 13, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 22, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 25, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 28, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 37, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 963, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 40, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 960, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 43, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 957, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 52, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 948, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 55, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 945, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 58, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 942, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 67, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 933, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}, + {REC_NO: 70, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 930, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: []}, + {REC_NO: 73, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 927, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 82, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 918, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1]}, + {REC_NO: 85, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 915, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, REPEATER: [0, 1, 2, 3, 4]}, + {REC_NO: 88, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 912, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 97, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 903, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, REPEATER: [0, 1, 2, 3, 4, 5, 6]}] + - + # testInQueryParameter — the IN list arrives as a parameter and there is no ORDER BY. + - query: select * from my_simple_record where num_value_3_indexed in !! !in {[1, 3, 4]} !! + - explain: "[IN arrayDistinct(@c7)] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH" + - unorderedResult: [{REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 13, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 28, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 43, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 957, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 58, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 942, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 73, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 927, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 88, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 912, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQuerySortedByParameter — the IN list arrives as a parameter, sorted on the IN column. A + # three-element list where one value (4) is at the top of the column's range. + - query: select * from my_simple_record where num_value_3_indexed in !! !in {[1, 3, 4]} !! + order by num_value_3_indexed + - explain: "[IN arrayDistinct(@c7) SORTED] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH" + - unorderedResult: [{REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 13, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 28, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 43, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 957, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 58, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 942, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 73, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 927, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 88, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 912, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] + - + # testInQuerySortedByParameter reverse — the same query sorted descending. + - query: select * from my_simple_record where num_value_3_indexed in !! !in {[1, 3, 4]} !! + order by num_value_3_indexed desc + - explain: "[IN arrayDistinct(@c7) SORTED DESC] | INJOIN q0 -> { COVERING(MY_SIMPLE_RECORD_NUM_VALUE_3_INDEXED [EQUALS q0] -> [NUM_VALUE_3_INDEXED: KEY:[0], REC_NO: KEY:[1]]) } | FETCH" + - unorderedResult: [{REC_NO: 1, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 6, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 11, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 16, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 21, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 26, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 31, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 969, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 36, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 964, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 41, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 959, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 46, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 954, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 51, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 949, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 56, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 944, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 61, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 939, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 66, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 934, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 71, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 929, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 76, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 924, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 81, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 919, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 86, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 914, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 91, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 909, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, REPEATER: [0]}, + {REC_NO: 96, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 904, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, REPEATER: [0, 1, 2, 3, 4, 5]}, + {REC_NO: 3, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 8, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 13, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 18, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 23, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 28, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 33, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 967, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 38, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 962, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 43, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 957, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 48, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 952, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 53, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 947, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 58, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 942, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 63, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 937, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 68, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 932, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 73, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 927, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 78, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 922, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 83, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 917, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 88, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 912, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 93, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 907, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2]}, + {REC_NO: 98, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 902, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7]}, + {REC_NO: 4, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 9, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 14, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 19, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 24, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 29, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 34, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 966, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 39, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 961, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 44, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 956, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 49, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 951, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 54, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 946, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 59, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 941, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 64, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 936, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 69, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 931, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 74, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 926, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 79, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 921, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 84, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 916, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 89, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 911, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}, + {REC_NO: 94, STR_VALUE_INDEXED: 'even', NUM_VALUE_UNIQUE: 906, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3]}, + {REC_NO: 99, STR_VALUE_INDEXED: 'odd', NUM_VALUE_UNIQUE: 901, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, REPEATER: [0, 1, 2, 3, 4, 5, 6, 7, 8]}] diff --git a/yaml-tests/src/test/resources/cascades-query-tests/includes/simple-record-100.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/includes/simple-record-100.yamsql new file mode 100644 index 00000000000..434ae27246c --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/includes/simple-record-100.yamsql @@ -0,0 +1,141 @@ +# +# simple-record-100.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The shared 100-record fixture used by most of the ported tests. This is the SQL equivalent of +# `FDBRecordStoreQueryTestBase.complexQuerySetup`, which writes 100 MySimpleRecord rows with +# +# rec_no = i +# str_value_indexed = (i & 1) == 1 ? "odd" : "even" +# num_value_unique = 1000 - i +# num_value_2 = i % 3 +# num_value_3_indexed = i % 5 +# repeater = [0 .. i % 10) +# +# Including this file requires the includer to declare exactly one `schema_template` containing a +# MY_SIMPLE_RECORD table with these six columns. A file that declares more than one schema +# template cannot use this include: an included `setup:` without an explicit `connect:` resolves +# to the single available connection URI and fails when several are registered. +--- +setup: + steps: + - query: | + INSERT INTO MY_SIMPLE_RECORD + (REC_NO, STR_VALUE_INDEXED, NUM_VALUE_UNIQUE, NUM_VALUE_2, NUM_VALUE_3_INDEXED, REPEATER) + VALUES + (0, 'even', 1000, 0, 0, []), + (1, 'odd', 999, 1, 1, [0]), + (2, 'even', 998, 2, 2, [0, 1]), + (3, 'odd', 997, 0, 3, [0, 1, 2]), + (4, 'even', 996, 1, 4, [0, 1, 2, 3]), + (5, 'odd', 995, 2, 0, [0, 1, 2, 3, 4]), + (6, 'even', 994, 0, 1, [0, 1, 2, 3, 4, 5]), + (7, 'odd', 993, 1, 2, [0, 1, 2, 3, 4, 5, 6]), + (8, 'even', 992, 2, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (9, 'odd', 991, 0, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (10, 'even', 990, 1, 0, []), + (11, 'odd', 989, 2, 1, [0]), + (12, 'even', 988, 0, 2, [0, 1]), + (13, 'odd', 987, 1, 3, [0, 1, 2]), + (14, 'even', 986, 2, 4, [0, 1, 2, 3]), + (15, 'odd', 985, 0, 0, [0, 1, 2, 3, 4]), + (16, 'even', 984, 1, 1, [0, 1, 2, 3, 4, 5]), + (17, 'odd', 983, 2, 2, [0, 1, 2, 3, 4, 5, 6]), + (18, 'even', 982, 0, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (19, 'odd', 981, 1, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (20, 'even', 980, 2, 0, []), + (21, 'odd', 979, 0, 1, [0]), + (22, 'even', 978, 1, 2, [0, 1]), + (23, 'odd', 977, 2, 3, [0, 1, 2]), + (24, 'even', 976, 0, 4, [0, 1, 2, 3]), + (25, 'odd', 975, 1, 0, [0, 1, 2, 3, 4]), + (26, 'even', 974, 2, 1, [0, 1, 2, 3, 4, 5]), + (27, 'odd', 973, 0, 2, [0, 1, 2, 3, 4, 5, 6]), + (28, 'even', 972, 1, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (29, 'odd', 971, 2, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (30, 'even', 970, 0, 0, []), + (31, 'odd', 969, 1, 1, [0]), + (32, 'even', 968, 2, 2, [0, 1]), + (33, 'odd', 967, 0, 3, [0, 1, 2]), + (34, 'even', 966, 1, 4, [0, 1, 2, 3]), + (35, 'odd', 965, 2, 0, [0, 1, 2, 3, 4]), + (36, 'even', 964, 0, 1, [0, 1, 2, 3, 4, 5]), + (37, 'odd', 963, 1, 2, [0, 1, 2, 3, 4, 5, 6]), + (38, 'even', 962, 2, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (39, 'odd', 961, 0, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (40, 'even', 960, 1, 0, []), + (41, 'odd', 959, 2, 1, [0]), + (42, 'even', 958, 0, 2, [0, 1]), + (43, 'odd', 957, 1, 3, [0, 1, 2]), + (44, 'even', 956, 2, 4, [0, 1, 2, 3]), + (45, 'odd', 955, 0, 0, [0, 1, 2, 3, 4]), + (46, 'even', 954, 1, 1, [0, 1, 2, 3, 4, 5]), + (47, 'odd', 953, 2, 2, [0, 1, 2, 3, 4, 5, 6]), + (48, 'even', 952, 0, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (49, 'odd', 951, 1, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (50, 'even', 950, 2, 0, []), + (51, 'odd', 949, 0, 1, [0]), + (52, 'even', 948, 1, 2, [0, 1]), + (53, 'odd', 947, 2, 3, [0, 1, 2]), + (54, 'even', 946, 0, 4, [0, 1, 2, 3]), + (55, 'odd', 945, 1, 0, [0, 1, 2, 3, 4]), + (56, 'even', 944, 2, 1, [0, 1, 2, 3, 4, 5]), + (57, 'odd', 943, 0, 2, [0, 1, 2, 3, 4, 5, 6]), + (58, 'even', 942, 1, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (59, 'odd', 941, 2, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (60, 'even', 940, 0, 0, []), + (61, 'odd', 939, 1, 1, [0]), + (62, 'even', 938, 2, 2, [0, 1]), + (63, 'odd', 937, 0, 3, [0, 1, 2]), + (64, 'even', 936, 1, 4, [0, 1, 2, 3]), + (65, 'odd', 935, 2, 0, [0, 1, 2, 3, 4]), + (66, 'even', 934, 0, 1, [0, 1, 2, 3, 4, 5]), + (67, 'odd', 933, 1, 2, [0, 1, 2, 3, 4, 5, 6]), + (68, 'even', 932, 2, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (69, 'odd', 931, 0, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (70, 'even', 930, 1, 0, []), + (71, 'odd', 929, 2, 1, [0]), + (72, 'even', 928, 0, 2, [0, 1]), + (73, 'odd', 927, 1, 3, [0, 1, 2]), + (74, 'even', 926, 2, 4, [0, 1, 2, 3]), + (75, 'odd', 925, 0, 0, [0, 1, 2, 3, 4]), + (76, 'even', 924, 1, 1, [0, 1, 2, 3, 4, 5]), + (77, 'odd', 923, 2, 2, [0, 1, 2, 3, 4, 5, 6]), + (78, 'even', 922, 0, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (79, 'odd', 921, 1, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (80, 'even', 920, 2, 0, []), + (81, 'odd', 919, 0, 1, [0]), + (82, 'even', 918, 1, 2, [0, 1]), + (83, 'odd', 917, 2, 3, [0, 1, 2]), + (84, 'even', 916, 0, 4, [0, 1, 2, 3]), + (85, 'odd', 915, 1, 0, [0, 1, 2, 3, 4]), + (86, 'even', 914, 2, 1, [0, 1, 2, 3, 4, 5]), + (87, 'odd', 913, 0, 2, [0, 1, 2, 3, 4, 5, 6]), + (88, 'even', 912, 1, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (89, 'odd', 911, 2, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]), + (90, 'even', 910, 0, 0, []), + (91, 'odd', 909, 1, 1, [0]), + (92, 'even', 908, 2, 2, [0, 1]), + (93, 'odd', 907, 0, 3, [0, 1, 2]), + (94, 'even', 906, 1, 4, [0, 1, 2, 3]), + (95, 'odd', 905, 2, 0, [0, 1, 2, 3, 4]), + (96, 'even', 904, 0, 1, [0, 1, 2, 3, 4, 5]), + (97, 'odd', 903, 1, 2, [0, 1, 2, 3, 4, 5, 6]), + (98, 'even', 902, 2, 3, [0, 1, 2, 3, 4, 5, 6, 7]), + (99, 'odd', 901, 0, 4, [0, 1, 2, 3, 4, 5, 6, 7, 8]) +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.metrics.binpb new file mode 100644 index 00000000000..a4273555716 Binary files /dev/null and b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.metrics.binpb differ diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.metrics.yaml new file mode 100644 index 00000000000..603cfbcee00 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.metrics.yaml @@ -0,0 +1,34 @@ +permuted-min-max-num-value-2: +- query: EXPLAIN select str_value_indexed, num_value_3_indexed, max(num_value_2) + as m from my_simple_record group by str_value_indexed, num_value_3_indexed + having str_value_indexed in ('even', 'odd') order by max(num_value_2) desc + ref: permuted-min-max-num-value-2.yamsql:72 + explain: '[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_2_BY_STR_VALUE_THREE + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | + MAP (_._0 AS STR_VALUE_INDEXED, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } + COMPARE BY (_.M, _.STR_VALUE_INDEXED, _.NUM_VALUE_3_INDEXED)' + task_count: 556 + task_total_time_ms: 26 + transform_count: 221 + transform_time_ms: 17 + transform_yield_count: 63 + insert_time_ms: 1 + insert_new_count: 36 + insert_reused_count: 0 +permuted-min-max-num-value-2-prepared: +- query: EXPLAIN select str_value_indexed, num_value_3_indexed, max(num_value_2) + as m from my_simple_record group by str_value_indexed, num_value_3_indexed + having str_value_indexed in ? order by max(num_value_2) desc + ref: permuted-min-max-num-value-2.yamsql:100 + explain: '[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_2_BY_STR_VALUE_THREE + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | + MAP (_._0 AS STR_VALUE_INDEXED, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } + COMPARE BY (_.M, _.STR_VALUE_INDEXED, _.NUM_VALUE_3_INDEXED)' + task_count: 556 + task_total_time_ms: 26 + transform_count: 221 + transform_time_ms: 18 + transform_yield_count: 63 + insert_time_ms: 0 + insert_new_count: 36 + insert_reused_count: 0 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.yamsql new file mode 100644 index 00000000000..18b9ff1311c --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-num-value-2.yamsql @@ -0,0 +1,111 @@ +# +# permuted-min-max-num-value-2.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Ported from `FDBPermutedMinMaxQueryTest.testMaxWithInAndDupes` — a PERMUTED_MAX index over +# max(num_value_2) grouped by (str_value_indexed, num_value_3_indexed), queried with an IN list on the +# leading grouping column. +# +# Its own file: `max2ByStrValueAnd3` is the only index the hook adds, and it aggregates a different +# column from the rest of the class. +# +# "AndDupes" is literal — every group's maximum is the same. STR_VALUE_INDEXED and NUM_VALUE_3_INDEXED +# together fix i modulo 10, and the ten records in each such class run through every residue of i modulo +# 3, so max(num_value_2) is 2 for all ten groups. The result is therefore asserted unordered: with the +# whole sort key tied, the sequence is whatever order the in-union happens to drain its legs in, which +# the source does not pin down either. +# +# See permuted-min-max.yamsql for how the permuted size is derived and how the three `InComparisonCase` +# variants map onto the literal block and the `statement_type: prepared` block. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index max_num_value_2_by_str_value_three as + select str_value_indexed, max(num_value_2), num_value_3_indexed from my_simple_record + group by str_value_indexed, num_value_3_indexed + order by str_value_indexed, max(num_value_2), num_value_3_indexed + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: permuted-min-max-num-value-2 + preset: single_repetition_ordered + tests: + - + # testMaxWithInAndDupes [literal IN list: byLiteral / byConstantObjectValue] + # testMaxWithInAndDupes + # in-union over the aggregate index, comparison key (m, str_value_indexed, + # num_value_3_indexed). + - query: select str_value_indexed, num_value_3_indexed, max(num_value_2) as m + from my_simple_record + group by str_value_indexed, num_value_3_indexed + having str_value_indexed in ('even', 'odd') + order by max(num_value_2) desc + - explain: "[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_2_BY_STR_VALUE_THREE [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE BY (_.M, _.STR_VALUE_INDEXED, _.NUM_VALUE_3_INDEXED)" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 0, M: 2}, + {STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 1, M: 2}, + {STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 2, M: 2}, + {STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 3, M: 2}, + {STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 4, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 0, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 1, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 2, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 3, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 4, M: 2}] +--- +test_block: + name: permuted-min-max-num-value-2-prepared + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testMaxWithInAndDupes [parameter: byParameter] + # testMaxWithInAndDupes + # in-union over the aggregate index, comparison key (m, str_value_indexed, + # num_value_3_indexed). + - query: select str_value_indexed, num_value_3_indexed, max(num_value_2) as m + from my_simple_record + group by str_value_indexed, num_value_3_indexed + having str_value_indexed in !! !in {['even', 'odd']} !! + order by max(num_value_2) desc + - explain: "[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_2_BY_STR_VALUE_THREE [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE BY (_.M, _.STR_VALUE_INDEXED, _.NUM_VALUE_3_INDEXED)" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 0, M: 2}, + {STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 1, M: 2}, + {STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 2, M: 2}, + {STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 3, M: 2}, + {STR_VALUE_INDEXED: 'even', NUM_VALUE_3_INDEXED: 4, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 0, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 1, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 2, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 3, M: 2}, + {STR_VALUE_INDEXED: 'odd', NUM_VALUE_3_INDEXED: 4, M: 2}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.metrics.binpb new file mode 100644 index 00000000000..dc4515f53e1 Binary files /dev/null and b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.metrics.binpb differ diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.metrics.yaml new file mode 100644 index 00000000000..263ba1c6cac --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.metrics.yaml @@ -0,0 +1,144 @@ +permuted-min-max-ordering-keys: +- query: EXPLAIN select str_value_indexed, max(num_value_unique) as m, num_value_2, + num_value_3_indexed from my_simple_record group by str_value_indexed, num_value_2, + num_value_3_indexed having str_value_indexed in ('even', 'odd', 'empty', 'other1', + 'other2') + ref: permuted-min-max-ordering-keys.yamsql:91 + explain: '[IN arrayDistinct(@c25)] | INJOIN q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 + [EQUALS q0] BY_GROUP -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) + | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS + NUM_VALUE_3_INDEXED) }' + task_count: 975 + task_total_time_ms: 245 + transform_count: 293 + transform_time_ms: 152 + transform_yield_count: 84 + insert_time_ms: 8 + insert_new_count: 107 + insert_reused_count: 3 +- query: EXPLAIN select str_value_indexed, max(num_value_unique) as m, num_value_2, + num_value_3_indexed from my_simple_record group by str_value_indexed, num_value_2, + num_value_3_indexed having str_value_indexed in ('even', 'odd', 'empty', 'other1', + 'other2') order by max(num_value_unique) desc + ref: permuted-min-max-ordering-keys.yamsql:137 + explain: '[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: + KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, + _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.STR_VALUE_INDEXED, _.NUM_VALUE_2, + _.NUM_VALUE_3_INDEXED)' + task_count: 564 + task_total_time_ms: 72 + transform_count: 221 + transform_time_ms: 48 + transform_yield_count: 59 + insert_time_ms: 1 + insert_new_count: 39 + insert_reused_count: 0 +- query: EXPLAIN select str_value_indexed, max(num_value_unique) as m, num_value_2, + num_value_3_indexed from my_simple_record group by str_value_indexed, num_value_2, + num_value_3_indexed having str_value_indexed in ('even', 'odd', 'empty', 'other1', + 'other2') order by max(num_value_unique) desc, num_value_2 desc + ref: permuted-min-max-ordering-keys.yamsql:183 + explain: '[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: + KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, + _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.NUM_VALUE_2, _.STR_VALUE_INDEXED, + _.NUM_VALUE_3_INDEXED)' + task_count: 544 + task_total_time_ms: 61 + transform_count: 217 + transform_time_ms: 43 + transform_yield_count: 57 + insert_time_ms: 1 + insert_new_count: 36 + insert_reused_count: 0 +- query: EXPLAIN select str_value_indexed, max(num_value_unique) as m, num_value_2, + num_value_3_indexed from my_simple_record group by str_value_indexed, num_value_2, + num_value_3_indexed having str_value_indexed in ('even', 'odd', 'empty', 'other1', + 'other2') order by max(num_value_unique) desc, num_value_2 desc, num_value_3_indexed + desc + ref: permuted-min-max-ordering-keys.yamsql:229 + explain: '[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: + KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, + _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED, + _.STR_VALUE_INDEXED)' + task_count: 535 + task_total_time_ms: 55 + transform_count: 214 + transform_time_ms: 39 + transform_yield_count: 55 + insert_time_ms: 1 + insert_new_count: 34 + insert_reused_count: 1 +permuted-min-max-ordering-keys-prepared: +- query: EXPLAIN select str_value_indexed, max(num_value_unique) as m, num_value_2, + num_value_3_indexed from my_simple_record group by str_value_indexed, num_value_2, + num_value_3_indexed having str_value_indexed in ? + ref: permuted-min-max-ordering-keys.yamsql:281 + explain: '[IN arrayDistinct(@c25)] | INJOIN q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 + [EQUALS q0] BY_GROUP -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) + | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS + NUM_VALUE_3_INDEXED) }' + task_count: 975 + task_total_time_ms: 82 + transform_count: 293 + transform_time_ms: 41 + transform_yield_count: 84 + insert_time_ms: 3 + insert_new_count: 107 + insert_reused_count: 3 +- query: EXPLAIN select str_value_indexed, max(num_value_unique) as m, num_value_2, + num_value_3_indexed from my_simple_record group by str_value_indexed, num_value_2, + num_value_3_indexed having str_value_indexed in ? order by max(num_value_unique) + desc + ref: permuted-min-max-ordering-keys.yamsql:327 + explain: '[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: + KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, + _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.STR_VALUE_INDEXED, _.NUM_VALUE_2, + _.NUM_VALUE_3_INDEXED)' + task_count: 564 + task_total_time_ms: 43 + transform_count: 221 + transform_time_ms: 29 + transform_yield_count: 59 + insert_time_ms: 1 + insert_new_count: 39 + insert_reused_count: 0 +- query: EXPLAIN select str_value_indexed, max(num_value_unique) as m, num_value_2, + num_value_3_indexed from my_simple_record group by str_value_indexed, num_value_2, + num_value_3_indexed having str_value_indexed in ? order by max(num_value_unique) + desc, num_value_2 desc + ref: permuted-min-max-ordering-keys.yamsql:373 + explain: '[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: + KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, + _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.NUM_VALUE_2, _.STR_VALUE_INDEXED, + _.NUM_VALUE_3_INDEXED)' + task_count: 544 + task_total_time_ms: 41 + transform_count: 217 + transform_time_ms: 29 + transform_yield_count: 57 + insert_time_ms: 0 + insert_new_count: 36 + insert_reused_count: 0 +- query: EXPLAIN select str_value_indexed, max(num_value_unique) as m, num_value_2, + num_value_3_indexed from my_simple_record group by str_value_indexed, num_value_2, + num_value_3_indexed having str_value_indexed in ? order by max(num_value_unique) + desc, num_value_2 desc, num_value_3_indexed desc + ref: permuted-min-max-ordering-keys.yamsql:419 + explain: '[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: + KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, + _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED, + _.STR_VALUE_INDEXED)' + task_count: 535 + task_total_time_ms: 40 + transform_count: 214 + transform_time_ms: 29 + transform_yield_count: 55 + insert_time_ms: 1 + insert_new_count: 34 + insert_reused_count: 1 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.yamsql new file mode 100644 index 00000000000..15dd4f8abe7 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-ordering-keys.yamsql @@ -0,0 +1,456 @@ +# +# permuted-min-max-ordering-keys.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Ported from `FDBPermutedMinMaxQueryTest.testMaxUniqueByStr2And3WithDifferentOrderingKeys` — the same +# PERMUTED_MAX index as permuted-min-max-str-value.yamsql, queried with an IN list on the leading +# grouping column and four different ORDER BY prefixes. +# +# Its own file for two reasons: the hook *removes* `MySimpleRecord$num_value_unique`, because the six +# extra records it writes duplicate NUM_VALUE_UNIQUE values (980, 992 and 972 each appear twice) and +# would violate the uniqueness constraint; and those extra records are part of the data. +# +# The four queries differ only in how much of (m, num_value_2, num_value_3_indexed) they sort by, and the +# source asserts a different plan shape for each. With no ORDER BY at all it expects an **in-join**; with +# any ordering constraint it expects an **in-union**, whose comparison key is the requested ordering +# keys, then STR_VALUE_INDEXED, then whatever remains of the underlying order. That progression is the +# whole point of the test, so all four are ported even though they return the same rows. +# +# Results are unordered. The source asserts the row *set* with `containsInAnyOrder` and checks +# monotonicity of the ordering prefix separately, which yamsql has no way to express; asserting a full +# sequence here would be stronger than the source and would depend on which leg of the union drains +# first. +# +# The source's `setAttemptFailedInJoinAsUnionMaxSize(10)` is not reproduced and is not load-bearing: the +# IN list has five elements, under both that limit and the 24 the SQL layer pins. +# +# 'empty' is in the IN list and matches no record, exactly as in the source. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create index max_num_value_unique_by_str_value_2_and_3 as + select str_value_indexed, max(num_value_unique), num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + order by str_value_indexed, max(num_value_unique), num_value_2, num_value_3_indexed + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +setup: + steps: + - query: INSERT INTO MY_SIMPLE_RECORD + (REC_NO, STR_VALUE_INDEXED, NUM_VALUE_UNIQUE, NUM_VALUE_2, + NUM_VALUE_3_INDEXED, REPEATER) + VALUES + (100000, 'other1', 980, 0, 0, []), + (100001, 'other2', 980, 1, 0, []), + (100002, 'other1', 992, 2, 0, []), + (100003, 'other2', 992, 2, 1, []), + (100004, 'other1', 972, 1, 3, []), + (100005, 'other2', 972, 2, 3, []) +--- +test_block: + name: permuted-min-max-ordering-keys + preset: single_repetition_ordered + tests: + - + # testMaxUniqueByStr2And3WithDifferentOrderingKeys [literal IN list: byLiteral / byConstantObjectValue] + # testMaxUniqueByStr2And3WithDifferentOrderingKeys + # no ORDER BY at all — the source expects an in-join here, not an in-union + - query: select str_value_indexed, max(num_value_unique) as m, num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + having str_value_indexed in ('even', 'odd', 'empty', 'other1', 'other2') + - explain: "[IN arrayDistinct(@c25)] | INJOIN q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 [EQUALS q0] BY_GROUP -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS NUM_VALUE_3_INDEXED) }" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', M: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'other1', M: 980, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other1', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'other1', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 980, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'other2', M: 972, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}] + - + # testMaxUniqueByStr2And3WithDifferentOrderingKeys [literal IN list: byLiteral / byConstantObjectValue] + # testMaxUniqueByStr2And3WithDifferentOrderingKeys + # ordering by the aggregate alone + - query: select str_value_indexed, max(num_value_unique) as m, num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + having str_value_indexed in ('even', 'odd', 'empty', 'other1', 'other2') + order by max(num_value_unique) desc + - explain: "[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.STR_VALUE_INDEXED, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', M: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'other1', M: 980, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other1', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'other1', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 980, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'other2', M: 972, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}] + - + # testMaxUniqueByStr2And3WithDifferentOrderingKeys [literal IN list: byLiteral / byConstantObjectValue] + # testMaxUniqueByStr2And3WithDifferentOrderingKeys + # aggregate then the second grouping column + - query: select str_value_indexed, max(num_value_unique) as m, num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + having str_value_indexed in ('even', 'odd', 'empty', 'other1', 'other2') + order by max(num_value_unique) desc, num_value_2 desc + - explain: "[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.NUM_VALUE_2, _.STR_VALUE_INDEXED, _.NUM_VALUE_3_INDEXED)" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', M: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'other1', M: 980, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other1', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'other1', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 980, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'other2', M: 972, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}] + - + # testMaxUniqueByStr2And3WithDifferentOrderingKeys [literal IN list: byLiteral / byConstantObjectValue] + # testMaxUniqueByStr2And3WithDifferentOrderingKeys + # a total ordering — the case the source pins a plan hash against, and the one it notes exposes issue #3331 + - query: select str_value_indexed, max(num_value_unique) as m, num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + having str_value_indexed in ('even', 'odd', 'empty', 'other1', 'other2') + order by max(num_value_unique) desc, num_value_2 desc, num_value_3_indexed desc + - explain: "[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED, _.STR_VALUE_INDEXED)" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', M: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'other1', M: 980, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other1', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'other1', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 980, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'other2', M: 972, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}] +--- +test_block: + name: permuted-min-max-ordering-keys-prepared + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testMaxUniqueByStr2And3WithDifferentOrderingKeys [parameter: byParameter] + # testMaxUniqueByStr2And3WithDifferentOrderingKeys + # no ORDER BY at all — the source expects an in-join here, not an in-union + - query: select str_value_indexed, max(num_value_unique) as m, num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + having str_value_indexed in !! !in {['even', 'odd', 'empty', 'other1', 'other2']} !! + - explain: "[IN arrayDistinct(@c25)] | INJOIN q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 [EQUALS q0] BY_GROUP -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS NUM_VALUE_3_INDEXED) }" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', M: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'other1', M: 980, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other1', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'other1', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 980, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'other2', M: 972, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}] + - + # testMaxUniqueByStr2And3WithDifferentOrderingKeys [parameter: byParameter] + # testMaxUniqueByStr2And3WithDifferentOrderingKeys + # ordering by the aggregate alone + - query: select str_value_indexed, max(num_value_unique) as m, num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + having str_value_indexed in !! !in {['even', 'odd', 'empty', 'other1', 'other2']} !! + order by max(num_value_unique) desc + - explain: "[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.STR_VALUE_INDEXED, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', M: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'other1', M: 980, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other1', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'other1', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 980, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'other2', M: 972, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}] + - + # testMaxUniqueByStr2And3WithDifferentOrderingKeys [parameter: byParameter] + # testMaxUniqueByStr2And3WithDifferentOrderingKeys + # aggregate then the second grouping column + - query: select str_value_indexed, max(num_value_unique) as m, num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + having str_value_indexed in !! !in {['even', 'odd', 'empty', 'other1', 'other2']} !! + order by max(num_value_unique) desc, num_value_2 desc + - explain: "[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.NUM_VALUE_2, _.STR_VALUE_INDEXED, _.NUM_VALUE_3_INDEXED)" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', M: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'other1', M: 980, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other1', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'other1', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 980, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'other2', M: 972, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}] + - + # testMaxUniqueByStr2And3WithDifferentOrderingKeys [parameter: byParameter] + # testMaxUniqueByStr2And3WithDifferentOrderingKeys + # a total ordering — the case the source pins a plan hash against, and the one it notes exposes issue #3331 + - query: select str_value_indexed, max(num_value_unique) as m, num_value_2, num_value_3_indexed + from my_simple_record + group by str_value_indexed, num_value_2, num_value_3_indexed + having str_value_indexed in !! !in {['even', 'odd', 'empty', 'other1', 'other2']} !! + order by max(num_value_unique) desc, num_value_2 desc, num_value_3_indexed desc + - explain: "[IN arrayDistinct(@c25)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_STR_VALUE_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[3], _3: KEY:[1]]) | MAP (_._0 AS STR_VALUE_INDEXED, _._3 AS M, _._1 AS NUM_VALUE_2, _._2 AS NUM_VALUE_3_INDEXED) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED, _.STR_VALUE_INDEXED)" + - unorderedResult: [{STR_VALUE_INDEXED: 'even', M: 1000, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 994, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 988, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 982, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 976, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 990, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 984, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 978, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 996, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'even', M: 980, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'even', M: 974, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'even', M: 998, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'even', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'even', M: 986, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 985, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 979, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 973, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 997, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 991, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 975, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 999, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 993, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 987, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 981, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'odd', M: 995, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'odd', M: 989, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'odd', M: 983, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2}, + {STR_VALUE_INDEXED: 'odd', M: 977, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'odd', M: 971, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4}, + {STR_VALUE_INDEXED: 'other1', M: 980, NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other1', M: 972, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3}, + {STR_VALUE_INDEXED: 'other1', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 980, NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0}, + {STR_VALUE_INDEXED: 'other2', M: 992, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1}, + {STR_VALUE_INDEXED: 'other2', M: 972, NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.metrics.binpb new file mode 100644 index 00000000000..c5db8fdd03e Binary files /dev/null and b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.metrics.binpb differ diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.metrics.yaml new file mode 100644 index 00000000000..46e682676de --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.metrics.yaml @@ -0,0 +1,70 @@ +permuted-min-max-repeater: +- query: EXPLAIN select sq.x, r.num_value_2, max(r.num_value_3_indexed) as m from + my_simple_record as r, (select x from r.repeater as x) sq where r.str_value_indexed + = 'even' group by r.str_value_indexed, sq.x, r.num_value_2 having sq.x in + (0, 2) order by max(r.num_value_3_indexed) desc + ref: permuted-min-max-repeater.yamsql:182 + explain: '[IN arrayDistinct(@c57)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_3_BY_STR_VALUE_REPEATER_AND_2 + [EQUALS promote(@c38 AS STRING), EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], + _1: KEY:[1], _2: KEY:[3], _3: KEY:[2]]) | MAP (_._1 AS X, _._2 AS NUM_VALUE_2, + _._3 AS M) } COMPARE BY (_.M, _.X, _.NUM_VALUE_2)' + task_count: 678 + task_total_time_ms: 53 + transform_count: 273 + transform_time_ms: 29 + transform_yield_count: 71 + insert_time_ms: 1 + insert_new_count: 54 + insert_reused_count: 0 +- query: EXPLAIN select sq.x, r.num_value_2, max(r.num_value_3_indexed) as m from + my_simple_record as r, (select x from r.repeater as x) sq where r.str_value_indexed + = 'even' group by r.str_value_indexed, sq.x, r.num_value_2 having sq.x in + (0, 0) order by max(r.num_value_3_indexed) desc + ref: permuted-min-max-repeater.yamsql:195 + explain: '[IN arrayDistinct(@c57)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_3_BY_STR_VALUE_REPEATER_AND_2 + [EQUALS promote(@c38 AS STRING), EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], + _1: KEY:[1], _2: KEY:[3], _3: KEY:[2]]) | MAP (_._1 AS X, _._2 AS NUM_VALUE_2, + _._3 AS M) } COMPARE BY (_.M, _.X, _.NUM_VALUE_2)' + task_count: 678 + task_total_time_ms: 53 + transform_count: 273 + transform_time_ms: 29 + transform_yield_count: 71 + insert_time_ms: 1 + insert_new_count: 54 + insert_reused_count: 0 +permuted-min-max-repeater-prepared: +- query: EXPLAIN select sq.x, r.num_value_2, max(r.num_value_3_indexed) as m from + my_simple_record as r, (select x from r.repeater as x) sq where r.str_value_indexed + = 'even' group by r.str_value_indexed, sq.x, r.num_value_2 having sq.x in + ? order by max(r.num_value_3_indexed) desc + ref: permuted-min-max-repeater.yamsql:214 + explain: '[IN arrayDistinct(@c57)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_3_BY_STR_VALUE_REPEATER_AND_2 + [EQUALS promote(@c38 AS STRING), EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], + _1: KEY:[1], _2: KEY:[3], _3: KEY:[2]]) | MAP (_._1 AS X, _._2 AS NUM_VALUE_2, + _._3 AS M) } COMPARE BY (_.M, _.X, _.NUM_VALUE_2)' + task_count: 678 + task_total_time_ms: 47 + transform_count: 273 + transform_time_ms: 31 + transform_yield_count: 71 + insert_time_ms: 1 + insert_new_count: 54 + insert_reused_count: 0 +- query: EXPLAIN select sq.x, r.num_value_2, max(r.num_value_3_indexed) as m from + my_simple_record as r, (select x from r.repeater as x) sq where r.str_value_indexed + = 'even' group by r.str_value_indexed, sq.x, r.num_value_2 having sq.x in + ? order by max(r.num_value_3_indexed) desc + ref: permuted-min-max-repeater.yamsql:227 + explain: '[IN arrayDistinct(@c57)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_3_BY_STR_VALUE_REPEATER_AND_2 + [EQUALS promote(@c38 AS STRING), EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], + _1: KEY:[1], _2: KEY:[3], _3: KEY:[2]]) | MAP (_._1 AS X, _._2 AS NUM_VALUE_2, + _._3 AS M) } COMPARE BY (_.M, _.X, _.NUM_VALUE_2)' + task_count: 678 + task_total_time_ms: 47 + transform_count: 273 + transform_time_ms: 31 + transform_yield_count: 71 + insert_time_ms: 1 + insert_new_count: 54 + insert_reused_count: 0 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.yamsql new file mode 100644 index 00000000000..c354991d7c2 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max-repeater.yamsql @@ -0,0 +1,229 @@ +# +# permuted-min-max-repeater.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Ported from `FDBPermutedMinMaxQueryTest.testSortedMaxWithInOnRepeater` — a PERMUTED_MAX index whose grouping key contains a fan-out over the +# repeated REPEATER column, so the aggregate is per (str_value_indexed, repeater element, num_value_2). +# +# Its own file, and its own data: this is the only method in the class that call +# `setUpWithRepeaters` rather than `complexQuerySetup`. That writes 100 records with REC_NO = i + 100, +# NUM_VALUE_3_INDEXED = i / 2, and REPEATER holding the *set bit positions* of i — so a record belongs to +# one group per bit set in its index. +# +# The lateral inside an aggregate index's GROUP BY is the `aggregate-index-tests.yamsql:50` idiom +# (`select a, ek.k, b, max(d) from t6, (select k from t6.c) as ek group by a, ek.k, b`). Three grouping +# values with the aggregate at position 2 gives `PERMUTED_SIZE_OPTION = 1`, matching the source's +# `max3ByStrValueRepeaterAnd2`. +# +# The source's `setAttemptFailedInJoinAsUnionMaxSize(20)` is not reproduced and does not need to be: the +# IN lists here have two elements, well under the 24 the SQL layer pins. +# +# The source loops str_value_indexed over {even, odd} and the repeater value over 0..8, i.e. 18 +# combinations for the equality case and 81 pairs for the IN case; only the IN case is ported here. A representative few are ported; what +# they exercise is the fan-out in the grouping key and the descending sort on the aggregate, not the +# arithmetic of every bit position. +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index max_num_value_3_by_str_value_repeater_and_2 as + select r.str_value_indexed, sq.x, max(r.num_value_3_indexed), r.num_value_2 + from my_simple_record as r, (select x from r.repeater as x) sq + group by r.str_value_indexed, sq.x, r.num_value_2 + order by r.str_value_indexed, sq.x, max(r.num_value_3_indexed), r.num_value_2 + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +setup: + steps: + - query: INSERT INTO MY_SIMPLE_RECORD + (REC_NO, STR_VALUE_INDEXED, NUM_VALUE_UNIQUE, NUM_VALUE_2, + NUM_VALUE_3_INDEXED, REPEATER) + VALUES + (100, 'even', 1000, 0, 0, []), + (101, 'odd', 999, 1, 0, [0]), + (102, 'even', 998, 2, 1, [1]), + (103, 'odd', 997, 0, 1, [0, 1]), + (104, 'even', 996, 1, 2, [2]), + (105, 'odd', 995, 2, 2, [0, 2]), + (106, 'even', 994, 0, 3, [1, 2]), + (107, 'odd', 993, 1, 3, [0, 1, 2]), + (108, 'even', 992, 2, 4, [3]), + (109, 'odd', 991, 0, 4, [0, 3]), + (110, 'even', 990, 1, 5, [1, 3]), + (111, 'odd', 989, 2, 5, [0, 1, 3]), + (112, 'even', 988, 0, 6, [2, 3]), + (113, 'odd', 987, 1, 6, [0, 2, 3]), + (114, 'even', 986, 2, 7, [1, 2, 3]), + (115, 'odd', 985, 0, 7, [0, 1, 2, 3]), + (116, 'even', 984, 1, 8, [4]), + (117, 'odd', 983, 2, 8, [0, 4]), + (118, 'even', 982, 0, 9, [1, 4]), + (119, 'odd', 981, 1, 9, [0, 1, 4]), + (120, 'even', 980, 2, 10, [2, 4]), + (121, 'odd', 979, 0, 10, [0, 2, 4]), + (122, 'even', 978, 1, 11, [1, 2, 4]), + (123, 'odd', 977, 2, 11, [0, 1, 2, 4]), + (124, 'even', 976, 0, 12, [3, 4]), + (125, 'odd', 975, 1, 12, [0, 3, 4]), + (126, 'even', 974, 2, 13, [1, 3, 4]), + (127, 'odd', 973, 0, 13, [0, 1, 3, 4]), + (128, 'even', 972, 1, 14, [2, 3, 4]), + (129, 'odd', 971, 2, 14, [0, 2, 3, 4]), + (130, 'even', 970, 0, 15, [1, 2, 3, 4]), + (131, 'odd', 969, 1, 15, [0, 1, 2, 3, 4]), + (132, 'even', 968, 2, 16, [5]), + (133, 'odd', 967, 0, 16, [0, 5]), + (134, 'even', 966, 1, 17, [1, 5]), + (135, 'odd', 965, 2, 17, [0, 1, 5]), + (136, 'even', 964, 0, 18, [2, 5]), + (137, 'odd', 963, 1, 18, [0, 2, 5]), + (138, 'even', 962, 2, 19, [1, 2, 5]), + (139, 'odd', 961, 0, 19, [0, 1, 2, 5]), + (140, 'even', 960, 1, 20, [3, 5]), + (141, 'odd', 959, 2, 20, [0, 3, 5]), + (142, 'even', 958, 0, 21, [1, 3, 5]), + (143, 'odd', 957, 1, 21, [0, 1, 3, 5]), + (144, 'even', 956, 2, 22, [2, 3, 5]), + (145, 'odd', 955, 0, 22, [0, 2, 3, 5]), + (146, 'even', 954, 1, 23, [1, 2, 3, 5]), + (147, 'odd', 953, 2, 23, [0, 1, 2, 3, 5]), + (148, 'even', 952, 0, 24, [4, 5]), + (149, 'odd', 951, 1, 24, [0, 4, 5]), + (150, 'even', 950, 2, 25, [1, 4, 5]), + (151, 'odd', 949, 0, 25, [0, 1, 4, 5]), + (152, 'even', 948, 1, 26, [2, 4, 5]), + (153, 'odd', 947, 2, 26, [0, 2, 4, 5]), + (154, 'even', 946, 0, 27, [1, 2, 4, 5]), + (155, 'odd', 945, 1, 27, [0, 1, 2, 4, 5]), + (156, 'even', 944, 2, 28, [3, 4, 5]), + (157, 'odd', 943, 0, 28, [0, 3, 4, 5]), + (158, 'even', 942, 1, 29, [1, 3, 4, 5]), + (159, 'odd', 941, 2, 29, [0, 1, 3, 4, 5]), + (160, 'even', 940, 0, 30, [2, 3, 4, 5]), + (161, 'odd', 939, 1, 30, [0, 2, 3, 4, 5]), + (162, 'even', 938, 2, 31, [1, 2, 3, 4, 5]), + (163, 'odd', 937, 0, 31, [0, 1, 2, 3, 4, 5]), + (164, 'even', 936, 1, 32, [6]), + (165, 'odd', 935, 2, 32, [0, 6]), + (166, 'even', 934, 0, 33, [1, 6]), + (167, 'odd', 933, 1, 33, [0, 1, 6]), + (168, 'even', 932, 2, 34, [2, 6]), + (169, 'odd', 931, 0, 34, [0, 2, 6]), + (170, 'even', 930, 1, 35, [1, 2, 6]), + (171, 'odd', 929, 2, 35, [0, 1, 2, 6]), + (172, 'even', 928, 0, 36, [3, 6]), + (173, 'odd', 927, 1, 36, [0, 3, 6]), + (174, 'even', 926, 2, 37, [1, 3, 6]), + (175, 'odd', 925, 0, 37, [0, 1, 3, 6]), + (176, 'even', 924, 1, 38, [2, 3, 6]), + (177, 'odd', 923, 2, 38, [0, 2, 3, 6]), + (178, 'even', 922, 0, 39, [1, 2, 3, 6]), + (179, 'odd', 921, 1, 39, [0, 1, 2, 3, 6]), + (180, 'even', 920, 2, 40, [4, 6]), + (181, 'odd', 919, 0, 40, [0, 4, 6]), + (182, 'even', 918, 1, 41, [1, 4, 6]), + (183, 'odd', 917, 2, 41, [0, 1, 4, 6]), + (184, 'even', 916, 0, 42, [2, 4, 6]), + (185, 'odd', 915, 1, 42, [0, 2, 4, 6]), + (186, 'even', 914, 2, 43, [1, 2, 4, 6]), + (187, 'odd', 913, 0, 43, [0, 1, 2, 4, 6]), + (188, 'even', 912, 1, 44, [3, 4, 6]), + (189, 'odd', 911, 2, 44, [0, 3, 4, 6]), + (190, 'even', 910, 0, 45, [1, 3, 4, 6]), + (191, 'odd', 909, 1, 45, [0, 1, 3, 4, 6]), + (192, 'even', 908, 2, 46, [2, 3, 4, 6]), + (193, 'odd', 907, 0, 46, [0, 2, 3, 4, 6]), + (194, 'even', 906, 1, 47, [1, 2, 3, 4, 6]), + (195, 'odd', 905, 2, 47, [0, 1, 2, 3, 4, 6]), + (196, 'even', 904, 0, 48, [5, 6]), + (197, 'odd', 903, 1, 48, [0, 5, 6]), + (198, 'even', 902, 2, 49, [1, 5, 6]), + (199, 'odd', 901, 0, 49, [0, 1, 5, 6]) +--- +test_block: + name: permuted-min-max-repeater + preset: single_repetition_ordered + tests: + - + # testSortedMaxWithInOnRepeater [literal IN list: byLiteral / byConstantObjectValue] + # testSortedMaxWithInOnRepeater — an in-union over the repeater values, which are now + # projected so the groups can be told apart. + - query: select sq.x, r.num_value_2, max(r.num_value_3_indexed) as m + from my_simple_record as r, (select x from r.repeater as x) sq + where r.str_value_indexed = 'even' + group by r.str_value_indexed, sq.x, r.num_value_2 + having sq.x in (0, 2) + order by max(r.num_value_3_indexed) desc + - explain: "[IN arrayDistinct(@c57)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_3_BY_STR_VALUE_REPEATER_AND_2 [EQUALS promote(@c38 AS STRING), EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[1], _2: KEY:[3], _3: KEY:[2]]) | MAP (_._1 AS X, _._2 AS NUM_VALUE_2, _._3 AS M) } COMPARE BY (_.M, _.X, _.NUM_VALUE_2)" + - unorderedResult: [{X: 2, NUM_VALUE_2: 1, M: 47}, + {X: 2, NUM_VALUE_2: 2, M: 46}, + {X: 2, NUM_VALUE_2: 0, M: 42}] + - + # testSortedMaxWithInOnRepeater [literal IN list: byLiteral / byConstantObjectValue] + # The same with a repeated IN value; the union must not emit a group twice. + - query: select sq.x, r.num_value_2, max(r.num_value_3_indexed) as m + from my_simple_record as r, (select x from r.repeater as x) sq + where r.str_value_indexed = 'even' + group by r.str_value_indexed, sq.x, r.num_value_2 + having sq.x in (0, 0) + order by max(r.num_value_3_indexed) desc + - explain: "[IN arrayDistinct(@c57)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_3_BY_STR_VALUE_REPEATER_AND_2 [EQUALS promote(@c38 AS STRING), EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[1], _2: KEY:[3], _3: KEY:[2]]) | MAP (_._1 AS X, _._2 AS NUM_VALUE_2, _._3 AS M) } COMPARE BY (_.M, _.X, _.NUM_VALUE_2)" + - unorderedResult: [] +--- +test_block: + name: permuted-min-max-repeater-prepared + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testSortedMaxWithInOnRepeater [parameter: byParameter] + # testSortedMaxWithInOnRepeater — an in-union over the repeater values, which are now + # projected so the groups can be told apart. + - query: select sq.x, r.num_value_2, max(r.num_value_3_indexed) as m + from my_simple_record as r, (select x from r.repeater as x) sq + where r.str_value_indexed = 'even' + group by r.str_value_indexed, sq.x, r.num_value_2 + having sq.x in !! !in {[0, 2]} !! + order by max(r.num_value_3_indexed) desc + - explain: "[IN arrayDistinct(@c57)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_3_BY_STR_VALUE_REPEATER_AND_2 [EQUALS promote(@c38 AS STRING), EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[1], _2: KEY:[3], _3: KEY:[2]]) | MAP (_._1 AS X, _._2 AS NUM_VALUE_2, _._3 AS M) } COMPARE BY (_.M, _.X, _.NUM_VALUE_2)" + - unorderedResult: [{X: 2, NUM_VALUE_2: 1, M: 47}, + {X: 2, NUM_VALUE_2: 2, M: 46}, + {X: 2, NUM_VALUE_2: 0, M: 42}] + - + # testSortedMaxWithInOnRepeater [parameter: byParameter] + # The same with a repeated IN value; the union must not emit a group twice. + - query: select sq.x, r.num_value_2, max(r.num_value_3_indexed) as m + from my_simple_record as r, (select x from r.repeater as x) sq + where r.str_value_indexed = 'even' + group by r.str_value_indexed, sq.x, r.num_value_2 + having sq.x in !! !in {[0, 0]} !! + order by max(r.num_value_3_indexed) desc + - explain: "[IN arrayDistinct(@c57)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_3_BY_STR_VALUE_REPEATER_AND_2 [EQUALS promote(@c38 AS STRING), EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[1], _2: KEY:[3], _3: KEY:[2]]) | MAP (_._1 AS X, _._2 AS NUM_VALUE_2, _._3 AS M) } COMPARE BY (_.M, _.X, _.NUM_VALUE_2)" + - unorderedResult: [] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.metrics.binpb new file mode 100644 index 00000000000..2f29511b1e6 Binary files /dev/null and b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.metrics.binpb differ diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.metrics.yaml new file mode 100644 index 00000000000..0dd95d2c662 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.metrics.yaml @@ -0,0 +1,98 @@ +permuted-min-max: +- query: EXPLAIN select num_value_2, num_value_3_indexed, max(num_value_unique) + as m from my_simple_record group by num_value_2, num_value_3_indexed having + num_value_2 in (0, 2) order by max(num_value_unique) desc + ref: permuted-min-max.yamsql:75 + explain: '[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | + MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE + BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)' + task_count: 556 + task_total_time_ms: 33 + transform_count: 221 + transform_time_ms: 22 + transform_yield_count: 63 + insert_time_ms: 1 + insert_new_count: 36 + insert_reused_count: 0 +- query: EXPLAIN select num_value_2, num_value_3_indexed, max(num_value_unique) + as m from my_simple_record group by num_value_2, num_value_3_indexed having + num_value_2 in (1, 1) order by max(num_value_unique) desc + ref: permuted-min-max.yamsql:95 + explain: '[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | + MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE + BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)' + task_count: 556 + task_total_time_ms: 33 + transform_count: 221 + transform_time_ms: 22 + transform_yield_count: 63 + insert_time_ms: 1 + insert_new_count: 36 + insert_reused_count: 0 +- query: EXPLAIN select num_value_2, num_value_3_indexed, max(num_value_unique) + as m from my_simple_record group by num_value_2, num_value_3_indexed having + num_value_2 in (-1, -1) order by max(num_value_unique) desc + ref: permuted-min-max.yamsql:109 + explain: '[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | + MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE + BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)' + task_count: 556 + task_total_time_ms: 33 + transform_count: 221 + transform_time_ms: 22 + transform_yield_count: 63 + insert_time_ms: 1 + insert_new_count: 36 + insert_reused_count: 0 +permuted-min-max-prepared: +- query: EXPLAIN select num_value_2, num_value_3_indexed, max(num_value_unique) + as m from my_simple_record group by num_value_2, num_value_3_indexed having + num_value_2 in ? order by max(num_value_unique) desc + ref: permuted-min-max.yamsql:128 + explain: '[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | + MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE + BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)' + task_count: 556 + task_total_time_ms: 38 + transform_count: 221 + transform_time_ms: 27 + transform_yield_count: 63 + insert_time_ms: 1 + insert_new_count: 36 + insert_reused_count: 0 +- query: EXPLAIN select num_value_2, num_value_3_indexed, max(num_value_unique) + as m from my_simple_record group by num_value_2, num_value_3_indexed having + num_value_2 in ? order by max(num_value_unique) desc + ref: permuted-min-max.yamsql:148 + explain: '[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | + MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE + BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)' + task_count: 556 + task_total_time_ms: 38 + transform_count: 221 + transform_time_ms: 27 + transform_yield_count: 63 + insert_time_ms: 1 + insert_new_count: 36 + insert_reused_count: 0 +- query: EXPLAIN select num_value_2, num_value_3_indexed, max(num_value_unique) + as m from my_simple_record group by num_value_2, num_value_3_indexed having + num_value_2 in ? order by max(num_value_unique) desc + ref: permuted-min-max.yamsql:162 + explain: '[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 + [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | + MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE + BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)' + task_count: 556 + task_total_time_ms: 38 + transform_count: 221 + transform_time_ms: 27 + transform_yield_count: 63 + insert_time_ms: 1 + insert_new_count: 36 + insert_reused_count: 0 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.yamsql new file mode 100644 index 00000000000..5093175db46 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/permuted-min-max.yamsql @@ -0,0 +1,164 @@ +# +# permuted-min-max.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Ported from `FDBPermutedMinMaxQueryTest.selectMaxWithInOrderByMax`, which uses `maxUniqueBy2And3` — a +# PERMUTED_MAX index over max(num_value_unique) grouped by (num_value_2, num_value_3_indexed) with +# `PERMUTED_SIZE_OPTION = 1`. +# +# The permuted size is not written out in SQL; it is *derived* from where the aggregate sits in the +# index's ORDER BY — `permutedSize = fieldValues.size() - aggregateOrderIndex` +# (`MaterializedViewIndexGenerator:238-240`). Two grouping columns with the aggregate at position 1 +# therefore gives 1, which is what puts NUM_VALUE_3_INDEXED after the max in the index key and makes +# `ORDER BY max(num_value_unique)` free. The idiom is the one `aggregate-index-tests.yamsql:36` uses. +# +# Index set: the three proto-declared indexes plus this one. `complexQuerySetup(hook)` does *not* apply +# `complexQuerySetupHook()`, so there is no MULTI_INDEX and no repeater fan-out index here. +# +# The source's `InComparisonCase` parameterization has three cases. `byLiteral` and `byConstantObjectValue` +# are the same query in SQL, since `AstNormalizer` lifts every literal into a `ConstantObjectValue`, so the +# first block is a literal IN list. `byParameter` is the second block, with the IN list parameter-injected +# and `statement_type: prepared`. +# +--- +schema_template: + create table my_simple_record( + rec_no bigint, + str_value_indexed string, + num_value_unique integer, + num_value_2 integer, + num_value_3_indexed integer, + repeater integer array, + primary key(rec_no)) + create index my_simple_record_str_value_indexed as select str_value_indexed from my_simple_record + create index my_simple_record_num_value_3_indexed as select num_value_3_indexed from my_simple_record + create unique index my_simple_record_num_value_unique as select num_value_unique from my_simple_record + create index max_num_value_unique_by_2_and_3 as + select num_value_2, max(num_value_unique), num_value_3_indexed from my_simple_record + group by num_value_2, num_value_3_indexed + order by num_value_2, max(num_value_unique), num_value_3_indexed + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +include: + cascades-query-tests/includes/simple-record-100.yamsql +--- +test_block: + name: permuted-min-max + preset: single_repetition_ordered + tests: + - + # selectMaxWithInOrderByMax [literal IN list: byLiteral / byConstantObjectValue] + # selectMaxWithInOrderByMax — an in-union over the aggregate index, comparison key + # (m, num_value_2, num_value_3_indexed). The source runs every pair (i, j) with i, j in + # -1..3; two representative pairs are used here. + - query: select num_value_2, num_value_3_indexed, max(num_value_unique) as m + from my_simple_record + group by num_value_2, num_value_3_indexed + having num_value_2 in (0, 2) + order by max(num_value_unique) desc + - explain: "[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)" + - result: [{NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, M: 1000}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, M: 998}, + {NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, M: 997}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, M: 995}, + {NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, M: 994}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, M: 992}, + {NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, M: 991}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, M: 989}, + {NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, M: 988}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, M: 986}] + - + # selectMaxWithInOrderByMax [literal IN list: byLiteral / byConstantObjectValue] + # The same with a repeated IN value. The source deduplicates its expectation, so the union + # must not emit a group twice. + - query: select num_value_2, num_value_3_indexed, max(num_value_unique) as m + from my_simple_record + group by num_value_2, num_value_3_indexed + having num_value_2 in (1, 1) + order by max(num_value_unique) desc + - explain: "[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)" + - result: [{NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, M: 999}, + {NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, M: 996}, + {NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, M: 993}, + {NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, M: 990}, + {NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, M: 987}] + - + # selectMaxWithInOrderByMax [literal IN list: byLiteral / byConstantObjectValue] + # An IN list that matches nothing. + - query: select num_value_2, num_value_3_indexed, max(num_value_unique) as m + from my_simple_record + group by num_value_2, num_value_3_indexed + having num_value_2 in (-1, -1) + order by max(num_value_unique) desc + - explain: "[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)" + - result: [] +--- +test_block: + name: permuted-min-max-prepared + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # selectMaxWithInOrderByMax [parameter: byParameter] + # selectMaxWithInOrderByMax — an in-union over the aggregate index, comparison key + # (m, num_value_2, num_value_3_indexed). The source runs every pair (i, j) with i, j in + # -1..3; two representative pairs are used here. + - query: select num_value_2, num_value_3_indexed, max(num_value_unique) as m + from my_simple_record + group by num_value_2, num_value_3_indexed + having num_value_2 in !! !in {[0, 2]} !! + order by max(num_value_unique) desc + - explain: "[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)" + - result: [{NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 0, M: 1000}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 2, M: 998}, + {NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 3, M: 997}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 0, M: 995}, + {NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 1, M: 994}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 3, M: 992}, + {NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 4, M: 991}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 1, M: 989}, + {NUM_VALUE_2: 0, NUM_VALUE_3_INDEXED: 2, M: 988}, + {NUM_VALUE_2: 2, NUM_VALUE_3_INDEXED: 4, M: 986}] + - + # selectMaxWithInOrderByMax [parameter: byParameter] + # The same with a repeated IN value. The source deduplicates its expectation, so the union + # must not emit a group twice. + - query: select num_value_2, num_value_3_indexed, max(num_value_unique) as m + from my_simple_record + group by num_value_2, num_value_3_indexed + having num_value_2 in !! !in {[1, 1]} !! + order by max(num_value_unique) desc + - explain: "[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)" + - result: [{NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 1, M: 999}, + {NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 4, M: 996}, + {NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 2, M: 993}, + {NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 0, M: 990}, + {NUM_VALUE_2: 1, NUM_VALUE_3_INDEXED: 3, M: 987}] + - + # selectMaxWithInOrderByMax [parameter: byParameter] + # An IN list that matches nothing. + - query: select num_value_2, num_value_3_indexed, max(num_value_unique) as m + from my_simple_record + group by num_value_2, num_value_3_indexed + having num_value_2 in !! !in {[-1, -1]} !! + order by max(num_value_unique) desc + - explain: "[IN arrayDistinct(@c21)] INUNION q0 -> { AISCAN(MAX_NUM_VALUE_UNIQUE_BY_2_AND_3 [EQUALS q0] BY_GROUP REVERSE -> [_0: KEY:[0], _1: KEY:[2], _2: KEY:[1]]) | MAP (_._0 AS NUM_VALUE_2, _._1 AS NUM_VALUE_3_INDEXED, _._2 AS M) } COMPARE BY (_.M, _.NUM_VALUE_2, _.NUM_VALUE_3_INDEXED)" + - result: [] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.metrics.binpb new file mode 100644 index 00000000000..5e95536b858 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.metrics.binpb @@ -0,0 +1,57 @@ +–< +¦ +!simple-query-graph-name-tag-index€EXPLAIN select r.rest_no from restaurant_record as r where r.name in ? and exists (select 1 from r.tags as t where t.value in ?)ê: +©Ò³•!² ý®ê(ˆ0 ‰ã8¬@æ[IN arrayDistinct(@c13)] | INJOIN q0 -> { COVERING(RESTAURANT_RECORD_NAME [EQUALS q0] -> [NAME: KEY:[0], REST_NO: KEY:[1]]) } | FETCH | FLATMAP q1 -> { EXPLODE arrayDistinct(@c30) | FLATMAP q2 -> { EXPLODE q1.TAGS | FILTER _.VALUE EQUALS q2 AS q3 RETURN (@c18 AS _0) } | DEFAULT NULL | FILTER _ NOT_NULL | MAP (1 AS _0) AS q4 RETURN (q1.REST_NO AS REST_NO) }ß7digraph G { + fontname=courier; + rankdir=BT; + splines=line; + 1 [ label=<
Nested Loop Join
FLATMAP (q2.REST_NO AS REST_NO)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REST_NO)" ]; + 2 [ label=<
Fetch Records
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REST_NO, STRING AS NAME, ARRAY(LONG AS REVIEWER, INT AS RATING) AS REVIEWS, ARRAY(STRING AS VALUE, INT AS WEIGHT) AS TAGS, ARRAY(STRING) AS CUSTOMER)" ]; + 3 [ label=<
Nested Loop Join
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="12" tooltip="RELATION(LONG AS REST_NO, STRING AS NAME, ARRAY(LONG AS REVIEWER, INT AS RATING) AS REVIEWS, ARRAY(STRING AS VALUE, INT AS WEIGHT) AS TAGS, ARRAY(STRING) AS CUSTOMER)" ]; + 4 [ label=<
Table Function
EXPLODE(arrayDistinct(@c13))
> color="black" shape="plain" style="filled" fillcolor="darkseagreen2" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REST_NO, STRING AS NAME, ARRAY(LONG AS REVIEWER, INT AS RATING) AS REVIEWS, ARRAY(STRING AS VALUE, INT AS WEIGHT) AS TAGS, ARRAY(STRING) AS CUSTOMER)" ]; + 5 [ label=<
Covering Index Scan
comparisons: [EQUALS q137]
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REST_NO, STRING AS NAME, ARRAY(LONG AS REVIEWER, INT AS RATING) AS REVIEWS, ARRAY(STRING AS VALUE, INT AS WEIGHT) AS TAGS, ARRAY(STRING) AS CUSTOMER)" ]; + 6 [ label=<
Index
RESTAURANT_RECORD_NAME
> color="black" shape="plain" style="filled" fillcolor="lightblue" fontname="courier" fontsize="8" tooltip="RELATION(LONG AS REST_NO, STRING AS NAME, ARRAY(LONG AS REVIEWER, INT AS RATING) AS REVIEWS, ARRAY(STRING AS VALUE, INT AS WEIGHT) AS TAGS, ARRAY(STRING) AS CUSTOMER)" ]; + 7 [ label=<
Value Computation
MAP (1 AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS _0)" ]; + 8 [ label=<
Predicate Filter
WHERE q8 NOT_NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS _0)" ]; + 9 [ label=<
Value Computation
FIRST $q8 OR NULL
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS _0)" ]; + 10 [ label=<
Nested Loop Join
FLATMAP (@c18 AS _0)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(INT AS _0)" ]; + 11 [ label=<
Value Computation
EXPLODE arrayDistinct(@c30)
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(STRING)" ]; + 12 [ label=<
Predicate Filter
WHERE q4.VALUE EQUALS q84
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(STRING AS VALUE, INT AS WEIGHT)" ]; + 13 [ label=<
Value Computation
EXPLODE q2.TAGS
> color="black" shape="plain" style="solid" fillcolor="black" fontname="courier" fontsize="8" tooltip="RELATION(STRING AS VALUE, INT AS WEIGHT)" ]; + 3 -> 2 [ label=< q178> label="q178" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 4 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 5 -> 3 [ color="black" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 6 -> 5 [ color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 2 -> 1 [ label=< q2> label="q2" color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 8 -> 7 [ label=< q121> label="q121" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 9 -> 8 [ label=< q8> label="q8" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 10 -> 9 [ label=< q8> label="q8" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 11 -> 10 [ label=< q84> label="q84" color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 12 -> 10 [ label=< q4> label="q4" color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 13 -> 12 [ label=< q4> label="q4" color="gray20" style="bold" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + 7 -> 1 [ label=< q8> label="q8" color="gray20" style="solid" fontname="courier" fontsize="8" arrowhead="normal" arrowtail="none" dir="both" ]; + { + 12 -> 10 [ color="blue" style="dotted" arrowhead="none" tailport="nw" headport="s" constraint="false" ]; + } + { + rank=same; + rankDir=LR; + 11 -> 12 [ color="red" style="invis" ]; + } + { + rank=same; + rankDir=LR; + 4 -> 5 [ color="red" style="invis" ]; + } + { + 13 -> 1 [ color="blue" style="dotted" arrowhead="none" tailport="nw" headport="s" constraint="false" ]; + } + { + 8 -> 1 [ color="blue" style="dotted" arrowhead="none" tailport="nw" headport="s" constraint="false" ]; + } + { + rank=same; + rankDir=LR; + 2 -> 7 [ color="red" style="invis" ]; + } +} \ No newline at end of file diff --git a/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.metrics.yaml new file mode 100644 index 00000000000..8e6c272f5dc --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.metrics.yaml @@ -0,0 +1,17 @@ +simple-query-graph-name-tag-index: +- query: EXPLAIN select r.rest_no from restaurant_record as r where r.name in ? + and exists (select 1 from r.tags as t where t.value in ?) + ref: simple-query-graph-name-tag-index.yamsql:111 + explain: '[IN arrayDistinct(@c13)] | INJOIN q0 -> { COVERING(RESTAURANT_RECORD_NAME + [EQUALS q0] -> [NAME: KEY:[0], REST_NO: KEY:[1]]) } | FETCH | FLATMAP q1 -> + { EXPLODE arrayDistinct(@c30) | FLATMAP q2 -> { EXPLODE q1.TAGS | FILTER _.VALUE + EQUALS q2 AS q3 RETURN (@c18 AS _0) } | DEFAULT NULL | FILTER _ NOT_NULL | + MAP (1 AS _0) AS q4 RETURN (q1.REST_NO AS REST_NO) }' + task_count: 2217 + task_total_time_ms: 69 + transform_count: 690 + transform_time_ms: 33 + transform_yield_count: 136 + insert_time_ms: 5 + insert_new_count: 300 + insert_reused_count: 20 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.yamsql new file mode 100644 index 00000000000..0a494d2da5a --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-name-tag-index.yamsql @@ -0,0 +1,113 @@ +# +# simple-query-graph-name-tag-index.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Ported from `FDBSimpleQueryGraphTest.testEqualityAndSimpleExistentialPredicate` — an `IN` on a +# non-repeated column combined with an existential predicate over the tags array, both served by one +# index whose key is concat(name, tags.value). +# +# Its own file: the hook adds `Restaurant$name-tagValue`. The source asserts a flat-map whose outer side +# is a fetch over a covering scan of `RestaurantRecord$name` bounded by the IN, with the existential +# evaluated per row — so the combined index is offered but the name index wins. + +# Schema: `openNestedRecordStore` — *both* TestRecords4 record types in one store, with bare primary keys +# and no record-type prefix. `WITH OPTIONS(INTERMINGLE_TABLES=true)` reproduces that, and here it is not +# just safe but load-bearing: it is why the source's plans carry a `typeFilterPlan` over the scan. The +# primary-key values do not collide (reviewers 1-2, restaurants 1000-1001). +# +# Data deviations, both invisible to every query: the apostrophe is dropped from the tag "l'atmosphère" +# and the accents from "Chez Thénardier" and "éponine". A SQL literal spells an apostrophe as the doubled +# quote `''`, and decoding that only landed in 4.13.5.0 (b9790b9af), so an older server in a mixed-mode +# configuration would read the value back with the escape still in it. +# +# One thing SQL cannot vary: `setUpWithNullableArray` builds the same records over +# `test_records_4_wrapper.proto`, where each repeated field is wrapped in a `…List` message with a +# `values` field. That *is* how the Relational layer models an `ARRAY` — `NullableArrayUtils.wrapArray` is +# applied to every generated index key — so the wrapped and unwrapped variants are one and the same DDL +# here, and `testSimplePlanGraphWithNullableArray` collapses into `testSimplePlanGraph`. +--- +schema_template: + create type as struct restaurant_review(reviewer bigint, rating integer) + create type as struct restaurant_tag(value string, weight integer) + create type as struct reviewer_stats(start_date bigint, school_name string, hometown string) + create table restaurant_record( + rest_no bigint, + name string, + reviews restaurant_review array, + tags restaurant_tag array, + customer string array, + primary key(rest_no)) + create table restaurant_reviewer( + id bigint, + name string, + email string, + stats reviewer_stats, + category integer, + primary key(id)) + create index restaurant_record_name as select name from restaurant_record + create index restaurant_reviewer_name as select name from restaurant_reviewer + create index review_rating as + select sq.rating from restaurant_record as r, (select rating from r.reviews) sq + order by sq.rating + create index tag as + select sq.value, sq.weight from restaurant_record as r, (select value, weight from r.tags) sq + order by sq.value, sq.weight + create index customers as + select sq.c from restaurant_record as r, (select c from r.customer as c) sq order by sq.c + create index customers_name as + select sq.c, r.name from restaurant_record as r, (select c from r.customer as c) sq + order by sq.c, r.name + create index stats_school as + select stats.start_date from restaurant_reviewer order by stats.start_date + create index restaurant_name_tag_value as + select r.name, sq.value from restaurant_record as r, (select value from r.tags) sq + order by r.name, sq.value + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +setup: + steps: + - query: INSERT INTO RESTAURANT_REVIEWER (ID, NAME, EMAIL, STATS) + VALUES + (1, 'Javert', 'inspecteur@policier.fr', (100, null, 'Toulon')), + (2, 'M. le Maire', null, (120, null, 'Montreuil-sur-mer')) + - query: INSERT INTO RESTAURANT_RECORD (REST_NO, NAME, REVIEWS, TAGS, CUSTOMER) + VALUES + (1000, 'Chez Thenardier', + [(1, 100), (2, 0)], + [('latmosphere', 10), ('les aliments', 70)], + ['jean', 'fantine', 'cosette', 'eponine']), + (1001, 'ABC', + [(1, 34), (2, 110)], + [('latmosphere', 40), ('les aliments', 20)], + ['gavroche', 'enjolras', 'eponine']) +--- +test_block: + name: simple-query-graph-name-tag-index + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testEqualityAndSimpleExistentialPredicate + - query: select r.rest_no from restaurant_record as r + where r.name in !! !in {['ABC']} !! + and exists (select 1 from r.tags as t where t.value in !! !in {['les aliments']} !!) + - explain: "[IN arrayDistinct(@c13)] | INJOIN q0 -> { COVERING(RESTAURANT_RECORD_NAME [EQUALS q0] -> [NAME: KEY:[0], REST_NO: KEY:[1]]) } | FETCH | FLATMAP q1 -> { EXPLODE arrayDistinct(@c30) | FLATMAP q2 -> { EXPLODE q1.TAGS | FILTER _.VALUE EQUALS q2 AS q3 RETURN (@c18 AS _0) } | DEFAULT NULL | FILTER _ NOT_NULL | MAP (1 AS _0) AS q4 RETURN (q1.REST_NO AS REST_NO) }" + - result: [{REST_NO: 1001}] +... diff --git a/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.metrics.binpb b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.metrics.binpb new file mode 100644 index 00000000000..bb049085b16 Binary files /dev/null and b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.metrics.binpb differ diff --git a/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.metrics.yaml b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.metrics.yaml new file mode 100644 index 00000000000..f08b4d9af38 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.metrics.yaml @@ -0,0 +1,29 @@ +simple-query-graph-tag-index: +- query: EXPLAIN select r.name from restaurant_record as r where exists (select + 1 from r.tags as t where t.value = ?) + ref: simple-query-graph-tag-index.yamsql:112 + explain: ISCAN(RESTAURANT_TAG_VALUE [EQUALS promote(@c24 AS STRING)]) | MAP (_.NAME + AS NAME) + task_count: 584 + task_total_time_ms: 17 + transform_count: 181 + transform_time_ms: 10 + transform_yield_count: 59 + insert_time_ms: 0 + insert_new_count: 64 + insert_reused_count: 0 +- query: EXPLAIN select r.name from restaurant_record as r where exists (select + 1 from r.tags as t where t.value in ?) + ref: simple-query-graph-tag-index.yamsql:118 + explain: ISCAN(RESTAURANT_RECORD_NAME <,>) | FLATMAP q0 -> { EXPLODE arrayDistinct(@c24) + | FLATMAP q1 -> { EXPLODE q0.TAGS | FILTER _.VALUE EQUALS q1 AS q2 RETURN + (@c12 AS _0) } | DEFAULT NULL | FILTER _ NOT_NULL AS q3 RETURN (q0.NAME AS + NAME) } + task_count: 988 + task_total_time_ms: 23 + transform_count: 336 + transform_time_ms: 11 + transform_yield_count: 77 + insert_time_ms: 1 + insert_new_count: 119 + insert_reused_count: 4 diff --git a/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.yamsql b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.yamsql new file mode 100644 index 00000000000..1073c382c22 --- /dev/null +++ b/yaml-tests/src/test/resources/cascades-query-tests/simple-query-graph-tag-index.yamsql @@ -0,0 +1,120 @@ +# +# simple-query-graph-tag-index.yamsql +# +# This source file is part of the FoundationDB open source project +# +# Copyright 2015-2026 Apple Inc. and the FoundationDB project authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Ported from `FDBSimpleQueryGraphTest.testSimpleExistentialPredicateOnSimpleIndex` — an existential +# predicate over the tags array, matched to a fan-out index on `tags.value`. +# +# Its own file: the hook adds `Restaurant$tag.value`, which exists in no other method. The source runs the +# method twice via `@BooleanSource`, once with an `EQUALS` comparison and once with `IN`; both are here. +# +# The source's index is `field("tags").nest(field("values", FanOut).nest("value"))` — the `values` hop is +# the array wrapper of `test_records_4_wrapper.proto`, which is exactly what the Relational layer +# generates for an `ARRAY` column, so the plain fan-out spelling below is the same key expression. + +# Schema: `openNestedRecordStore` — *both* TestRecords4 record types in one store, with bare primary keys +# and no record-type prefix. `WITH OPTIONS(INTERMINGLE_TABLES=true)` reproduces that, and here it is not +# just safe but load-bearing: it is why the source's plans carry a `typeFilterPlan` over the scan. The +# primary-key values do not collide (reviewers 1-2, restaurants 1000-1001). +# +# Data deviations, both invisible to every query: the apostrophe is dropped from the tag "l'atmosphère" +# and the accents from "Chez Thénardier" and "éponine". A SQL literal spells an apostrophe as the doubled +# quote `''`, and decoding that only landed in 4.13.5.0 (b9790b9af), so an older server in a mixed-mode +# configuration would read the value back with the escape still in it. +# +# One thing SQL cannot vary: `setUpWithNullableArray` builds the same records over +# `test_records_4_wrapper.proto`, where each repeated field is wrapped in a `…List` message with a +# `values` field. That *is* how the Relational layer models an `ARRAY` — `NullableArrayUtils.wrapArray` is +# applied to every generated index key — so the wrapped and unwrapped variants are one and the same DDL +# here, and `testSimplePlanGraphWithNullableArray` collapses into `testSimplePlanGraph`. +--- +schema_template: + create type as struct restaurant_review(reviewer bigint, rating integer) + create type as struct restaurant_tag(value string, weight integer) + create type as struct reviewer_stats(start_date bigint, school_name string, hometown string) + create table restaurant_record( + rest_no bigint, + name string, + reviews restaurant_review array, + tags restaurant_tag array, + customer string array, + primary key(rest_no)) + create table restaurant_reviewer( + id bigint, + name string, + email string, + stats reviewer_stats, + category integer, + primary key(id)) + create index restaurant_record_name as select name from restaurant_record + create index restaurant_reviewer_name as select name from restaurant_reviewer + create index review_rating as + select sq.rating from restaurant_record as r, (select rating from r.reviews) sq + order by sq.rating + create index tag as + select sq.value, sq.weight from restaurant_record as r, (select value, weight from r.tags) sq + order by sq.value, sq.weight + create index customers as + select sq.c from restaurant_record as r, (select c from r.customer as c) sq order by sq.c + create index customers_name as + select sq.c, r.name from restaurant_record as r, (select c from r.customer as c) sq + order by sq.c, r.name + create index stats_school as + select stats.start_date from restaurant_reviewer order by stats.start_date + create index restaurant_tag_value as + select sq.value from restaurant_record as r, (select value from r.tags) sq order by sq.value + WITH OPTIONS(INTERMINGLE_TABLES=true) +--- +setup: + steps: + - query: INSERT INTO RESTAURANT_REVIEWER (ID, NAME, EMAIL, STATS) + VALUES + (1, 'Javert', 'inspecteur@policier.fr', (100, null, 'Toulon')), + (2, 'M. le Maire', null, (120, null, 'Montreuil-sur-mer')) + - query: INSERT INTO RESTAURANT_RECORD (REST_NO, NAME, REVIEWS, TAGS, CUSTOMER) + VALUES + (1000, 'Chez Thenardier', + [(1, 100), (2, 0)], + [('latmosphere', 10), ('les aliments', 70)], + ['jean', 'fantine', 'cosette', 'eponine']), + (1001, 'ABC', + [(1, 34), (2, 110)], + [('latmosphere', 40), ('les aliments', 20)], + ['gavroche', 'enjolras', 'eponine']) +--- +test_block: + name: simple-query-graph-tag-index + preset: single_repetition_ordered + options: + statement_type: prepared + tests: + - + # testSimpleExistentialPredicateOnSimpleIndex[inComparison=false] — + # Map(Index(Restaurant$tag.value [EQUALS $t])) + - query: select r.name from restaurant_record as r + where exists (select 1 from r.tags as t where t.value = !! 'les aliments' !!) + - explain: "ISCAN(RESTAURANT_TAG_VALUE [EQUALS promote(@c24 AS STRING)]) | MAP (_.NAME AS NAME)" + - unorderedResult: [{NAME: 'Chez Thenardier'}, {NAME: 'ABC'}] + - + # testSimpleExistentialPredicateOnSimpleIndex[inComparison=true] + - query: select r.name from restaurant_record as r + where exists (select 1 from r.tags as t where t.value in !! !in {['les aliments']} !!) + - explain: "ISCAN(RESTAURANT_RECORD_NAME <,>) | FLATMAP q0 -> { EXPLODE arrayDistinct(@c24) | FLATMAP q1 -> { EXPLODE q0.TAGS | FILTER _.VALUE EQUALS q1 AS q2 RETURN (@c12 AS _0) } | DEFAULT NULL | FILTER _ NOT_NULL AS q3 RETURN (q0.NAME AS NAME) }" + - unorderedResult: [{NAME: 'Chez Thenardier'}, {NAME: 'ABC'}] +...