From 0dcd639c9d3cf7580a595b08c410bff1f446fe78 Mon Sep 17 00:00:00 2001 From: Salvo 'LtWorf' Tomaselli Date: Tue, 9 Jun 2020 11:18:33 +0200 Subject: [PATCH] Enable one optimization Due to the fact that the nodes are different classes, this required some refactor. --- relational/optimizations.py | 57 +++++++++++-------------------------- relational/optimizer.py | 44 ++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/relational/optimizations.py b/relational/optimizations.py index 07909a6..1133907 100644 --- a/relational/optimizations.py +++ b/relational/optimizations.py @@ -30,6 +30,7 @@ from io import StringIO from tokenize import generate_tokens +from typing import Tuple from relational import parser @@ -98,36 +99,7 @@ def replace_node(replace, replacement): replace.left = replacement.left -def recoursive_scan(function, node, rels=None): - '''Does a recoursive optimization on the tree. - - This function will recoursively execute the function given - as "function" parameter starting from node to all the tree. - if rels is provided it will be passed as argument to the function. - Otherwise the function will be called just on the node. - - Result value: function is supposed to return the amount of changes - it has performed on the tree. - The various result will be added up and this final value will be the - returned value.''' - changes = 0 - # recoursive scan - if node.kind == parser.UNARY: - if rels != None: - changes += function(node.child, rels) - else: - changes += function(node.child) - elif node.kind == parser.BINARY: - if rels != None: - changes += function(node.right, rels) - changes += function(node.left, rels) - else: - changes += function(node.right) - changes += function(node.left) - return changes - - -def duplicated_select(n: parser.Node) -> int: +def duplicated_select(n: parser.Node) -> Tuple[parser.Node, int]: '''This function locates and deletes things like σ a ( σ a(C)) and the ones like σ a ( σ b(C)) replacing the 1st one with a single select and @@ -135,19 +107,22 @@ def duplicated_select(n: parser.Node) -> int: in and ''' changes = 0 - if n.name == SELECTION and n.child.name == SELECTION: + while n.name == SELECTION and n.child.name == SELECTION: + changes += 1 + prop = n.prop + if n.prop != n.child.prop: # Nested but different, joining them - n.prop = n.prop + " and " + n.child.prop + prop = n.prop + " and " + n.child.prop # This adds parenthesis if they are needed if n.child.prop.startswith('(') or n.prop.startswith('('): - n.prop = '(%s)' % n.prop - - n.child = n.child.child - changes = 1 - changes += duplicated_select(n) - - return changes + recoursive_scan(duplicated_select, n) + prop = '(%s)' % prop + n = parser.Unary( + SELECTION, + prop, + n.child.child, + ) + return n, changes def futile_union_intersection_subtraction(n: parser.Node) -> int: @@ -706,10 +681,10 @@ def useless_projection(n, rels) -> int: changes = 1 replace_node(n, n.child) - return changes + recoursive_scan(useless_projection, n, rels) + return changes + recursive_scan(useless_projection, n, rels) general_optimizations = [ - #duplicated_select, + duplicated_select, #down_to_unions_subtractions_intersections, #duplicated_projection, #selection_inside_projection, diff --git a/relational/optimizer.py b/relational/optimizer.py index 3e8ccd5..30c24bd 100644 --- a/relational/optimizer.py +++ b/relational/optimizer.py @@ -1,5 +1,5 @@ # Relational -# Copyright (C) 2008-2016 Salvo "LtWorf" Tomaselli +# Copyright (C) 2008-2020 Salvo "LtWorf" Tomaselli # # Relational is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,10 +22,10 @@ # relational query, or it can be a parse tree for a relational expression (ie: class parser.node). # The functions will always return a string with the optimized query, but if a parse tree was provided, # the parse tree itself will be modified accordingly. -from typing import Union, Optional, Dict, Any +from typing import Union, Optional, Dict, Any, Tuple from relational import optimizations -from relational.parser import Node, RELATION, UNARY, BINARY, op_functions, tokenize, tree +from relational.parser import Node, Variable, Unary, Binary, op_functions, tokenize, tree from relational import querysplit from relational.maintenance import UserInterface @@ -88,10 +88,10 @@ def optimize_all(expression: Union[str, Node], rels: ContextDict, specific: bool total += res if general: for i in optimizations.general_optimizations: - res = i(n) # Performs the optimization - if res != 0 and dbg: + n, c = recursive_scan(i, n, None) + if c != 0 and dbg: debug.append(str(n)) - total += res + total += c if tostr: return str(n) else: @@ -117,3 +117,35 @@ def general_optimize(expression): Return value: this will return an optimized version of the expression''' return optimize_all(expression, None, specific=False, general=True) + + +def recursive_scan(function, node, rels) -> Tuple[Node, int]: + '''Does a recursive optimization on the tree. + + This function will recursively execute the function given + as "function" parameter starting from node to all the tree. + if rels is provided it will be passed as argument to the function. + Otherwise the function will be called just on the node. + + Result value: function is supposed to return the amount of changes + it has performed on the tree. + The various result will be added up and this final value will be the + returned value.''' + + args = [] + if rels: + args.append(rels) + + changes = 0 + node, c = function(node, *args) + changes += c + + if isinstance(node, Unary): + node.child, c = recursive_scan(function, node.child, rels) + changes += c + elif isinstance(node, Binary): + node.left, c = recursive_scan(function, node.left, rels) + changes += c + node.right, c = recursive_scan(function, node.right, rels) + changes += c + return node, changes