/** * * Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved. * * 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. */ package com.speedment.runtime.core.internal.component.sql.optimizer; import com.speedment.runtime.core.component.sql.Metrics; import com.speedment.runtime.core.component.sql.SqlStreamOptimizer; import com.speedment.runtime.core.component.sql.SqlStreamOptimizerInfo; import com.speedment.runtime.core.db.AsynchronousQueryResult; import com.speedment.runtime.core.db.DbmsType; import com.speedment.runtime.core.internal.stream.builder.action.reference.FilterAction; import com.speedment.runtime.core.internal.stream.builder.streamterminator.StreamTerminatorUtil; import static com.speedment.runtime.core.internal.stream.builder.streamterminator.StreamTerminatorUtil.isContainingOnlyFieldPredicate; import com.speedment.runtime.core.stream.Pipeline; import com.speedment.runtime.core.stream.action.Action; import java.util.ArrayList; import java.util.List; import static java.util.Objects.requireNonNull; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Predicate; import static java.util.stream.Collectors.toList; /** * This Optimizer can take care of the case where there is a mix of Field * predicates and other predicates. Field predicates will be optimized and other * predicates will be applied in the stream. * * @author Per Minborg * @param <ENTITY> the entity type */ public final class InitialFilterOptimizer<ENTITY> implements SqlStreamOptimizer<ENTITY> { // Todo: A more general expression would be better. Eg. stream().peek().filter() would still be possible... // Todo: Allow CombinedPredicates @Override public Metrics metrics(Pipeline initialPipeline, DbmsType dbmsType) { requireNonNull(initialPipeline); requireNonNull(dbmsType); final AtomicInteger filterCounter = new AtomicInteger(); traverse(initialPipeline, $ -> filterCounter.getAndIncrement()); return Metrics.of(filterCounter.get(), filterCounter.get(), 0, 0, 0); } @Override public <P extends Pipeline> P optimize( final P initialPipeline, final SqlStreamOptimizerInfo<ENTITY> info, final AsynchronousQueryResult<ENTITY> query ) { requireNonNull(initialPipeline); requireNonNull(info); requireNonNull(query); final List<FilterAction<ENTITY>> filters = new ArrayList<>(); traverse(initialPipeline, filters::add); final List<Object> values = new ArrayList<>(); final StringBuilder sql = new StringBuilder(); sql.append(info.getSqlSelect()); if (!filters.isEmpty()) { @SuppressWarnings("unchecked") List<Predicate<ENTITY>> predicates = filters.stream() .map(FilterAction::getPredicate) .map(p -> (Predicate<ENTITY>) p) .collect(toList()); final StreamTerminatorUtil.RenderResult rr = StreamTerminatorUtil.renderSqlWhere( info.getDbmsType(), info.getSqlColumnNamer(), info.getSqlDatabaseTypeFunction(), predicates ); sql.append(" WHERE ").append(rr.getSql()); values.addAll(rr.getValues()); } // final List<FieldPredicate<ENTITY>> andPredicateBuilders = topLevelAndPredicates(initialPipeline); // // if (!andPredicateBuilders.isEmpty()) { // modifySource(andPredicateBuilders, info, query); // } query.setSql(sql.toString()); query.setValues(values); initialPipeline.removeIf(filters::contains); return initialPipeline; } private void traverse(Pipeline pipeline, final Consumer<? super FilterAction<ENTITY>> filterConsumer ) { for (Action<?, ?> action : pipeline) { if (action instanceof FilterAction) { @SuppressWarnings("unchecked") final FilterAction<ENTITY> filterAction = (FilterAction<ENTITY>) action; if (isContainingOnlyFieldPredicate(filterAction.getPredicate())) { filterConsumer.accept(filterAction); } } else { // We are done. Only initial filters can be optimized return; } } } }