|
|
|
@ -34,25 +34,15 @@ from typing import Tuple, Dict
|
|
|
|
|
|
|
|
|
|
from relational.relation import Relation
|
|
|
|
|
from relational import parser
|
|
|
|
|
from relational.parser import Binary, Unary, PRODUCT, \
|
|
|
|
|
DIFFERENCE, UNION, INTERSECTION, DIVISION, JOIN, \
|
|
|
|
|
JOIN_LEFT, JOIN_RIGHT, JOIN_FULL, PROJECTION, \
|
|
|
|
|
SELECTION, RENAME, ARROW
|
|
|
|
|
|
|
|
|
|
sel_op = (
|
|
|
|
|
'//=', '**=', 'and', 'not', 'in', '//', '**', '<<', '>>', '==', '!=', '>=', '<=', '+=', '-=',
|
|
|
|
|
'*=', '/=', '%=', 'or', '+', '-', '*', '/', '&', '|', '^', '~', '<', '>', '%', '=', '(', ')', ',', '[', ']')
|
|
|
|
|
|
|
|
|
|
PRODUCT = parser.PRODUCT
|
|
|
|
|
DIFFERENCE = parser.DIFFERENCE
|
|
|
|
|
UNION = parser.UNION
|
|
|
|
|
INTERSECTION = parser.INTERSECTION
|
|
|
|
|
DIVISION = parser.DIVISION
|
|
|
|
|
JOIN = parser.JOIN
|
|
|
|
|
JOIN_LEFT = parser.JOIN_LEFT
|
|
|
|
|
JOIN_RIGHT = parser.JOIN_RIGHT
|
|
|
|
|
JOIN_FULL = parser.JOIN_FULL
|
|
|
|
|
PROJECTION = parser.PROJECTION
|
|
|
|
|
SELECTION = parser.SELECTION
|
|
|
|
|
RENAME = parser.RENAME
|
|
|
|
|
ARROW = parser.ARROW
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_duplicates(node, dups=None):
|
|
|
|
|
'''
|
|
|
|
@ -72,7 +62,7 @@ def duplicated_select(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
in and
|
|
|
|
|
'''
|
|
|
|
|
changes = 0
|
|
|
|
|
while isinstance(n, parser.Unary) and n.name == SELECTION and isinstance(n.child, parser.Unary) and n.child.name == SELECTION:
|
|
|
|
|
while isinstance(n, Unary) and n.name == SELECTION and isinstance(n.child, Unary) and n.child.name == SELECTION:
|
|
|
|
|
changes += 1
|
|
|
|
|
prop = n.prop
|
|
|
|
|
|
|
|
|
@ -82,7 +72,7 @@ def duplicated_select(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
# This adds parenthesis if they are needed
|
|
|
|
|
if n.child.prop.startswith('(') or n.prop.startswith('('):
|
|
|
|
|
prop = '(%s)' % prop
|
|
|
|
|
n = parser.Unary(
|
|
|
|
|
n = Unary(
|
|
|
|
|
SELECTION,
|
|
|
|
|
prop,
|
|
|
|
|
n.child.child,
|
|
|
|
@ -125,14 +115,14 @@ def futile_union_intersection_subtraction(n: parser.Node) -> Tuple[parser.Node,
|
|
|
|
|
elif n.name == DIFFERENCE and \
|
|
|
|
|
n.right.name == SELECTION and \
|
|
|
|
|
n.right.child == n.left:
|
|
|
|
|
return parser.Unary(
|
|
|
|
|
return Unary(
|
|
|
|
|
SELECTION,
|
|
|
|
|
'(not (%s))' % n.right.prop,
|
|
|
|
|
n.right.child), 1
|
|
|
|
|
|
|
|
|
|
# Subtraction of the same thing or with selection on the left child
|
|
|
|
|
elif n.name == DIFFERENCE and (n.left == n.right or (n.left.name == SELECTION and n.left.child == n.right)):
|
|
|
|
|
return parser.Unary(
|
|
|
|
|
return Unary(
|
|
|
|
|
SELECTION,
|
|
|
|
|
'False',
|
|
|
|
|
n.get_left_leaf()
|
|
|
|
@ -147,12 +137,12 @@ def down_to_unions_subtractions_intersections(n: parser.Node) -> Tuple[parser.No
|
|
|
|
|
'''
|
|
|
|
|
changes = 0
|
|
|
|
|
_o = (UNION, DIFFERENCE, INTERSECTION)
|
|
|
|
|
if isinstance(n, parser.Unary) and n.name == SELECTION and n.child.name in _o:
|
|
|
|
|
assert isinstance(n.child, parser.Binary)
|
|
|
|
|
l = parser.Unary(SELECTION, n.prop, n.child.left)
|
|
|
|
|
r = parser.Unary(SELECTION, n.prop, n.child.right)
|
|
|
|
|
if isinstance(n, Unary) and n.name == SELECTION and n.child.name in _o:
|
|
|
|
|
assert isinstance(n.child, Binary)
|
|
|
|
|
l = Unary(SELECTION, n.prop, n.child.left)
|
|
|
|
|
r = Unary(SELECTION, n.prop, n.child.right)
|
|
|
|
|
|
|
|
|
|
return parser.Binary(n.child.name, l, r), 1
|
|
|
|
|
return Binary(n.child.name, l, r), 1
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -160,8 +150,8 @@ def duplicated_projection(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
'''This function locates thing like π i ( π j (R)) and replaces
|
|
|
|
|
them with π i (R)'''
|
|
|
|
|
|
|
|
|
|
if isinstance(n, parser.Unary) and n.name == PROJECTION and isinstance(n.child, parser.Unary) and n.child.name == PROJECTION:
|
|
|
|
|
return parser.Unary(
|
|
|
|
|
if isinstance(n, Unary) and n.name == PROJECTION and isinstance(n.child, Unary) and n.child.name == PROJECTION:
|
|
|
|
|
return Unary(
|
|
|
|
|
PROJECTION,
|
|
|
|
|
n.prop,
|
|
|
|
|
n.child.child), 1
|
|
|
|
@ -171,14 +161,14 @@ def duplicated_projection(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
def selection_inside_projection(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
'''This function locates things like σ j (π k(R)) and
|
|
|
|
|
converts them into π k(σ j (R))'''
|
|
|
|
|
if isinstance(n, parser.Unary) and n.name == SELECTION and isinstance(n.child, parser.Unary) and n.child.name == PROJECTION:
|
|
|
|
|
child = parser.Unary(
|
|
|
|
|
if isinstance(n, Unary) and n.name == SELECTION and isinstance(n.child, Unary) and n.child.name == PROJECTION:
|
|
|
|
|
child = Unary(
|
|
|
|
|
SELECTION,
|
|
|
|
|
n.prop,
|
|
|
|
|
n.child.child
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return parser.Unary(PROJECTION, n.child.prop, child), 0
|
|
|
|
|
return Unary(PROJECTION, n.child.prop, child), 0
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -192,8 +182,8 @@ def swap_union_renames(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
l_vars = n.left.get_rename_prop()
|
|
|
|
|
r_vars = n.right.get_rename_prop()
|
|
|
|
|
if r_vars == l_vars:
|
|
|
|
|
child = parser.Binary(n.name, n.left.child, n.right.child)
|
|
|
|
|
return parser.Unary(RENAME, n.left.prop, child), 1
|
|
|
|
|
child = Binary(n.name, n.left.child, n.right.child)
|
|
|
|
|
return Unary(RENAME, n.left.prop, child), 1
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -231,7 +221,7 @@ def subsequent_renames(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
# Located two nested renames.
|
|
|
|
|
prop = n.prop + ',' + n.child.prop
|
|
|
|
|
child = n.child.child
|
|
|
|
|
n = parser.Unary(RENAME, prop, child)
|
|
|
|
|
n = Unary(RENAME, prop, child)
|
|
|
|
|
|
|
|
|
|
# Creating a dictionary with the attributes
|
|
|
|
|
renames = n.get_rename_prop()
|
|
|
|
@ -306,7 +296,7 @@ def swap_rename_projection(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
Will also eliminate fields in the rename that are cut in the projection.
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
if isinstance(n, parser.Unary) and n.name == PROJECTION and n.child.name == RENAME:
|
|
|
|
|
if isinstance(n, Unary) and n.name == PROJECTION and n.child.name == RENAME:
|
|
|
|
|
# π index,name(ρ id➡index(R))
|
|
|
|
|
renames = n.child.get_rename_prop()
|
|
|
|
|
projections = set(n.get_projection_prop())
|
|
|
|
@ -322,9 +312,9 @@ def swap_rename_projection(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
if i not in projections:
|
|
|
|
|
del renames[i]
|
|
|
|
|
|
|
|
|
|
child = parser.Unary(PROJECTION,'' , n.child.child)
|
|
|
|
|
child = Unary(PROJECTION,'' , n.child.child)
|
|
|
|
|
child.set_projection_prop(projections)
|
|
|
|
|
n = parser.Unary(RENAME, '', child)
|
|
|
|
|
n = Unary(RENAME, '', child)
|
|
|
|
|
n.set_rename_prop(renames)
|
|
|
|
|
return n, 1
|
|
|
|
|
|
|
|
|
@ -339,7 +329,7 @@ def swap_rename_select(n: parser.Node) -> int:
|
|
|
|
|
Renaming the attributes used in the
|
|
|
|
|
selection, so the operation is still valid.'''
|
|
|
|
|
|
|
|
|
|
if isinstance(n, parser.Unary) and n.name == SELECTION and n.child.name == RENAME:
|
|
|
|
|
if isinstance(n, Unary) and n.name == SELECTION and n.child.name == RENAME:
|
|
|
|
|
# This is an inverse mapping for the rename
|
|
|
|
|
renames = {v: k for k, v in n.child.get_rename_prop().items()}
|
|
|
|
|
|
|
|
|
@ -354,8 +344,8 @@ def swap_rename_select(n: parser.Node) -> int:
|
|
|
|
|
if len(splitted) > 1:
|
|
|
|
|
tokens[i] += '.' + splitted[1]
|
|
|
|
|
|
|
|
|
|
child = parser.Unary(SELECTION, ' '.join(tokens), n.child.child)
|
|
|
|
|
return parser.Unary(RENAME, n.child.prop, child), 1
|
|
|
|
|
child = Unary(SELECTION, ' '.join(tokens), n.child.child)
|
|
|
|
|
return Unary(RENAME, n.child.prop, child), 1
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -389,7 +379,7 @@ def select_union_intersect_subtract(n: parser.Node) -> int:
|
|
|
|
|
prop = t_str % (n.left.prop, op, n.right.prop)
|
|
|
|
|
else:
|
|
|
|
|
prop = '%s %s %s' % (n.left.prop, op, n.right.prop)
|
|
|
|
|
return parser.Unary(SELECTION, prop, n.left.child), 1
|
|
|
|
|
return Unary(SELECTION, prop, n.left.child), 1
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -403,13 +393,13 @@ def union_and_product(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
if n.left.left == n.right.left or n.left.left == n.right.right:
|
|
|
|
|
l = n.left.right
|
|
|
|
|
r = n.right.left if n.left.left == n.right.right else n.right.right
|
|
|
|
|
newchild = parser.Binary(UNION, l, r)
|
|
|
|
|
return parser.Binary(n.left.name, n.left.left, newchild), 1
|
|
|
|
|
newchild = Binary(UNION, l, r)
|
|
|
|
|
return Binary(n.left.name, n.left.left, newchild), 1
|
|
|
|
|
elif n.left.right == n.right.left or n.left.left == n.right.right:
|
|
|
|
|
l = n.left.left
|
|
|
|
|
r = n.right.left if n.right.left == n.right.right else n.right.right
|
|
|
|
|
newchild = parser.Binary(UNION, l, r)
|
|
|
|
|
return parser.Binary(n.left.name, n.left.right, newchild), 1
|
|
|
|
|
newchild = Binary(UNION, l, r)
|
|
|
|
|
return Binary(n.left.name, n.left.right, newchild), 1
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -429,8 +419,8 @@ def projection_and_union(n: parser.Node, rels: Dict[str, Relation]) -> Tuple[par
|
|
|
|
|
n.right.name == PROJECTION and \
|
|
|
|
|
set(n.left.child.result_format(rels)) == set(n.right.child.result_format(rels)):
|
|
|
|
|
|
|
|
|
|
child = parser.Binary(UNION, n.left.child, n.right.child)
|
|
|
|
|
return parser.Unary(PROJECTION, n.right.prop, child), 0
|
|
|
|
|
child = Binary(UNION, n.left.child, n.right.child)
|
|
|
|
|
return Unary(PROJECTION, n.right.prop, child), 0
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -439,7 +429,7 @@ def selection_and_product(n: parser.Node, rels: Dict[str, Relation]) -> parser.N
|
|
|
|
|
σ l (σ j (R) * σ i (Q)). Where j contains only attributes belonging to R,
|
|
|
|
|
i contains attributes belonging to Q and l contains attributes belonging to both'''
|
|
|
|
|
|
|
|
|
|
if isinstance(n, parser.Unary) and n.name == SELECTION and n.child.name in (PRODUCT, JOIN):
|
|
|
|
|
if isinstance(n, Unary) and n.name == SELECTION and n.child.name in (PRODUCT, JOIN):
|
|
|
|
|
l_attr = n.child.left.result_format(rels)
|
|
|
|
|
r_attr = n.child.right.result_format(rels)
|
|
|
|
|
|
|
|
|
@ -484,7 +474,7 @@ def selection_and_product(n: parser.Node, rels: Dict[str, Relation]) -> parser.N
|
|
|
|
|
l_prop = ' and '.join((' '.join(i) for i in left))
|
|
|
|
|
if '(' in l_prop:
|
|
|
|
|
l_prop = '(%s)' % l_prop
|
|
|
|
|
l_node = parser.Unary(SELECTION, l_prop, n.child.left)
|
|
|
|
|
l_node = Unary(SELECTION, l_prop, n.child.left)
|
|
|
|
|
else:
|
|
|
|
|
l_node = n.child.left
|
|
|
|
|
|
|
|
|
@ -493,18 +483,18 @@ def selection_and_product(n: parser.Node, rels: Dict[str, Relation]) -> parser.N
|
|
|
|
|
r_prop = ' and '.join((' '.join(i) for i in right))
|
|
|
|
|
if '(' in r_prop:
|
|
|
|
|
r_prop = '(%s)' % r_prop
|
|
|
|
|
r_node = parser.Unary(SELECTION, r_prop, n.child.right)
|
|
|
|
|
r_node = Unary(SELECTION, r_prop, n.child.right)
|
|
|
|
|
else:
|
|
|
|
|
r_node = n.child.right
|
|
|
|
|
|
|
|
|
|
b_node = parser.Binary(n.child.name, l_node, r_node)
|
|
|
|
|
b_node = Binary(n.child.name, l_node, r_node)
|
|
|
|
|
|
|
|
|
|
# Changing main selection
|
|
|
|
|
if both:
|
|
|
|
|
both_prop = ' and '.join((' '.join(i) for i in both))
|
|
|
|
|
if '(' in both_prop:
|
|
|
|
|
both_prop = '(%s)' % both_prop
|
|
|
|
|
r = parser.Unary(SELECTION, both_prop, b_node)
|
|
|
|
|
r = Unary(SELECTION, both_prop, b_node)
|
|
|
|
|
return r, len(left) + len(right)
|
|
|
|
|
else: # No need for general select
|
|
|
|
|
return b_node, 1
|
|
|
|
@ -516,7 +506,7 @@ def useless_projection(n: parser.Node, rels: Dict[str, Relation]) -> Tuple[parse
|
|
|
|
|
'''
|
|
|
|
|
Removes projections that are over all the fields
|
|
|
|
|
'''
|
|
|
|
|
if isinstance(n, parser.Unary) and n.name == PROJECTION and \
|
|
|
|
|
if isinstance(n, Unary) and n.name == PROJECTION and \
|
|
|
|
|
set(n.child.result_format(rels)) == set(i.strip() for i in n.prop.split(',')):
|
|
|
|
|
return n.child, 1
|
|
|
|
|
|
|
|
|
|