Package org.voltdb.plannodes

Source Code of org.voltdb.plannodes.AbstractPlanNode

/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB L.L.C.
*
* VoltDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VoltDB 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.voltdb.plannodes;

import java.util.*;
import java.util.Map.Entry;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONString;
import org.json.JSONStringer;
import org.json.JSONException;
import org.voltdb.catalog.Cluster;
import org.voltdb.catalog.Database;
import org.voltdb.compiler.DatabaseEstimates;
import org.voltdb.compiler.ScalarValueHints;
import org.voltdb.planner.PlanAssembler;
import org.voltdb.planner.PlanColumn;
import org.voltdb.planner.PlanStatistics;
import org.voltdb.planner.PlannerContext;
import org.voltdb.planner.StatsField;
import org.voltdb.types.*;

import edu.brown.plannodes.PlanNodeUtil;
import edu.brown.utils.ClassUtil;

public abstract class AbstractPlanNode implements JSONString, Cloneable, Comparable<AbstractPlanNode> {

    public enum Members {
        ID,
        PLAN_NODE_TYPE,
        INLINE_NODES,
        CHILDREN_IDS,
        PARENT_IDS,
        OUTPUT_COLUMNS,
        IS_INLINE,
    }

    private int m_id = -1;
    protected List<AbstractPlanNode> m_children = new ArrayList<AbstractPlanNode>();
    protected List<AbstractPlanNode> m_parents = new ArrayList<AbstractPlanNode>();
    protected HashSet<AbstractPlanNode> m_dominators = new HashSet<AbstractPlanNode>();

    // PAVLO: We need this figure out how to reconstruct the tree
    protected List<Integer> m_childrenIds = new ArrayList<Integer>();
    protected List<Integer> m_parentIds = new ArrayList<Integer>();
   
    // TODO: planner accesses this data directly. Should be protected.
    protected ArrayList<Integer> m_outputColumns = new ArrayList<Integer>();
    protected List<ScalarValueHints> m_outputColumnHints = new ArrayList<ScalarValueHints>();
    protected long m_estimatedOutputTupleCount = 0;

    /**
     * Some PlanNodes can take advantage of inline PlanNodes to perform
     * certain additional tasks while performing their main operation, rather than
     * having to re-read tuples from intermediate results
     */
    protected Map<PlanNodeType, AbstractPlanNode> m_inlineNodes = new HashMap<PlanNodeType, AbstractPlanNode>();
    protected boolean m_isInline = false;

    protected final PlannerContext m_context;

