1
1
class Operation :
2
2
"""
3
- Operation, single operation of execution plan.
3
+ Operation, single operation within execution plan.
4
4
"""
5
+
5
6
def __init__ (self , name , args = None ):
6
7
"""
7
8
Create a new operation.
8
9
9
10
Args:
10
11
name: string that represents the name of the operation
11
- args: coperation arguments
12
+ args: operation arguments
12
13
"""
13
14
self .name = name
14
15
self .args = args
@@ -21,58 +22,99 @@ def append_child(self, child):
21
22
self .children .append (child )
22
23
return self
23
24
25
+ def child_count (self ):
26
+ return array_len (self .children )
27
+
24
28
def __eq__ (self , o : object ) -> bool :
25
29
if not isinstance (o , Operation ):
26
30
return False
27
31
28
- if self .name != o .name or self .args != o .args or len ( self . children ) != len ( o . children ) :
32
+ if self .name != o .name or self .args != o .args :
29
33
return False
30
34
31
- for i in range (len (self .children )):
32
- if not self .children [i ] == o .children [i ]:
33
- return False
34
-
35
35
return True
36
36
37
37
def __str__ (self ) -> str :
38
38
args_str = "" if self .args is None else f" | { self .args } "
39
39
return f"{ self .name } { args_str } "
40
40
41
-
42
41
class ExecutionPlan :
43
42
"""
44
43
ExecutionPlan, collection of operations.
45
44
"""
45
+
46
46
def __init__ (self , plan ):
47
47
"""
48
48
Create a new execution plan.
49
49
50
50
Args:
51
51
plan: array of strings that represents the collection operations
52
+ the output from GRAPH.EXPLAIN
52
53
"""
53
54
if not isinstance (plan , list ):
54
- raise Exception ("plan must be array" )
55
+ raise Exception ("plan must be an array" )
55
56
56
57
self .plan = plan
57
58
self .structured_plan = self ._operation_tree ()
58
59
59
- def _operation_traverse (self , op , op_f , aggregate_f , combine_f ):
60
- child_res = op_f (op )
61
- if len (op .children ) == 0 :
62
- return child_res
63
- else :
64
- children = [self ._operation_traverse (op , op_f , aggregate_f , combine_f ) for op in op .children ]
65
- return combine_f (child_res , aggregate_f (children ))
60
+ def _compare_operations (self , root_a , root_b ):
61
+ """
62
+ Compare execution plan operation tree
63
+
64
+ Return: True if operation trees are equal, False otherwise
65
+ """
66
+
67
+ # compare current root
68
+ if root_a != root_b :
69
+ return False
70
+
71
+ # make sure root have the same number of children
72
+ if root_a .child_count () != root_b .child_count ():
73
+ return False
74
+
75
+ # recursively compare children
76
+ for i in range (root_a .child_count ()):
77
+ if not self ._compare_operations (root_a .children [i ], root_b .children [i ]):
78
+ return False
79
+
80
+ return True
66
81
67
82
def __str__ (self ) -> str :
68
- def aggraget_str (str_ops ):
69
- return "\n " .join ([" " + line for str_op in str_ops for line in str_op .splitlines ()])
83
+ def aggraget_str (str_children ):
84
+ return "\n " .join ([" " + line for str_child in str_children for line in str_child .splitlines ()])
70
85
71
86
def combine_str (x , y ):
72
87
return f"{ x } \n { y } "
88
+
73
89
return self ._operation_traverse (self .structured_plan , str , aggraget_str , combine_str )
74
90
91
+ def __eq__ (self , o : object ) -> bool :
92
+ """ Compares two execution plans
93
+
94
+ Return: True if the two plans are equal False otherwise
95
+ """
96
+ # make sure 'o' is an execution-plan
97
+ if not isinstance (o , ExecutionPlan ):
98
+ return False
99
+
100
+ # get root for both plans
101
+ root_a = self .structured_plan
102
+ root_b = o .structured_plan
103
+
104
+ # compare execution trees
105
+ return self ._compare_operations (root_a , root_b )
106
+
107
+ def _operation_traverse (self , op , op_f , aggregate_f , combine_f ):
108
+ # TODO: document function and its arguments
109
+ child_res = op_f (op )
110
+ if len (op .children ) == 0 :
111
+ return child_res
112
+ else :
113
+ children = [self ._operation_traverse (child , op_f , aggregate_f , combine_f ) for child in op .children ]
114
+ return combine_f (child_res , aggregate_f (children ))
115
+
75
116
def _operation_tree (self ):
117
+ # TODO: document!
76
118
i = 0
77
119
level = 0
78
120
stack = []
@@ -101,3 +143,4 @@ def _operation_tree(self):
101
143
else :
102
144
raise Exception ("corrupted plan" )
103
145
return stack [0 ]
146
+
0 commit comments