use of com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan in project fdb-record-layer by FoundationDB.
the class FilterVisitor method postVisit.
@Nonnull
@Override
public RecordQueryPlan postVisit(@Nonnull RecordQueryPlan recordQueryPlan) {
if (recordQueryPlan instanceof RecordQueryFilterPlan) {
final RecordQueryFilterPlan filterPlan = (RecordQueryFilterPlan) recordQueryPlan;
final List<QueryComponent> filters = filterPlan.getFilters();
final AvailableFields availableFields = availableFields(((RecordQueryFilterPlan) recordQueryPlan).getInnerPlan());
// Partition the filters according to whether they can be evaluated using just the fields from the index or
// if they need a full record.
final List<QueryComponent> indexFilters = Lists.newArrayListWithCapacity(filters.size());
final List<QueryComponent> residualFilters = Lists.newArrayListWithCapacity(filters.size());
final Set<KeyExpression> allReferencedFields = new HashSet<>();
partitionFilters(filters, availableFields, indexFilters, residualFilters, allReferencedFields);
Verify.verify(indexFilters.size() + residualFilters.size() == filters.size());
if (indexFilters.isEmpty()) {
return recordQueryPlan;
}
@Nullable RecordQueryPlan removedFetchPlan = removeIndexFetch(filterPlan.getChild(), allReferencedFields);
if (removedFetchPlan == null) {
return recordQueryPlan;
}
recordQueryPlan = new RecordQueryFetchFromPartialRecordPlan(new RecordQueryFilterPlan(removedFetchPlan, indexFilters), TranslateValueFunction.unableToTranslate());
if (!residualFilters.isEmpty()) {
recordQueryPlan = new RecordQueryFilterPlan(recordQueryPlan, residualFilters);
}
}
return recordQueryPlan;
}
use of com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan in project fdb-record-layer by FoundationDB.
the class IntersectionVisitor method postVisit.
@Nonnull
@Override
public RecordQueryPlan postVisit(@Nonnull final RecordQueryPlan recordQueryPlan) {
if (recordQueryPlan instanceof RecordQueryIntersectionPlan) {
RecordQueryIntersectionPlan intersectionPlan = (RecordQueryIntersectionPlan) recordQueryPlan;
Set<KeyExpression> requiredFields = intersectionPlan.getRequiredFields();
List<RecordQueryPlan> newChildren = new ArrayList<>(intersectionPlan.getChildren().size());
for (RecordQueryPlan plan : intersectionPlan.getChildren()) {
@Nullable RecordQueryPlan newPlan = removeIndexFetch(plan, requiredFields);
if (newPlan == null) {
// can't remove index fetch, so give up
return recordQueryPlan;
}
newChildren.add(newPlan);
}
return new RecordQueryFetchFromPartialRecordPlan(RecordQueryIntersectionPlan.from(newChildren, intersectionPlan.getComparisonKey()), TranslateValueFunction.unableToTranslate());
}
return recordQueryPlan;
}
use of com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan in project fdb-record-layer by FoundationDB.
the class ValueIndexScanMatchCandidate method tryFetchCoveringIndexScan.
@Nonnull
private Optional<RelationalExpression> tryFetchCoveringIndexScan(@Nonnull final PartialMatch partialMatch, @Nonnull final List<ComparisonRange> comparisonRanges, final boolean isReverse) {
if (recordTypes.size() > 1) {
return Optional.empty();
}
final RecordType recordType = Iterables.getOnlyElement(recordTypes);
final IndexKeyValueToPartialRecord.Builder builder = IndexKeyValueToPartialRecord.newBuilder(recordType);
for (int i = 0; i < indexKeyValues.size(); i++) {
final Value keyValue = indexKeyValues.get(i);
if (keyValue instanceof FieldValue && keyValue.isFunctionallyDependentOn(recordValue)) {
final AvailableFields.FieldData fieldData = AvailableFields.FieldData.of(IndexKeyValueToPartialRecord.TupleSource.KEY, i);
addCoveringField(builder, (FieldValue) keyValue, fieldData);
}
}
for (int i = 0; i < indexValueValues.size(); i++) {
final Value valueValue = indexValueValues.get(i);
if (valueValue instanceof FieldValue && valueValue.isFunctionallyDependentOn(recordValue)) {
final AvailableFields.FieldData fieldData = AvailableFields.FieldData.of(IndexKeyValueToPartialRecord.TupleSource.VALUE, i);
addCoveringField(builder, (FieldValue) valueValue, fieldData);
}
}
if (!builder.isValid()) {
return Optional.empty();
}
final IndexScanParameters scanParameters = IndexScanComparisons.byValue(toScanComparisons(comparisonRanges));
final RecordQueryPlanWithIndex indexPlan = new RecordQueryIndexPlan(index.getName(), scanParameters, isReverse, false, (ValueIndexScanMatchCandidate) partialMatch.getMatchCandidate());
final RecordQueryCoveringIndexPlan coveringIndexPlan = new RecordQueryCoveringIndexPlan(indexPlan, recordType.getName(), // not used except for old planner properties
AvailableFields.NO_FIELDS, builder.build());
return Optional.of(new RecordQueryFetchFromPartialRecordPlan(coveringIndexPlan, coveringIndexPlan::pushValueThroughFetch));
}
use of com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan in project fdb-record-layer by FoundationDB.
the class PushSetOperationThroughFetchRule method onMatch.
@Override
@SuppressWarnings("java:S1905")
public void onMatch(@Nonnull PlannerRuleCall call) {
final PlannerBindings bindings = call.getBindings();
final RecordQuerySetPlan setOperationPlan = bindings.get(getMatcher());
final List<? extends Quantifier.Physical> quantifiersOverFetches = bindings.getAll(quantifierOverFetchMatcher);
// if set operation is dynamic all quantifiers must have fetches
if (setOperationPlan.isDynamic()) {
if (quantifiersOverFetches.size() < setOperationPlan.getQuantifiers().size()) {
return;
}
} else {
if (quantifiersOverFetches.size() <= 1) {
// pulling up the fetch is meaningless in this case
return;
}
}
final List<? extends RecordQueryFetchFromPartialRecordPlan> fetchPlans = bindings.getAll(fetchPlanMatcher);
final ImmutableList<TranslateValueFunction> dependentFunctions = fetchPlans.stream().map(RecordQueryFetchFromPartialRecordPlan::getPushValueFunction).collect(ImmutableList.toImmutableList());
Verify.verify(quantifiersOverFetches.size() == fetchPlans.size());
Verify.verify(fetchPlans.size() == dependentFunctions.size());
final List<? extends Value> requiredValues = setOperationPlan.getRequiredValues(CorrelationIdentifier.uniqueID());
final Set<CorrelationIdentifier> pushableAliases = setOperationPlan.tryPushValues(dependentFunctions, quantifiersOverFetches, requiredValues);
// if set operation is dynamic all aliases must be pushable
if (setOperationPlan.isDynamic()) {
if (pushableAliases.size() < setOperationPlan.getQuantifiers().size()) {
return;
}
} else {
if (pushableAliases.size() <= 1) {
// pulling up the fetch is meaningless in this case
return;
}
}
final ImmutableList.Builder<Quantifier.Physical> pushableQuantifiersBuilder = ImmutableList.builder();
final ImmutableList.Builder<RecordQueryFetchFromPartialRecordPlan> pushableFetchPlansBuilder = ImmutableList.builder();
final ImmutableList.Builder<TranslateValueFunction> pushableDependentFunctionsBuilder = ImmutableList.builder();
for (int i = 0; i < quantifiersOverFetches.size(); i++) {
final Quantifier.Physical quantifier = quantifiersOverFetches.get(i);
if (pushableAliases.contains(quantifier.getAlias())) {
pushableQuantifiersBuilder.add(quantifier);
pushableFetchPlansBuilder.add(fetchPlans.get(i));
pushableDependentFunctionsBuilder.add(dependentFunctions.get(i));
}
}
final ImmutableList<Quantifier.Physical> pushableQuantifiers = pushableQuantifiersBuilder.build();
final ImmutableList<RecordQueryFetchFromPartialRecordPlan> pushableFetchPlans = pushableFetchPlansBuilder.build();
final ImmutableList<TranslateValueFunction> pushableDependentFunctions = pushableDependentFunctionsBuilder.build();
final ImmutableList<Quantifier.Physical> nonPushableQuantifiers = setOperationPlan.getQuantifiers().stream().map(quantifier -> (Quantifier.Physical) quantifier).filter(quantifier -> !pushableAliases.contains(quantifier.getAlias())).collect(ImmutableList.toImmutableList());
final List<? extends ExpressionRef<RecordQueryPlan>> newPushedInnerPlans = pushableFetchPlans.stream().map(RecordQueryFetchFromPartialRecordPlan::getChild).map(GroupExpressionRef::of).collect(ImmutableList.toImmutableList());
Verify.verify(pushableQuantifiers.size() + nonPushableQuantifiers.size() == setOperationPlan.getQuantifiers().size());
final TranslateValueFunction combinedTranslateValueFunction = setOperationPlan.pushValueFunction(pushableDependentFunctions);
final RecordQuerySetPlan newSetOperationPlan = setOperationPlan.withChildrenReferences(newPushedInnerPlans);
final RecordQueryFetchFromPartialRecordPlan newFetchPlan = new RecordQueryFetchFromPartialRecordPlan(newSetOperationPlan, combinedTranslateValueFunction);
if (nonPushableQuantifiers.isEmpty()) {
call.yield(GroupExpressionRef.of(newFetchPlan));
} else {
final List<ExpressionRef<? extends RecordQueryPlan>> newFetchPlanAndResidualInners = Streams.concat(Stream.of(GroupExpressionRef.of(newFetchPlan)), nonPushableQuantifiers.stream().map(Quantifier.Physical::getRangesOver).map(RecordQueryPlan::narrowReference)).collect(ImmutableList.toImmutableList());
call.yield(GroupExpressionRef.of(setOperationPlan.withChildrenReferences(newFetchPlanAndResidualInners)));
}
}
use of com.apple.foundationdb.record.query.plan.plans.RecordQueryFetchFromPartialRecordPlan in project fdb-record-layer by FoundationDB.
the class MergeProjectionAndFetchRule method onMatch.
@Override
public void onMatch(@Nonnull PlannerRuleCall call) {
final LogicalProjectionExpression projectionExpression = call.get(root);
// if the fetch is able to push all values we can eliminate the fetch as well
final RecordQueryFetchFromPartialRecordPlan fetchPlan = call.get(innerPlanMatcher);
final CorrelationIdentifier newInnerAlias = CorrelationIdentifier.uniqueID();
final List<? extends Value> resultValues = projectionExpression.getResultValues();
final boolean allPushable = resultValues.stream().allMatch(value -> fetchPlan.pushValue(value, newInnerAlias).isPresent());
if (allPushable) {
// all fields in the projection are already available underneath the fetch
// we don't need the projection nor the fetch
call.yield(call.ref(fetchPlan.getChild()));
}
}
Aggregations