/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.sql.optimizer.rule.join_enum; import com.foundationdb.sql.optimizer.plan.*; import com.foundationdb.sql.optimizer.plan.IndexScan.OrderEffectiveness; import com.foundationdb.sql.optimizer.rule.SchemaRulesContext; import com.foundationdb.sql.optimizer.rule.cost.CostEstimator; /** The overall goal of a query: WHERE conditions, ORDER BY, etc. */ public class QueryIndexGoal { private BaseQuery query; private SchemaRulesContext rulesContext; private ConditionList whereConditions; // If both grouping and ordering are present, they must be // compatible. Something satisfying the ordering would also handle // the grouping. All the order by columns must also be group by // columns, though not necessarily in the same order. There can't // be any additional order by columns, because even though it // would be properly grouped going into aggregation, it wouldn't // still be sorted by those coming out. It's hard to write such a // query in SQL, since the ORDER BY can't contain columns not in // the GROUP BY, and non-columns won't appear in the index. private AggregateSource grouping; private Sort ordering; private Project projectDistinct; private Limit limit; private TableSource updateTarget; public QueryIndexGoal(BaseQuery query, SchemaRulesContext rulesContext, ConditionList whereConditions, AggregateSource grouping, Sort ordering, Project projectDistinct, Limit limit) { this.query = query; this.rulesContext = rulesContext; this.whereConditions = whereConditions; this.grouping = grouping; this.ordering = ordering; this.projectDistinct = projectDistinct; this.limit = limit; if (query instanceof DMLStatement) { DMLStatement stmt = (DMLStatement)query; if (stmt.getType() == BaseUpdateStatement.StatementType.DELETE || stmt.getType() == BaseUpdateStatement.StatementType.UPDATE) { updateTarget = stmt.getSelectTable(); } } } public BaseQuery getQuery() { return query; } public SchemaRulesContext getRulesContext() { return rulesContext; } public CostEstimator getCostEstimator() { return rulesContext.getCostEstimator(); } public ConditionList getWhereConditions() { return whereConditions; } public AggregateSource getGrouping() { return grouping; } public Sort getOrdering() { return ordering; } public Project getProjectDistinct() { return projectDistinct; } public TableSource getUpdateTarget() { return updateTarget; } public boolean needSort(OrderEffectiveness orderEffectiveness) { if ((ordering != null) || (projectDistinct != null)) return (orderEffectiveness != IndexScan.OrderEffectiveness.SORTED); if (grouping != null) return ((orderEffectiveness != IndexScan.OrderEffectiveness.SORTED) && (orderEffectiveness != IndexScan.OrderEffectiveness.GROUPED)); return false; } public int sortFields() { if (ordering != null) return ordering.getOrderBy().size(); if (projectDistinct != null) return projectDistinct.getFields().size(); if (grouping != null) return grouping.getNGroupBy(); return 0; } /** Change GROUP BY, and ORDER BY upstream of <code>node</code> as * a consequence of <code>orderEffectiveness</code> being used. */ public void installOrderEffectiveness(OrderEffectiveness orderEffectiveness) { if (grouping != null) { AggregateSource.Implementation implementation; switch (orderEffectiveness) { case SORTED: case GROUPED: implementation = AggregateSource.Implementation.PRESORTED; break; case PARTIAL_GROUPED: implementation = AggregateSource.Implementation.PREAGGREGATE_RESORT; break; default: implementation = AggregateSource.Implementation.SORT; break; } grouping.setImplementation(implementation); } if (ordering != null) { if (orderEffectiveness == IndexScan.OrderEffectiveness.SORTED) { // Sort not needed: splice it out. ordering.getOutput().replaceInput(ordering, ordering.getInput()); } } if (projectDistinct != null) { Distinct distinct = (Distinct)projectDistinct.getOutput(); Distinct.Implementation implementation; switch (orderEffectiveness) { case SORTED: implementation = Distinct.Implementation.PRESORTED; break; default: implementation = Distinct.Implementation.SORT; break; } distinct.setImplementation(implementation); } } public long getLimit() { if ((limit == null) || limit.isOffsetParameter() || limit.isLimitParameter()) return -1; // The number of rows that must be processed before you are done. return (limit.getOffset() + limit.getLimit()); } }