<commit_before>/**
 * END USER LICENSE AGREEMENT (“EULA”)
 *
 * READ THIS AGREEMENT CAREFULLY (date: 9/13/2011):
 * http://www.akiban.com/licensing/20110913
 *
 * BY INSTALLING OR USING ALL OR ANY PORTION OF THE SOFTWARE, YOU ARE ACCEPTING
 * ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. YOU AGREE THAT THIS
 * AGREEMENT IS ENFORCEABLE LIKE ANY WRITTEN AGREEMENT SIGNED BY YOU.
 *
 * IF YOU HAVE PAID A LICENSE FEE FOR USE OF THE SOFTWARE AND DO NOT AGREE TO
 * THESE TERMS, YOU MAY RETURN THE SOFTWARE FOR A FULL REFUND PROVIDED YOU (A) DO
 * NOT USE THE SOFTWARE AND (B) RETURN THE SOFTWARE WITHIN THIRTY (30) DAYS OF
 * YOUR INITIAL PURCHASE.
 *
 * IF YOU WISH TO USE THE SOFTWARE AS AN EMPLOYEE, CONTRACTOR, OR AGENT OF A
 * CORPORATION, PARTNERSHIP OR SIMILAR ENTITY, THEN YOU MUST BE AUTHORIZED TO SIGN
 * FOR AND BIND THE ENTITY IN ORDER TO ACCEPT THE TERMS OF THIS AGREEMENT. THE
 * LICENSES GRANTED UNDER THIS AGREEMENT ARE EXPRESSLY CONDITIONED UPON ACCEPTANCE
 * BY SUCH AUTHORIZED PERSONNEL.
 *
 * IF YOU HAVE ENTERED INTO A SEPARATE WRITTEN LICENSE AGREEMENT WITH AKIBAN FOR
 * USE OF THE SOFTWARE, THE TERMS AND CONDITIONS OF SUCH OTHER AGREEMENT SHALL
 * PREVAIL OVER ANY CONFLICTING TERMS OR CONDITIONS IN THIS AGREEMENT.
 */

package com.akiban.sql.optimizer.rule;

import com.akiban.server.types3.TPreptimeValue;
import com.akiban.server.types3.Types3Switch;
import com.akiban.server.types3.pvalue.PValueSource;
import com.akiban.sql.optimizer.plan.*;
import com.akiban.sql.optimizer.plan.ExpressionsSource.DistinctState;

import com.akiban.server.expression.std.Comparison;

import com.akiban.server.types.AkType;
import com.akiban.server.types.NullValueSource;

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

import java.util.*;

/** Evaluate as much as possible at generate time. 
 * As with any compiler, false constants used in conditions can lead
 * to dead code, that is, join sources that don't need to bother
 * outputting any data. And these empty data sets can in turn affect
 * subqueries and aggregation.
 */
public class ConstantFolder extends BaseRule 
{
    private static final Logger logger = LoggerFactory.getLogger(ConstantFolder.class);
    
    private final boolean usePValues;
    
    public ConstantFolder() {
        this(Types3Switch.ON);
    }

    public ConstantFolder(boolean usePValues) {
        this.usePValues = usePValues;
    }

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

    @Override
    public void apply(PlanContext planContext) {
        Folder folder = usePValues ? new NewFolder(planContext) : new OldFolder(planContext);
        while (folder.foldConstants());
        folder.finishAggregates();
    }

    private static abstract class Folder implements PlanVisitor, ExpressionRewriteVisitor {
        private final PlanContext planContext;
        protected final ExpressionAssembler<?> expressionAssembler;
        private Set<ColumnSource> eliminatedSources = new HashSet<ColumnSource>();
        private Set<AggregateSource> changedAggregates = null;
        private enum State { FOLDING, AGGREGATES };
        private State state;
        private boolean changed;
        private Map<ConditionExpression,Boolean> topLevelConditions = 
            new IdentityHashMap<ConditionExpression,Boolean>();

        protected Folder(PlanContext planContext, ExpressionAssembler<?> expressionAssembler) {
            this.planContext = planContext;
            this.expressionAssembler = expressionAssembler;
        }
        
