github.com/m3db/m3@v1.5.0/src/query/plan/logical.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package plan
    22  
    23  import (
    24  	"fmt"
    25  
    26  	"github.com/m3db/m3/src/query/parser"
    27  )
    28  
    29  // LogicalPlan converts a DAG into a list of steps to be executed in order
    30  type LogicalPlan struct {
    31  	Steps    map[parser.NodeID]LogicalStep
    32  	Pipeline []parser.NodeID // Ordered list of steps to be performed
    33  }
    34  
    35  // LogicalStep is a single step in a logical plan
    36  type LogicalStep struct {
    37  	Parents   []parser.NodeID
    38  	Children  []parser.NodeID
    39  	Transform parser.Node
    40  }
    41  
    42  // NewLogicalPlan creates a plan from the DAG structure
    43  func NewLogicalPlan(transforms parser.Nodes, edges parser.Edges) (LogicalPlan, error) {
    44  	lp := LogicalPlan{
    45  		Steps:    make(map[parser.NodeID]LogicalStep),
    46  		Pipeline: make([]parser.NodeID, 0, len(transforms)),
    47  	}
    48  
    49  	// Create all steps
    50  	for _, transform := range transforms {
    51  		lp.Steps[transform.ID] = LogicalStep{
    52  			Transform: transform,
    53  			Parents:   make([]parser.NodeID, 0, 1),
    54  			Children:  make([]parser.NodeID, 0, 1),
    55  		}
    56  		lp.Pipeline = append(lp.Pipeline, transform.ID)
    57  	}
    58  
    59  	// Link all parent/children
    60  	for _, edge := range edges {
    61  		parent, ok := lp.Steps[edge.ParentID]
    62  		if !ok {
    63  			return LogicalPlan{}, fmt.Errorf("invalid DAG found, parent %s not found for child %s", edge.ParentID, edge.ChildID)
    64  		}
    65  
    66  		child, ok := lp.Steps[edge.ChildID]
    67  		if !ok {
    68  			return LogicalPlan{}, fmt.Errorf("invalid DAG found, child %s not found for parent %s", edge.ChildID, edge.ParentID)
    69  		}
    70  
    71  		parent.Children = append(parent.Children, child.ID())
    72  		child.Parents = append(child.Parents, parent.ID())
    73  		// Write back since we are doing copy instead reference
    74  		lp.Steps[edge.ParentID] = parent
    75  		lp.Steps[edge.ChildID] = child
    76  	}
    77  
    78  	return lp, nil
    79  }
    80  
    81  func (l LogicalPlan) String() string {
    82  	return fmt.Sprintf("Plan steps: %s, Pipeline: %s", l.Steps, l.Pipeline)
    83  }
    84  
    85  // Clone the plan
    86  func (l LogicalPlan) Clone() LogicalPlan {
    87  	steps := make(map[parser.NodeID]LogicalStep, len(l.Steps))
    88  	for id, step := range l.Steps {
    89  		steps[id] = step.Clone()
    90  	}
    91  
    92  	pipeline := make([]parser.NodeID, len(l.Pipeline))
    93  	copy(pipeline, l.Pipeline)
    94  	return LogicalPlan{
    95  		Steps:    steps,
    96  		Pipeline: pipeline,
    97  	}
    98  }
    99  
   100  func (l LogicalStep) String() string {
   101  	return fmt.Sprintf("Parents: %s, Children: %s, Transform: %s", l.Parents, l.Children, l.Transform)
   102  }
   103  
   104  // ID is a convenience method to expose the inner transforms' ID
   105  func (l LogicalStep) ID() parser.NodeID {
   106  	return l.Transform.ID
   107  }
   108  
   109  // Clone the step, the transform is immutable so its left as is
   110  func (l LogicalStep) Clone() LogicalStep {
   111  	parents := make([]parser.NodeID, len(l.Parents))
   112  	copy(parents, l.Parents)
   113  
   114  	children := make([]parser.NodeID, len(l.Children))
   115  	copy(children, l.Children)
   116  
   117  	return LogicalStep{
   118  		Transform: l.Transform,
   119  		Parents:   parents,
   120  		Children:  children,
   121  	}
   122  }