diff --git a/relational/optimizer.py b/relational/optimizer.py index 4c0dc1c..1fb2ee1 100644 --- a/relational/optimizer.py +++ b/relational/optimizer.py @@ -25,6 +25,7 @@ from relational import optimizations from relational import parser +from relational import querysplit from relational.maintenance import UserInterface @@ -57,8 +58,8 @@ def optimize_program(code, rels): parsed = parser.tree(query) optimizations.replace_leaves(parsed, context) context[res] = parsed - result = optimize_all(context[last_res], rels) - return result + node = optimize_all(context[last_res], rels, tostr=False) + return querysplit.split(node, rels) def optimize_all(expression, rels, specific=True, general=True, debug=None,tostr=True): diff --git a/relational/querysplit.py b/relational/querysplit.py index a80d07a..d6cd699 100644 --- a/relational/querysplit.py +++ b/relational/querysplit.py @@ -19,6 +19,52 @@ # This module splits a query into a program. +from relational import parser + + +class Program: + def __init__(self, rels): + self.queries = [] + self.dictionary = {} # Key is the query, value is the relation + self.vgen = vargen(rels, 'optm_') + + def __str__(self): + r = '' + for q in self.queries: + r += '%s = %s' % (q[0], q[1]) + '\n' + return r.rstrip() + + def append_query(self, node): + strnode = str(node) + + rel = self.dictionary.get(strnode) + if rel: + return rel + + qname = next(self.vgen) + self.queries.append((qname, node)) + n = parser.Node() + n.kind = parser.RELATION + n.name = qname + self.dictionary[strnode] = n + return n + +def _separate(node, program): + if node.kind == parser.UNARY and node.child.kind != parser.RELATION: + _separate(node.child, program) + rel = program.append_query(node.child) + node.child = rel + elif node.kind == parser.BINARY: + if node.left.kind != parser.RELATION: + _separate(node.left, program) + rel = program.append_query(node.left) + node.left = rel + if node.right.kind != parser.RELATION: + _separate(node.right, program) + rel = program.append_query(node.right) + node.right = rel + program.append_query(node) + def vargen(avoid, prefix=''): ''' Generates temp variables. @@ -40,3 +86,14 @@ def vargen(avoid, prefix=''): if r not in avoid: yield r count += 1 + +def split(node, rels): + ''' + Split a query into a program. + + The idea is that if there are duplicated subdtrees they + get executed only once. + ''' + p = Program(rels) + _separate(node, p) + return str(p) diff --git a/relational_gui/guihandler.py b/relational_gui/guihandler.py index 586e5d5..f6e3a4e 100644 --- a/relational_gui/guihandler.py +++ b/relational_gui/guihandler.py @@ -15,6 +15,7 @@ # along with this program. If not, see . # # author Salvo "LtWorf" Tomaselli +import base64 import sys from PyQt5 import QtCore, QtWidgets, QtGui @@ -360,10 +361,13 @@ class relForm(QtWidgets.QMainWindow): self.settings.setValue('maingui/geometry', self.saveGeometry()) self.settings.setValue('maingui/windowState', self.saveState()) self.settings.setValue('maingui/splitter', self.ui.splitter.saveState()) - self.settings.setValue('maingui/relations', self.user_interface.session_dump()) + self.settings.setValue('maingui/relations', base64.b64encode(self.user_interface.session_dump()).decode()) def _restore_settings(self): - self.user_interface.session_restore(self.settings.value('maingui/relations')) + try: + self.user_interface.session_restore(base64.b64decode(self.settings.value('maingui/relations'))) + except: + pass self.updateRelations() self.setMultiline(self.settings.value('multiline', 'false') == 'true')