<commit_before>/**
 * 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;

import com.foundationdb.sql.optimizer.plan.*;
import com.foundationdb.sql.types.DataTypeDescriptor;
import com.foundationdb.sql.types.TypeId;
import com.foundationdb.server.error.InvalidOptimizerPropertyException;
import com.foundationdb.server.error.NoAggregateWithGroupByException;
import com.foundationdb.server.error.UnsupportedSQLException;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.ais.model.TableIndex;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/** Resolve aggregate functions and group by expressions to output
 * columns of the "group table," that is, the result of aggregation.
 */
public class AggregateMapper extends BaseRule
{
    private static final Logger logger = LoggerFactory.getLogger(AggregateMapper.class);

    @Override
    protected Logger getLogger() {
        return logger;
    }

    @Override
    public void apply(PlanContext plan) {
        List<AggregateSourceState> sources = new AggregateSourceFinder(plan).find();
        for (AggregateSourceState source : sources) {
            Mapper m = new Mapper((SchemaRulesContext)plan.getRulesContext(), source.aggregateSource, source.containingQuery);
            m.remap(source.aggregateSource);
        }
        for (AggregateSourceState source : sources) {
            Mapper2 m2 = new Mapper2();
            m2.remap(source.aggregateSource);
        }
    }

    static class AggregateFunctionWithAnnotation extends AggregateFunctionExpression {
        public int closeness;
        private AggregateSource source;

        public AggregateFunctionWithAnnotation(AggregateFunctionExpression aggregateFunc, int closeness, AggregateSource source) {
            super(aggregateFunc.getFunction(),
                  aggregateFunc.getOperand(),
                  aggregateFunc.isDistinct(),
                  aggregateFunc.getSQLtype(),
                  aggregateFunc.getSQLsource(),
                  aggregateFunc.getType(),
                  aggregateFunc.getOption(),
                  aggregateFunc.getOrderBy());
            this.closeness = closeness;
            this.source = source;
        }

        public AggregateFunctionWithAnnotation setQueryAndCloseness(Integer closeness, AggregateSource source) {
            if (this.closeness > closeness) {
                this.closeness = closeness;
                this.source = source;
            }
            return this;
        }

        public boolean isThisIt(AggregateSource source) {
            if (this.source == source) {
                return true;
            }
            return false;
        }

        public AggregateSource getSource() {
            return source;
        }
    }

    static class AggregateSourceFinder extends SubqueryBoundTablesTracker {
        List<AggregateSourceState> result = new ArrayList<>();

        public AggregateSourceFinder(PlanContext planContext) {
            super(planContext);
        }

        public List<AggregateSourceState> find() {
            run();
            return result;
        }

        @Override
        public boolean visit(PlanNode n) {
            super.visit(n);
            if (n instanceof AggregateSource)
                result.add(new AggregateSourceState((AggregateSource)n, currentQuery()));
            return true;
        }
    }

    static class AggregateSourceState {
        AggregateSource aggregateSource;
        BaseQuery containingQuery;

        public AggregateSourceState(AggregateSource aggregateSource, 
                                    BaseQuery containingQuery) {
            this.aggregateSource = aggregateSource;
            this.containingQuery = containingQuery;
        }
    }

    static class Mapper implements ExpressionRewriteVisitor, PlanVisitor {
        private SchemaRulesContext rulesContext;
        private AggregateSource source;
        private BaseQuery query;
        private Deque<BaseQuery> subqueries = new ArrayDeque<>();
        private Set<ColumnSource> aggregated = new HashSet<>();
        private Map<ExpressionNode,ExpressionNode> map = 
            new HashMap<>();
        private enum ImplicitAggregateSetting {
            ERROR, FIRST, FIRST_IF_UNIQUE
        };
        private ImplicitAggregateSetting implicitAggregateSetting;
        private Set<TableSource> uniqueGroupedTables;
<commit_msg>check for aggregatefunctionexpressions without aggregatesources<commit_after>/**
 * 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;

import com.foundationdb.sql.optimizer.plan.*;
import com.foundationdb.sql.types.DataTypeDescriptor;
import com.foundationdb.sql.types.TypeId;
import com.foundationdb.server.error.InvalidOptimizerPropertyException;
import com.foundationdb.server.error.NoAggregateWithGroupByException;
import com.foundationdb.server.error.UnsupportedSQLException;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.ais.model.TableIndex;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/** Resolve aggregate functions and group by expressions to output
 * columns of the "group table," that is, the result of aggregation.
 */
public class AggregateMapper extends BaseRule
{
    private static final Logger logger = LoggerFactory.getLogger(AggregateMapper.class);

