Package org.voltdb.plannodes

Source Code of org.voltdb.plannodes.AbstractJoinPlanNode

/* This file is part of VoltDB.
* Copyright (C) 2008-2014 VoltDB Inc.
*
* 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 VoltDB.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.voltdb.plannodes;

import java.util.List;
import java.util.TreeMap;

import org.json_voltpatches.JSONArray;
import org.json_voltpatches.JSONException;
import org.json_voltpatches.JSONObject;
import org.json_voltpatches.JSONStringer;
import org.voltdb.catalog.Database;
import org.voltdb.expressions.AbstractExpression;
import org.voltdb.expressions.ExpressionUtil;
import org.voltdb.expressions.TupleValueExpression;
import org.voltdb.types.JoinType;
import org.voltdb.types.PlanNodeType;
import org.voltdb.types.SortDirectionType;

public abstract class AbstractJoinPlanNode extends AbstractPlanNode {

    public enum Members {
        SORT_DIRECTION,
        JOIN_TYPE,
        PRE_JOIN_PREDICATE,
        JOIN_PREDICATE,
        WHERE_PREDICATE,
        OUTPUT_SCHEMA_PRE_AGG;
    }

    protected JoinType m_joinType = JoinType.INNER;
    // sortDirection is only used in handleOrderBy(),
    // and the sortDirection used in EE is from inlined IndexScan node for NLIJ
    protected SortDirectionType m_sortDirection = SortDirectionType.INVALID;
    protected AbstractExpression m_preJoinPredicate = null;
    protected AbstractExpression m_joinPredicate = null;
    protected AbstractExpression m_wherePredicate = null;

    protected NodeSchema m_outputSchemaPreInlineAgg = null;

    protected AbstractJoinPlanNode() {
        super();
    }

    @Override
    public void validate() throws Exception {
        super.validate();

        if (m_preJoinPredicate != null) {
            m_preJoinPredicate.validate();
        }
        if (m_joinPredicate != null) {
            m_joinPredicate.validate();
        }
        if (m_wherePredicate != null) {
            m_wherePredicate.validate();
        }
    }

    /**
     * @return the join_type
     */
    public JoinType getJoinType() {
        return m_joinType;
    }

    /**
     * @param join_type the join_type to set
     */
    public void setJoinType(JoinType join_type) {
        m_joinType = join_type;
    }

    /**
     * @return the  pre join predicate
     */
    public AbstractExpression getPreJoinPredicate() {
        return m_preJoinPredicate;
    }

    /**
     * @return the  join predicate
     */
    public AbstractExpression getJoinPredicate() {
        return m_joinPredicate;
    }

    /**
     * @return the  where predicate
     */
    public AbstractExpression getWherePredicate() {
        return m_wherePredicate;
    }

    /**
     * @param predicate the where predicate to set
     */
    public void setWherePredicate(AbstractExpression predicate)
    {
        if (predicate != null) {
            m_wherePredicate = (AbstractExpression) predicate.clone();
        }
    }

    /**
     * @param predicate the join predicate to set
     */
    public void setPreJoinPredicate(AbstractExpression predicate)
    {
        if (predicate != null) {
            m_preJoinPredicate = (AbstractExpression) predicate.clone();
        }
    }

    /**
     * @param predicate the join predicate to set
     */
    public void setJoinPredicate(AbstractExpression predicate)
    {
        if (predicate != null) {
            m_joinPredicate = (AbstractExpression) predicate.clone();
        }
    }

    @Override
    public void generateOutputSchema(Database db)
    {
        // FUTURE: At some point it would be awesome to further
        // cull the columns out of the join to remove columns that were only
        // used by scans/joins.  I think we can coerce HSQL into provide this
        // info relatively easily. --izzy

        // Index join will have to override this method.
        // Assert and provide functionality for generic join
        assert(m_children.size() == 2);
        for (AbstractPlanNode child : m_children)
        {
            child.generateOutputSchema(db);
        }
        // Join the schema together to form the output schema
        m_outputSchemaPreInlineAgg =
            m_children.get(0).getOutputSchema().
            join(m_children.get(1).getOutputSchema()).copyAndReplaceWithTVE();
        m_hasSignificantOutputSchema = true;

        generateRealOutputSchema();
    }

    protected void generateRealOutputSchema() {
        AggregatePlanNode aggNode = AggregatePlanNode.getInlineAggregationNode(this);
        if (aggNode != null) {
            m_outputSchema = aggNode.getOutputSchema().copyAndReplaceWithTVE();
        } else {
            m_outputSchema = m_outputSchemaPreInlineAgg;
        }
    }

    // Given any non-inlined type of join, this method will resolve the column
    // order and TVE indexes for the output SchemaColumns.
    @Override
    public void resolveColumnIndexes()
    {
        // First, assert that our topology is sane and then
        // recursively resolve all child/inline column indexes
        IndexScanPlanNode index_scan =
            (IndexScanPlanNode) getInlinePlanNode(PlanNodeType.INDEXSCAN);
        assert(m_children.size() == 2 && index_scan == null);
        for (AbstractPlanNode child : m_children)
        {
            child.resolveColumnIndexes();
        }

        final NodeSchema outer_schema = m_children.get(0).getOutputSchema();
        final NodeSchema inner_schema = m_children.get(1).getOutputSchema();

        // resolve predicates
        resolvePredicate(m_preJoinPredicate, outer_schema, inner_schema);
        resolvePredicate(m_joinPredicate, outer_schema, inner_schema);
        resolvePredicate(m_wherePredicate, outer_schema, inner_schema);

        // need to order the combined input schema coherently.  We make the
        // output schema ordered: [outer table columns][inner table columns]
        TreeMap<Integer, SchemaColumn> sort_cols =
            new TreeMap<Integer, SchemaColumn>();
        for (SchemaColumn col : m_outputSchemaPreInlineAgg.getColumns())
        {
            // Right now these all need to be TVEs
            assert(col.getExpression() instanceof TupleValueExpression);
            TupleValueExpression tve = (TupleValueExpression)col.getExpression();
            int index = tve.resolveColumnIndexesUsingSchema(outer_schema);
            if (index == -1)
            {
                index = tve.resolveColumnIndexesUsingSchema(inner_schema);
                if (index == -1)
                {
                    throw new RuntimeException("Unable to find index for column: " +
                                               col.toString());
                }
                sort_cols.put(index + outer_schema.size(), col);
            }
            else
            {
                sort_cols.put(index, col);
            }
            tve.setColumnIndex(index);
        }
        // rebuild the output schema from the tree-sorted columns
        NodeSchema new_output_schema = new NodeSchema();
        for (SchemaColumn col : sort_cols.values())
        {
            new_output_schema.addColumn(col);
        }
        m_outputSchemaPreInlineAgg = new_output_schema;
        m_hasSignificantOutputSchema = true;

        resolveRealOutputSchema();
    }

    protected void resolveRealOutputSchema() {
        AggregatePlanNode aggNode = AggregatePlanNode.getInlineAggregationNode(this);
        if (aggNode != null) {
            aggNode.resolveColumnIndexesUsingSchema(m_outputSchemaPreInlineAgg);
            m_outputSchema = aggNode.getOutputSchema().clone();
        } else {
            m_outputSchema = m_outputSchemaPreInlineAgg;
        }
    }

    public SortDirectionType getSortDirection() {
        return m_sortDirection;
    }

    // TODO: need to extend the sort direction for join from one table to the other table if possible
    // right now, only consider the sort direction on the outer table
    public void resolveSortDirection() {
        AbstractPlanNode outerTable = m_children.get(0);
        if (outerTable.getPlanNodeType() == PlanNodeType.INDEXSCAN) {
            m_sortDirection = ((IndexScanPlanNode)outerTable).getSortDirection();
            return;
        }
        if (outerTable instanceof AbstractJoinPlanNode) {
            ((AbstractJoinPlanNode)outerTable).resolveSortDirection();
            m_sortDirection = ((AbstractJoinPlanNode)outerTable).getSortDirection();
        }
    }

    @Override
    public void toJSONString(JSONStringer stringer) throws JSONException
    {
        super.toJSONString(stringer);
        stringer.key(Members.JOIN_TYPE.name()).value(m_joinType.toString());
        stringer.key(Members.PRE_JOIN_PREDICATE.name()).value(m_preJoinPredicate);
        stringer.key(Members.JOIN_PREDICATE.name()).value(m_joinPredicate);
        stringer.key(Members.WHERE_PREDICATE.name()).value(m_wherePredicate);

        if (m_outputSchemaPreInlineAgg != m_outputSchema) {
            stringer.key(Members.OUTPUT_SCHEMA_PRE_AGG.name());
            stringer.array();
            for (SchemaColumn column : m_outputSchemaPreInlineAgg.getColumns()) {
                column.toJSONString(stringer, true);
            }
            stringer.endArray();
        }
    }

    @Override
    public void loadFromJSONObject( JSONObject jobj, Database db ) throws JSONException
    {
        helpLoadFromJSONObject(jobj, db);
        m_joinType = JoinType.get( jobj.getString( Members.JOIN_TYPE.name() ) );
        m_preJoinPredicate = AbstractExpression.fromJSONChild(jobj, Members.PRE_JOIN_PREDICATE.name());
        m_joinPredicate = AbstractExpression.fromJSONChild(jobj, Members.JOIN_PREDICATE.name());
        m_wherePredicate = AbstractExpression.fromJSONChild(jobj, Members.WHERE_PREDICATE.name());

        if ( !jobj.isNull( Members.OUTPUT_SCHEMA_PRE_AGG.name() ) ) {
            m_outputSchemaPreInlineAgg = new NodeSchema();
            m_hasSignificantOutputSchema = true;
            JSONArray jarray = jobj.getJSONArray( Members.OUTPUT_SCHEMA_PRE_AGG.name() );
            int size = jarray.length();
            for( int i = 0; i < size; i++ ) {
                m_outputSchemaPreInlineAgg.addColumn( SchemaColumn.fromJSONObject(jarray.getJSONObject(i)) );
            }
        } else {
            m_outputSchemaPreInlineAgg = m_outputSchema;
        }
    }


    /**
     *
     * @param expression
     * @param outer_schema
     * @param inner_schema
     */
    protected static void resolvePredicate(AbstractExpression expression,
            NodeSchema outer_schema, NodeSchema inner_schema)
    {
        // Finally, resolve m_predicate
        List<TupleValueExpression> predicate_tves =
                ExpressionUtil.getTupleValueExpressions(expression);
        for (TupleValueExpression tve : predicate_tves)
        {
            int index = tve.resolveColumnIndexesUsingSchema(outer_schema);
            int tableIdx = 0;   // 0 for outer table
            if (index == -1)
            {
                index = tve.resolveColumnIndexesUsingSchema(inner_schema);
                if (index == -1)
                {
                    throw new RuntimeException("Unable to find index for join TVE: " +
                                               tve.toString());
                }
                tableIdx = 1;   // 1 for inner table
            }
            tve.setColumnIndex(index);
            tve.setTableIndex(tableIdx);
        }
    }

    protected static void resolvePredicate(List<AbstractExpression> expressions,
            NodeSchema outer_schema, NodeSchema inner_schema)
    {
        for (AbstractExpression expr : expressions) {
            resolvePredicate(expr, outer_schema, inner_schema);
        }
    }

    protected String explainFilters(String indent) {
        String result = "";
        String prefix = "\n" + indent + " filter by ";
        AbstractExpression[] predicates = { m_preJoinPredicate, m_joinPredicate, m_wherePredicate };
        for (AbstractExpression pred : predicates) {
            if (pred != null) {
                result += prefix + pred.explain("!?"); // No default table name prefix for columns.
                prefix = " AND ";
            }
        }
        return result;
    }

}
TOP

Related Classes of org.voltdb.plannodes.AbstractJoinPlanNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.