    /**
     * Instantiates a new plan node.
     *
     * @param id the id
     */
    protected AbstractPlanNode(PlannerContext context, int id) {
        assert(context != null);
        assert(id != 0);
        m_context = context;
        m_id = id;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof AbstractPlanNode) {
            AbstractPlanNode other = (AbstractPlanNode)obj;
            return (m_id == other.m_id &&
                    m_isInline == other.m_isInline &&
                    this.getPlanNodeType() == other.getPlanNodeType() &&
                    this.getOutputColumnGUIDs().equals(other.getOutputColumnGUIDs()));
        }
        return (false);
    }
   
    protected final int getId() {
        return (m_id);
    }
   
    @Override
    public final Object clone() throws CloneNotSupportedException {
        return (this.clone(true, true));
    }
   
    public Object clone(boolean clone_children, boolean clone_inline) throws CloneNotSupportedException {
        AbstractPlanNode clone = (AbstractPlanNode)super.clone();
        clone.overrideId(PlanAssembler.getNextPlanNodeId());
       
        clone.m_children = new ArrayList<AbstractPlanNode>();
        clone.m_parents = new ArrayList<AbstractPlanNode>();
        clone.m_dominators = new HashSet<AbstractPlanNode>(m_dominators);
        clone.m_childrenIds = new ArrayList<Integer>();
        clone.m_outputColumns = new ArrayList<Integer>(m_outputColumns);
        clone.m_outputColumnHints = new ArrayList<ScalarValueHints>(m_outputColumnHints);
        clone.m_inlineNodes = new HashMap<PlanNodeType, AbstractPlanNode>();
       
        // Clone Children
        if (clone_children) {
//            clone.m_children.clear();
//            clone.m_childrenIds.clear();
            for (AbstractPlanNode child_node : this.m_children) {
                AbstractPlanNode child_clone = (AbstractPlanNode)child_node.clone(clone_inline, clone_children);
                child_clone.m_parents.clear();
                child_clone.m_parentIds.clear();
                child_clone.m_parents.add(clone);
                child_clone.m_parentIds.add(clone.m_id);
                clone.m_children.add(child_clone);
                clone.m_childrenIds.add(child_clone.m_id);
            } // FOR
        }
       
        // Clone Inlines
        if (clone_inline) {
//            clone.m_inlineNodes.clear();
            for (Entry<PlanNodeType, AbstractPlanNode> e : this.m_inlineNodes.entrySet()) {
                AbstractPlanNode inline_clone = (AbstractPlanNode)e.getValue().clone(clone_inline, clone_children);
                clone.m_inlineNodes.put(e.getKey(), inline_clone);
            } // FOR
        }
       
        return (clone);
    }
   
    public void overrideId(int newId) {
        m_id = newId;
    }

    /**
     * Create a PlanNode that clones the configuration information but
     * is not inserted in the plan graph and has a unique plan node id.
     */
    protected void produceCopyForTransformation(AbstractPlanNode copy) {
        for (Integer colGuid : m_outputColumns) {
            copy.m_outputColumns.add(colGuid);
        }
        copy.m_outputColumnHints.addAll(m_outputColumnHints);
        copy.m_estimatedOutputTupleCount = m_estimatedOutputTupleCount;

        // clone is not yet implemented for every node.
        assert(m_inlineNodes.size() == 0);
        assert(m_isInline == false);

        // the api requires the copy is not (yet) connected
        assert (copy.m_parents.size() == 0);
        assert (copy.m_children.size() == 0);
    }


    public abstract PlanNodeType getPlanNodeType();

    public void setOutputColumns(Collection<Integer> col_guids) {
        this.m_outputColumns.clear();
        this.m_outputColumns.addAll(col_guids);
    }
   
    public boolean updateOutputColumns(Database db) {
        //System.out.println("updateOutputColumns Node type: " + this.getPlanNodeType() + " # of inline nodes: " + this.getInlinePlanNodes().size());

        ArrayList<Integer> childCols = new ArrayList<Integer>();
        for (AbstractPlanNode child : m_children) {
            boolean result = child.updateOutputColumns(db);
            assert(result);
           
            // print child inline columns
//            for (Integer out : child.m_outputColumns)
//            {
//              System.out.println(m_context.get(out).displayName());
//            }
           
            childCols.addAll(child.m_outputColumns);
        }

        ArrayList<Integer> new_output_cols = new ArrayList<Integer>();
        new_output_cols = createOutputColumns(db, childCols);
        for (AbstractPlanNode child : m_inlineNodes.values()) {
            if (child instanceof IndexScanPlanNode)
                continue;
            new_output_cols = child.createOutputColumns(db, new_output_cols);
        }

        // Before we wipe out the old column list, free any PlanColumns that
        // aren't getting reused
        for (Integer col : m_outputColumns)
        {
            if (!new_output_cols.contains(col))
            {
                m_context.freeColumn(col);
            }
        }

        m_outputColumns = new_output_cols;

        return true;
    }

    /** By default, a plan node does not alter its input schema */
    @SuppressWarnings("unchecked")
    protected ArrayList<Integer> createOutputColumns(Database db, ArrayList<Integer> input) {
        return (ArrayList<Integer>)input.clone();
    }

    /**
     * Get number of output columns for this node
     * @return
     */
    public int getOutputColumnGUIDCount() {
        return (this.m_outputColumns.size());
    }
   
    /**
     * Return the PlanColumn GUID at the given offset
     * @param idx
     * @return
     */
    public int getOutputColumnGUID(int idx) {
        return (this.m_outputColumns.get(idx));
    }
   
    /**
     * Get the list of the Output PlanColumn GUIDs
     * @return
     */
    public List<Integer> getOutputColumnGUIDs() {
        return (this.m_outputColumns);
    }
   
    public PlanColumn findMatchingOutputColumn(String tableName,
                                               String columnName,
                                               String columnAlias)
    {
        boolean found = false;
        PlanColumn retval = null;
        for (Integer colguid : m_outputColumns) {
            PlanColumn plancol = m_context.get(colguid);
            if ((plancol.originTableName().equals(tableName)) &&
                ((plancol.originColumnName().equals(columnName)) ||
                 (plancol.originColumnName().equals(columnAlias))))
            {
                found = true;
                retval = plancol;
                break;
            }
        }
        if (!found) {
            assert(found) : "Found no candidate output column.";
            throw new RuntimeException("Found no candidate output column.");
        }
        return retval;
    }

    public void validate() throws Exception {
        //
        // Make sure our children have us listed as their parents
        //
        for (AbstractPlanNode child : m_children) {
            if (!child.m_parents.contains(this)) {
                throw new Exception("ERROR: The child PlanNode '" + child.toString() + "' does not " +
                                    "have its parent PlanNode '" + toString() + "' in its parents list");
            }
            child.validate();
        }
        //
        // Inline PlanNodes
        //
        if (!m_inlineNodes.isEmpty()) {
            for (AbstractPlanNode node : m_inlineNodes.values()) {
                //
                // Make sure that we're not attached to some kind of tree somewhere...
                //
                if (!node.m_children.isEmpty()) {
                    throw new Exception("ERROR: The inline PlanNode '" + node + "' has children inside of PlanNode '" + this + "'");
                } else if (!node.m_parents.isEmpty()) {
                    throw new Exception("ERROR: The inline PlanNode '" + node + "' has parents inside of PlanNode '" + this + "'");
                } else if (!node.isInline()) {
                    throw new Exception("ERROR: The inline PlanNode '" + node + "' was not marked as inline for PlanNode '" + this + "'");
                } else if (!node.getInlinePlanNodes().isEmpty()) {
                    throw new Exception("ERROR: The inline PlanNode '" + node + "' has its own inline PlanNodes inside of PlanNode '" + this + "'");
                }
                node.validate();
            }
        }
    }

    @Override
    public final String toString() {
        return String.format("%s[#%02d]", getPlanNodeType().toString(), m_id);
    }

    public boolean computeEstimatesRecursively(PlanStatistics stats, Cluster cluster, Database db, DatabaseEstimates estimates, ScalarValueHints[] paramHints) {
        assert(estimates != null);

        m_outputColumnHints.clear();
        m_estimatedOutputTupleCount = 0;

        // recursively compute and collect stats from children
        for (AbstractPlanNode child : m_children) {
            boolean result = child.computeEstimatesRecursively(stats, cluster, db, estimates, paramHints);
            assert(result);
            m_outputColumnHints.addAll(child.m_outputColumnHints);
            m_estimatedOutputTupleCount += child.m_estimatedOutputTupleCount;

            stats.incrementStatistic(0, StatsField.TUPLES_READ, m_estimatedOutputTupleCount);
        }

        return true;
    }

    /**
     * Gets the id.
     *
     * @return the id
     */
    public Integer getPlanNodeId() {
        return m_id;
    }

    /**
     * Add a plan node as a child of this node and link this node as it's parent.
     * @param child The node to add.
     */
    public void addAndLinkChild(AbstractPlanNode child) {
        m_children.add(child);
        child.m_parents.add(this);
    }

    /** Remove child from this node.
     * @param child to remove.
     */
    public void unlinkChild(AbstractPlanNode child) {
        m_children.remove(child);
        child.m_parents.remove(this);
    }

    /**
     * Gets the children.
     * @return the children
     */
    public int getChildPlanNodeCount() {
        return m_children.size();
    }

    /**
     * @param index
     * @return The child node of this node at a given index or null if none exists.
     */
    public AbstractPlanNode getChild(int index) {
        return m_children.get(index);
    }
   
    /**
     * Gets all of the children of this node
     * @return
     */
    public List<AbstractPlanNode> getChildren() {
        return (Collections.unmodifiableList(m_children));
    }

    public void clearChildren() {
        m_children.clear();
        m_childrenIds.clear();
    }

    public boolean hasChild(AbstractPlanNode receive) {
        return m_children.contains(receive);
    }

    /**
     * Gets the number of parents.
     * @return the parents
     */
    public int getParentPlanNodeCount() {
        return m_parents.size();
    }

    public AbstractPlanNode getParent(int index) {
        return m_parents.get(index);
    }
   
    /**
     * Gets all of the parents of this node
     * @return
     */
    public List<AbstractPlanNode> getParents() {
        return (Collections.unmodifiableList(m_parents));
    }

    public void clearParents() {
        m_parents.clear();
        m_parentIds.clear();
    }

    public void removeFromGraph() {
       for (AbstractPlanNode parent : m_parents)
           parent.m_children.remove(this);
       for (AbstractPlanNode child : m_children)
           child.m_parents.remove(this);

       m_parents.clear();
       m_children.clear();
    }

    /** Interject the provided node between this node and this node's current children */
    public void addIntermediary(AbstractPlanNode node) {

        // transfer this node's children to node
        Iterator<AbstractPlanNode> it = m_children.iterator();
        while (it.hasNext()) {
            AbstractPlanNode child = it.next();
            it.remove();                          // remove this.child from m_children
            assert(child.getParentPlanNodeCount() == 1) :
                String.format("Expected %s to have only one parent but it has %s", child, child.getParents());
            child.clearParents();                 // and reset child's parents list
            node.addAndLinkChild(child);          // set node.child and child.parent
        }

        // and add node to this node's children
        assert(m_children.size() == 0);
        addAndLinkChild(node);
    }

    /**
     * @return The map of inlined nodes.
     */
    public Map<PlanNodeType, AbstractPlanNode> getInlinePlanNodes() {
        return m_inlineNodes;
    }
   
    public int getInlinePlanNodeCount() {
        return (m_inlineNodes.size());
    }

    /**
     * @param node
     */
    public void addInlinePlanNode(AbstractPlanNode node) {
        node.m_isInline = true;
        m_inlineNodes.put(node.getPlanNodeType(), node);
        node.m_children.clear();
        node.m_parents.clear();
    }

    /**
     *
     * @param type
     */
    public void removeInlinePlanNode(PlanNodeType type) {
        if (m_inlineNodes.containsKey(type)) {
            m_inlineNodes.remove(type);
        }
    }

    /**
     *
     * @param type
     * @return An inlined node of the given type or null if none.
     */
    @SuppressWarnings("unchecked")
    public <T extends AbstractPlanNode> T getInlinePlanNode(PlanNodeType type) {
        return (T)m_inlineNodes.get(type);
    }
   
    /**
     * Return all of the inline AbstractPlanNodes with the same class
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T extends AbstractPlanNode> Collection<T> getInlinePlanNodes(Class<T> clazz) {
        Set<T> ret = new HashSet<T>();
        for (AbstractPlanNode inline : this.m_inlineNodes.values()) {
            if (ClassUtil.getSuperClasses(inline.getClass()).contains(clazz)) {
                ret.add((T)inline);
            }
        } // FOR
        return (ret);
    }

    /**
     *
     * @return Is this node inlined in another node.
     */
    public Boolean isInline() {
        return m_isInline;
    }


    /**
     * @return the dominator list for a node
     */
    public HashSet<AbstractPlanNode> getDominators() {
        return m_dominators;
    }

    /**
    *   Initialize a hashset for each node containing that node's dominators
    *   (the set of predecessors that *always* precede this node in a traversal
    *   of the plan-graph in reverse-execution order (from root to leaves)).
    */
    public void calculateDominators() {
        HashSet<AbstractPlanNode> visited = new HashSet<AbstractPlanNode>();
        calculateDominators_recurse(visited);
    }

    private void calculateDominators_recurse(HashSet<AbstractPlanNode> visited) {
        if (visited.contains(this)) {
            assert(false): "do not expect loops in plangraph.";
            return;
        }

        visited.add(this);
        m_dominators.clear();
        m_dominators.add(this);

        // find nodes that are in every parent's dominator set.

        HashMap<AbstractPlanNode, Integer> union = new HashMap<AbstractPlanNode, Integer>();
        for (AbstractPlanNode n : m_parents) {
            for (AbstractPlanNode d : n.getDominators()) {
                if (union.containsKey(d))
                    union.put(d, union.get(d) + 1);
                else
                    union.put(d, 1);
            }
        }

        for (AbstractPlanNode pd : union.keySet() ) {
            if (union.get(pd) == m_parents.size())
                m_dominators.add(pd);
        }

        for (AbstractPlanNode n : m_children)
            n.calculateDominators_recurse(visited);
    }

    /**
     * @param type plan node type to search for
     * @return a list of nodes that are eventual successors of this node of the desired type
     */
    public List<AbstractPlanNode> findAllNodesOfType(PlanNodeType type) {
        HashSet<AbstractPlanNode> visited = new HashSet<AbstractPlanNode>();
        ArrayList<AbstractPlanNode> collected = new ArrayList<AbstractPlanNode>();
        findAllNodesOfType_recurse(type, collected, visited);
        return collected;
    }

    public void findAllNodesOfType_recurse(PlanNodeType type,ArrayList<AbstractPlanNode> collected,
        HashSet<AbstractPlanNode> visited)
    {
        if (visited.contains(this)) {
            assert(false): "do not expect loops in plangraph.";
            return;
        }
        visited.add(this);
        if (getPlanNodeType() == type)
            collected.add(this);

        for (AbstractPlanNode n : m_children)
            n.findAllNodesOfType_recurse(type, collected, visited);
    }

    public void freeColumns(Set<Integer> skip) {
        Collection<Integer> guids = PlanNodeUtil.getAllPlanColumnGuids(this);
        guids.removeAll(skip);
        for (Integer guid : guids) {
            m_context.freeColumn(guid);
        } // FOR
    }

    @Override
    public int compareTo(AbstractPlanNode other) {
        int diff = 0;

        // compare child nodes
        HashMap<Integer, AbstractPlanNode> nodesById = new HashMap<Integer, AbstractPlanNode>();
        for (AbstractPlanNode node : m_children)
            nodesById.put(node.getPlanNodeId(), node);
        for (AbstractPlanNode node : other.m_children) {
            AbstractPlanNode myNode = nodesById.get(node.getPlanNodeId());
            diff = myNode.compareTo(node);
            if (diff != 0) return diff;
        }

        // compare inline nodes
        HashMap<Integer, Entry<PlanNodeType, AbstractPlanNode>> inlineNodesById =
               new HashMap<Integer, Entry<PlanNodeType, AbstractPlanNode>>();
        for (Entry<PlanNodeType, AbstractPlanNode> e : m_inlineNodes.entrySet())
            inlineNodesById.put(e.getValue().getPlanNodeId(), e);
        for (Entry<PlanNodeType, AbstractPlanNode> e : other.m_inlineNodes.entrySet()) {
            Entry<PlanNodeType, AbstractPlanNode> myE = inlineNodesById.get(e.getValue().getPlanNodeId());
            if (myE.getKey() != e.getKey()) return -1;

            diff = myE.getValue().compareTo(e.getValue());
            if (diff != 0) return diff;
        }

        diff = m_id - other.m_id;
        return diff;
    }

    // produce a file that can imported into graphviz for easier visualization
    public String toDOTString() {
        StringBuilder sb = new StringBuilder();

        // id [label=id: value-type <value-type-attributes>];
        // id -> child_id;
        // id -> child_id;

        sb.append(m_id).append(" [label=\"").append(m_id).append(": ").append(getPlanNodeType()).append("\" ");
        sb.append(getValueTypeDotString(this));
        sb.append("];\n");
        for (AbstractPlanNode node : m_inlineNodes.values()) {
            sb.append(m_id).append(" -> ").append(node.getPlanNodeId().intValue()).append(";\n");
            sb.append(node.toDOTString());
        }
        for (AbstractPlanNode node : m_children) {
           sb.append(m_id).append(" -> ").append(node.getPlanNodeId().intValue()).append(";\n");
        }

        return sb.toString();
    }

    // maybe not worth polluting
    private String getValueTypeDotString(AbstractPlanNode pn) {
        PlanNodeType pnt = pn.getPlanNodeType();
        if (pn.isInline()) {
            return "fontcolor=\"white\" style=\"filled\" fillcolor=\"red\"";
        }
        if (pnt == PlanNodeType.SEND || pnt == PlanNodeType.RECEIVE) {
            return "fontcolor=\"white\" style=\"filled\" fillcolor=\"black\"";
        }
        return "";
    }

    @Override
    public String toJSONString() {
        JSONStringer stringer = new JSONStringer();
        try
        {
            stringer.object();
            toJSONString(stringer);
            stringer.endObject();
        }
        catch (JSONException e)
        {
            throw new RuntimeException("Failed to serialize " + this, e);
//            System.exit(-1);
        }
        return stringer.toString();
    }

    public void toJSONString(JSONStringer stringer) throws JSONException {
        stringer.key(Members.ID.name()).value(m_id);
        stringer.key(Members.PLAN_NODE_TYPE.name()).value(getPlanNodeType().toString());

        stringer.key(Members.IS_INLINE.name()).value(m_isInline);
        stringer.key(Members.INLINE_NODES.name()).array();
        PlanNodeType types[] = new PlanNodeType[m_inlineNodes.size()];
        int i = 0;
        for (PlanNodeType type : m_inlineNodes.keySet()) {
            types[i++] = type;
        }
        Arrays.sort(types);
        for (PlanNodeType type : types) {
            AbstractPlanNode node = m_inlineNodes.get(type);
            assert(node != null);
            assert(node instanceof JSONString);
            stringer.value(node);
        }
        /*for (Map.Entry<PlanNodeType, AbstractPlanNode> entry : m_inlineNodes.entrySet()) {
            assert (entry.getValue() instanceof JSONString);
            stringer.value(entry.getValue());
        }*/
        stringer.endArray();
        stringer.key(Members.CHILDREN_IDS.name()).array();
        for (AbstractPlanNode node : m_children) {
            stringer.value(node.getPlanNodeId().intValue());
        }
        stringer.endArray().key(Members.PARENT_IDS.name()).array();
        for (AbstractPlanNode node : m_parents) {
            stringer.value(node.getPlanNodeId().intValue());
        }
        stringer.endArray(); //end inlineNodes

        stringer.key(Members.OUTPUT_COLUMNS.name());
        stringer.array();
        for (int col = 0; col < m_outputColumns.size(); col++) {
            PlanColumn column = m_context.get(m_outputColumns.get(col));
            column.toJSONString(stringer);
        }
        stringer.endArray();
    }
   
    abstract protected void loadFromJSONObject(JSONObject obj, Database db) throws JSONException;
   
    public static AbstractPlanNode fromJSONObject(JSONObject obj, Database db) throws JSONException {
        PlanNodeType pnt = PlanNodeType.valueOf(obj.getString(Members.PLAN_NODE_TYPE.name()));
       
        AbstractPlanNode node = null;
        try {
            node = (AbstractPlanNode)ClassUtil.newInstance(pnt.getPlanNodeClass(),
                                                  new Object[]{ PlannerContext.singleton(), 1 },
                                                  new Class[]{  PlannerContext.class, Integer.class });
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        node.m_id = obj.getInt(Members.ID.name());
        node.m_isInline = obj.getBoolean(Members.IS_INLINE.name());
       
        JSONArray inlineNodes = obj.getJSONArray(Members.INLINE_NODES.name());
        for (int ii = 0; ii < inlineNodes.length(); ii++) {
            JSONObject inobj = inlineNodes.getJSONObject(ii);
            AbstractPlanNode inlineNode = AbstractPlanNode.fromJSONObject(inobj, db);
            node.m_inlineNodes.put(inlineNode.getPlanNodeType(), inlineNode);
        }
       
        JSONArray childrenIds = obj.getJSONArray(Members.CHILDREN_IDS.name());
        for (int ii = 0; ii < childrenIds.length(); ii++) {
            node.m_childrenIds.add(childrenIds.getInt(ii));
        }
       
        JSONArray parentIds = obj.getJSONArray(Members.PARENT_IDS.name());
        for (int ii = 0; ii < parentIds.length(); ii++) {
            node.m_parentIds.add(parentIds.getInt(ii));
        }
       
        JSONArray outputColumns = obj.getJSONArray(Members.OUTPUT_COLUMNS.name());
        for (int ii = 0; ii < outputColumns.length(); ii++) {
            JSONObject jsonObject = outputColumns.getJSONObject(ii);
            PlanColumn column = PlanColumn.fromJSONObject(jsonObject, db);
            assert(column != null);
            node.m_outputColumns.add(column.guid());
//            System.err.println(String.format("[%02d] %s => %s", ii, node, column));
        }
       
        node.loadFromJSONObject(obj, db);
       
        return node;
    }
}
TOP

Related Classes of org.voltdb.plannodes.AbstractPlanNode

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.