    @Override
    protected Logger getLogger() {
        return logger;
    }

    @Override
    public void apply(PlanContext plan) {
        AggregateSourceFinder aggregateSourceFinder = new AggregateSourceFinder(plan);
        List<AggregateSourceState> sources = aggregateSourceFinder.find();
        List<AggregateFunctionExpression> functions = aggregateSourceFinder.getFunctions();
        if (sources.isEmpty() && !functions.isEmpty()) {
            throw new UnsupportedSQLException("Aggregate not allowed in WHERE",
                                              functions.get(0).getSQLsource());
        }
        for (AggregateSourceState source : sources) {
            Mapper m = new Mapper((SchemaRulesContext)plan.getRulesContext(), source.aggregateSource, source.containingQuery);
            m.remap(source.aggregateSource);
        }
        for (AggregateSourceState source : sources) {
            Mapper2 m2 = new Mapper2();
            m2.remap(source.aggregateSource);
        }
    }

    static class AggregateFunctionWithAnnotation extends AggregateFunctionExpression {
        public int closeness;
        private AggregateSource source;

        public AggregateFunctionWithAnnotation(AggregateFunctionExpression aggregateFunc, int closeness, AggregateSource source) {
            super(aggregateFunc.getFunction(),
                  aggregateFunc.getOperand(),
                  aggregateFunc.isDistinct(),
                  aggregateFunc.getSQLtype(),
                  aggregateFunc.getSQLsource(),
                  aggregateFunc.getType(),
                  aggregateFunc.getOption(),
                  aggregateFunc.getOrderBy());
            this.closeness = closeness;
            this.source = source;
        }

        public AggregateFunctionWithAnnotation setQueryAndCloseness(Integer closeness, AggregateSource source) {
            if (this.closeness > closeness) {
                this.closeness = closeness;
                this.source = source;
            }
            return this;
        }

        public AggregateSource getSource() {
            return source;
        }
    }

    static class AggregateSourceFinder extends SubqueryBoundTablesTracker {
        List<AggregateSourceState> result = new ArrayList<>();
        List<AggregateFunctionExpression> functions = new ArrayList<>();

        public AggregateSourceFinder(PlanContext planContext) {
            super(planContext);
        }

        public List<AggregateSourceState> find() {
            run();
            return result;
        }

        public List<AggregateFunctionExpression> getFunctions() {
            return functions;
        }

        @Override
        public boolean visit(PlanNode n) {
            super.visit(n);
            if (n instanceof AggregateSource)
                result.add(new AggregateSourceState((AggregateSource)n, currentQuery()));
            return true;
        }

        @Override
        public boolean visit(ExpressionNode n) {
            if (n instanceof AggregateFunctionExpression)
                functions.add((AggregateFunctionExpression)n);
            return true;
        }
    }

    static class AggregateSourceState {
        AggregateSource aggregateSource;
        BaseQuery containingQuery;

        public AggregateSourceState(AggregateSource aggregateSource, 
                                    BaseQuery containingQuery) {
            this.aggregateSource = aggregateSource;
            this.containingQuery = containingQuery;
        }
    }

    static class Mapper implements ExpressionRewriteVisitor, PlanVisitor {
        private SchemaRulesContext rulesContext;
        private AggregateSource source;
        private BaseQuery query;
        private Deque<BaseQuery> subqueries = new ArrayDeque<>();
        private Set<ColumnSource> aggregated = new HashSet<>();
        private Map<ExpressionNode,ExpressionNode> map = 
            new HashMap<>();
        private enum ImplicitAggregateSetting {
            ERROR, FIRST, FIRST_IF_UNIQUE
        };
        private ImplicitAggregateSetting implicitAggregateSetting;
        private Set<TableSource> uniqueGroupedTables;

{'fraction_non_alphanumeric': 0.07296253032617486, 'fraction_numerical': 0.002785515320334262, 'mean_word_length': 2.6551724137931036, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3752051', 'n_tokens_mistral': 2776, 'n_tokens_neox': 2727, 'n_words': 741}