        public ExpressionNode foldConstants(ExpressionNode fromNode) {
            do {
                state = State.FOLDING;
                changed = false;
                topLevelConditions.clear();
                fromNode = fromNode.accept(this);
            } while (changed);
            return fromNode;
        }

        /** Return <code>true</code> if substantial enough changes were made that
         * need to be run again.
         */
        public boolean foldConstants() {
            state = State.FOLDING;
            changed = false;
            topLevelConditions.clear();
            planContext.accept(this);
            return changed;
        }

        @Override
        public boolean visitEnter(PlanNode n) {
            return visit(n);
        }

        @Override
        public boolean visitLeave(PlanNode n) {
            if (state == State.FOLDING) {
                if (n instanceof Select)
                    selectNode((Select)n);
                else if (n instanceof SubquerySource)
                    subquerySource((SubquerySource)n);
                else if (n instanceof AggregateSource)
                    aggregateSource((AggregateSource)n);
            }
            return true;
        }

        @Override
        public boolean visit(PlanNode n) {
            if (n instanceof Select) {
                for (ConditionExpression condition : ((Select)n).getConditions()) {
                    topLevelConditions.put(condition, Boolean.TRUE);
                }
            }
            return true;
        }

        @Override
        public boolean visitChildrenFirst(ExpressionNode expr) {
            // This assumes that there is no particular advantage to
            // figuring out the whole expression tree of functions and
            // constants and passing it to eval as a whole, rather than
            // doing it piece-by-piece.
            return true;
        }

        @Override
        public ExpressionNode visit(ExpressionNode expr) {
            if (state == State.FOLDING) {
                if (expr instanceof ComparisonCondition)
                    return comparisonCondition((ComparisonCondition)expr);
                else if (expr instanceof CastExpression)
                    return castExpression((CastExpression)expr);
                else if (expr instanceof FunctionExpression) {
                    if (expr instanceof LogicalFunctionCondition)
                        return logicalFunctionCondition((LogicalFunctionCondition)expr);
                    else
                        return functionExpression((FunctionExpression)expr);
                }
                else if (expr instanceof IfElseExpression)
                    return ifElseExpression((IfElseExpression)expr);
                else if (expr instanceof ColumnExpression)
                    return columnExpression((ColumnExpression)expr);
                else if (expr instanceof SubqueryValueExpression)
                    return subqueryValueExpression((SubqueryValueExpression)expr);
                else if (expr instanceof ExistsCondition)
                    return existsCondition((ExistsCondition)expr);
                else if (expr instanceof AnyCondition)
                    return anyCondition((AnyCondition)expr);
            }
            else if (state == State.AGGREGATES) {
                if (expr instanceof ColumnExpression)
                    return columnExpression((ColumnExpression)expr);
            }
            return expr;
        }

        protected ExpressionNode comparisonCondition(ComparisonCondition cond) {
            Constantness lc = isConstant(cond.getLeft());
            Constantness rc = isConstant(cond.getRight());
            if ((lc != Constantness.VARIABLE) && (rc != Constantness.VARIABLE))
                return evalNow(cond);
            if ((lc == Constantness.NULL) || (rc == Constantness.NULL))
                return new BooleanConstantExpression(null, 
                                                     cond.getSQLtype(), 
                                                     cond.getSQLsource());
            if (isIdempotentEquality(cond)) {
                if ((cond.getLeft().getSQLtype() != null) &&
                    !cond.getLeft().getSQLtype().isNullable()) {
                    return new BooleanConstantExpression(Boolean.TRUE, 
                                                         cond.getSQLtype(), 
                                                         cond.getSQLsource());
                }
            }
            return cond;
        }
        
        protected boolean isIdempotentEquality(ComparisonCondition cond) {
            if (cond.getOperation() != Comparison.EQ)
                return false;
            ExpressionNode left = cond.getLeft();
            if (!left.equals(cond.getRight()))
                return false;
            if ((left instanceof FunctionExpression) &&<commit_msg>when constant folding expressions, check for NULLs before all else<commit_after>/**
 * END USER LICENSE AGREEMENT (“EULA”)
 *
 * READ THIS AGREEMENT CAREFULLY (date: 9/13/2011):
 * http://www.akiban.com/licensing/20110913
 *
 * BY INSTALLING OR USING ALL OR ANY PORTION OF THE SOFTWARE, YOU ARE ACCEPTING
 * ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. YOU AGREE THAT THIS
 * AGREEMENT IS ENFORCEABLE LIKE ANY WRITTEN AGREEMENT SIGNED BY YOU.
 *
 * IF YOU HAVE PAID A LICENSE FEE FOR USE OF THE SOFTWARE AND DO NOT AGREE TO
 * THESE TERMS, YOU MAY RETURN THE SOFTWARE FOR A FULL REFUND PROVIDED YOU (A) DO
 * NOT USE THE SOFTWARE AND (B) RETURN THE SOFTWARE WITHIN THIRTY (30) DAYS OF
 * YOUR INITIAL PURCHASE.
 *
 * IF YOU WISH TO USE THE SOFTWARE AS AN EMPLOYEE, CONTRACTOR, OR AGENT OF A
 * CORPORATION, PARTNERSHIP OR SIMILAR ENTITY, THEN YOU MUST BE AUTHORIZED TO SIGN
 * FOR AND BIND THE ENTITY IN ORDER TO ACCEPT THE TERMS OF THIS AGREEMENT. THE
 * LICENSES GRANTED UNDER THIS AGREEMENT ARE EXPRESSLY CONDITIONED UPON ACCEPTANCE
 * BY SUCH AUTHORIZED PERSONNEL.
 *
 * IF YOU HAVE ENTERED INTO A SEPARATE WRITTEN LICENSE AGREEMENT WITH AKIBAN FOR
 * USE OF THE SOFTWARE, THE TERMS AND CONDITIONS OF SUCH OTHER AGREEMENT SHALL
 * PREVAIL OVER ANY CONFLICTING TERMS OR CONDITIONS IN THIS AGREEMENT.
 */

package com.akiban.sql.optimizer.rule;

import com.akiban.server.types3.TPreptimeValue;
import com.akiban.server.types3.Types3Switch;
import com.akiban.server.types3.pvalue.PValueSource;
import com.akiban.sql.optimizer.plan.*;
import com.akiban.sql.optimizer.plan.ExpressionsSource.DistinctState;

import com.akiban.server.expression.std.Comparison;

import com.akiban.server.types.AkType;
import com.akiban.server.types.NullValueSource;

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

import java.util.*;

/** Evaluate as much as possible at generate time. 
 * As with any compiler, false constants used in conditions can lead
 * to dead code, that is, join sources that don't need to bother
 * outputting any data. And these empty data sets can in turn affect
 * subqueries and aggregation.
 */
public class ConstantFolder extends BaseRule 
{
    private static final Logger logger = LoggerFactory.getLogger(ConstantFolder.class);
    
    private final boolean usePValues;
    
    public ConstantFolder() {
        this(Types3Switch.ON);
    }

    public ConstantFolder(boolean usePValues) {
        this.usePValues = usePValues;
    }

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

    @Override
    public void apply(PlanContext planContext) {
        Folder folder = usePValues ? new NewFolder(planContext) : new OldFolder(planContext);
        while (folder.foldConstants());
        folder.finishAggregates();
    }

    private static abstract class Folder implements PlanVisitor, ExpressionRewriteVisitor {
        private final PlanContext planContext;
        protected final ExpressionAssembler<?> expressionAssembler;
        private Set<ColumnSource> eliminatedSources = new HashSet<ColumnSource>();
        private Set<AggregateSource> changedAggregates = null;
        private enum State { FOLDING, AGGREGATES };
        private State state;
        private boolean changed;
        private Map<ConditionExpression,Boolean> topLevelConditions = 
            new IdentityHashMap<ConditionExpression,Boolean>();

        protected Folder(PlanContext planContext, ExpressionAssembler<?> expressionAssembler) {
            this.planContext = planContext;
            this.expressionAssembler = expressionAssembler;
        }
        
        public ExpressionNode foldConstants(ExpressionNode fromNode) {
            do {
                state = State.FOLDING;
                changed = false;
                topLevelConditions.clear();
                fromNode = fromNode.accept(this);
            } while (changed);
            return fromNode;
        }

        /** Return <code>true</code> if substantial enough changes were made that
         * need to be run again.
         */
        public boolean foldConstants() {
            state = State.FOLDING;
            changed = false;
            topLevelConditions.clear();
            planContext.accept(this);
            return changed;
        }

        @Override
        public boolean visitEnter(PlanNode n) {
            return visit(n);
        }

        @Override
        public boolean visitLeave(PlanNode n) {
            if (state == State.FOLDING) {
                if (n instanceof Select)
                    selectNode((Select)n);
                else if (n instanceof SubquerySource)
                    subquerySource((SubquerySource)n);
                else if (n instanceof AggregateSource)
                    aggregateSource((AggregateSource)n);
            }
            return true;
        }

        @Override
        public boolean visit(PlanNode n) {
            if (n instanceof Select) {
                for (ConditionExpression condition : ((Select)n).getConditions()) {
                    topLevelConditions.put(condition, Boolean.TRUE);
                }
            }
            return true;
        }

        @Override
        public boolean visitChildrenFirst(ExpressionNode expr) {
            // This assumes that there is no particular advantage to
            // figuring out the whole expression tree of functions and
            // constants and passing it to eval as a whole, rather than
            // doing it piece-by-piece.
            return true;
        }

        @Override
        public ExpressionNode visit(ExpressionNode expr) {
            if (state == State.FOLDING) {
                if (expr instanceof ComparisonCondition)
                    return comparisonCondition((ComparisonCondition)expr);
                else if (expr instanceof CastExpression)
                    return castExpression((CastExpression)expr);
                else if (expr instanceof FunctionExpression) {
                    if (expr instanceof LogicalFunctionCondition)
                        return logicalFunctionCondition((LogicalFunctionCondition)expr);
                    else
                        return functionExpression((FunctionExpression)expr);
                }
                else if (expr instanceof IfElseExpression)
                    return ifElseExpression((IfElseExpression)expr);
                else if (expr instanceof ColumnExpression)
                    return columnExpression((ColumnExpression)expr);
                else if (expr instanceof SubqueryValueExpression)
                    return subqueryValueExpression((SubqueryValueExpression)expr);
                else if (expr instanceof ExistsCondition)
                    return existsCondition((ExistsCondition)expr);
                else if (expr instanceof AnyCondition)
                    return anyCondition((AnyCondition)expr);
            }
            else if (state == State.AGGREGATES) {
                if (expr instanceof ColumnExpression)
                    return columnExpression((ColumnExpression)expr);
            }
            return expr;
        }

        protected ExpressionNode comparisonCondition(ComparisonCondition cond) {
            Constantness lc = isConstant(cond.getLeft());
            Constantness rc = isConstant(cond.getRight());
            if ((lc == Constantness.NULL) || (rc == Constantness.NULL))
                return new BooleanConstantExpression(null, 
                                                     cond.getSQLtype(), 
                                                     cond.getSQLsource());
            if ((lc != Constantness.VARIABLE) && (rc != Constantness.VARIABLE))
                return evalNow(cond);
            if (isIdempotentEquality(cond)) {
                if ((cond.getLeft().getSQLtype() != null) &&
                    !cond.getLeft().getSQLtype().isNullable()) {
                    return new BooleanConstantExpression(Boolean.TRUE, 
                                                         cond.getSQLtype(), 
                                                         cond.getSQLsource());
                }
            }
            return cond;
        }
        
        protected boolean isIdempotentEquality(ComparisonCondition cond) {
            if (cond.getOperation() != Comparison.EQ)
                return false;
            ExpressionNode left = cond.getLeft();
            if (!left.equals(cond.getRight()))
                return false;
            if ((left instanceof FunctionExpression) &&
{'fraction_non_alphanumeric': 0.0723379290114403, 'fraction_numerical': 0.002816075095335876, 'mean_word_length': 2.0493738819320213, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6743007', 'n_tokens_mistral': 4247, 'n_tokens_neox': 4021, 'n_words': 1265}