|
|
|
@ -30,29 +30,19 @@
|
|
|
|
|
|
|
|
|
|
from io import StringIO
|
|
|
|
|
from tokenize import generate_tokens
|
|
|
|
|
from typing import Tuple, Dict
|
|
|
|
|
from typing import Tuple, Dict, List
|
|
|
|
|
|
|
|
|
|
from relational.relation import Relation
|
|
|
|
|
from relational import parser
|
|
|
|
|
from relational.parser import Binary, Unary, Node, 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):
|
|
|
|
|
'''
|
|
|
|
@ -64,41 +54,6 @@ def find_duplicates(node, dups=None):
|
|
|
|
|
dups[str(node)] = node
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def replace_leaves(node, context):
|
|
|
|
|
'''
|
|
|
|
|
Node is a parsed tree
|
|
|
|
|
context is a dictionary containing
|
|
|
|
|
parsed trees as values.
|
|
|
|
|
|
|
|
|
|
If a name appearing in node appears
|
|
|
|
|
also in context, the parse tree is
|
|
|
|
|
modified to replace the node with the
|
|
|
|
|
subtree found in context.
|
|
|
|
|
'''
|
|
|
|
|
if node.kind == parser.UNARY:
|
|
|
|
|
replace_leaves(node.child, context)
|
|
|
|
|
elif node.kind == parser.BINARY:
|
|
|
|
|
replace_leaves(node.left, context)
|
|
|
|
|
replace_leaves(node.right, context)
|
|
|
|
|
elif node.name in context:
|
|
|
|
|
replace_node(node, context[node.name])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def replace_node(replace, replacement):
|
|
|
|
|
'''This function replaces "replace" node with the node "with",
|
|
|
|
|
the father of the node will now point to the with node'''
|
|
|
|
|
replace.name = replacement.name
|
|
|
|
|
replace.kind = replacement.kind
|
|
|
|
|
|
|
|
|
|
if replace.kind == parser.UNARY:
|
|
|
|
|
replace.child = replacement.child
|
|
|
|
|
replace.prop = replacement.prop
|
|
|
|
|
elif replace.kind == parser.BINARY:
|
|
|
|
|
replace.right = replacement.right
|
|
|
|
|
replace.left = replacement.left
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
@ -107,7 +62,7 @@ def duplicated_select(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
in and
|
|
|
|
|
'''
|
|
|
|
|
changes = 0
|
|
|
|
|
while n.name == SELECTION 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
|
|
|
|
|
|
|
|
|
@ -117,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,
|
|
|
|
@ -136,38 +91,45 @@ def futile_union_intersection_subtraction(n: parser.Node) -> Tuple[parser.Node,
|
|
|
|
|
σ k (R) ∩ R --> σ k (R)
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
changes = 0
|
|
|
|
|
if not isinstance(n, Binary):
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
# Union and intersection of the same thing
|
|
|
|
|
if n.name in (UNION, INTERSECTION, JOIN, JOIN_LEFT, JOIN_RIGHT, JOIN_FULL) and n.left == n.right:
|
|
|
|
|
return n.left, 1
|
|
|
|
|
|
|
|
|
|
# selection and union of the same thing
|
|
|
|
|
elif (n.name == UNION):
|
|
|
|
|
if n.left.name == SELECTION and n.left.child == n.right:
|
|
|
|
|
elif n.name == UNION:
|
|
|
|
|
if n.left.name == SELECTION and isinstance(n.left, Unary) and n.left.child == n.right:
|
|
|
|
|
return n.right, 1
|
|
|
|
|
elif n.right.name == SELECTION and n.right.child == n.left:
|
|
|
|
|
elif n.right.name == SELECTION and isinstance(n.right, Unary) and n.right.child == n.left:
|
|
|
|
|
return n.left, 1
|
|
|
|
|
|
|
|
|
|
# selection and intersection of the same thing
|
|
|
|
|
elif n.name == INTERSECTION:
|
|
|
|
|
if n.left.name == SELECTION and n.left.child == n.right:
|
|
|
|
|
if n.left.name == SELECTION and isinstance(n.left, Unary) and n.left.child == n.right:
|
|
|
|
|
return n.left, 1
|
|
|
|
|
elif n.right.name == SELECTION and n.right.child == n.left:
|
|
|
|
|
elif n.right.name == SELECTION and \
|
|
|
|
|
isinstance(n.right, Unary) and \
|
|
|
|
|
n.right.child == n.left:
|
|
|
|
|
return n.right, 1
|
|
|
|
|
|
|
|
|
|
# Subtraction and selection of the same thing
|
|
|
|
|
elif n.name == DIFFERENCE and \
|
|
|
|
|
isinstance(n, Binary) and \
|
|
|
|
|
n.right.name == SELECTION and \
|
|
|
|
|
isinstance(n.right, Unary) 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(
|
|
|
|
|
elif n.name == DIFFERENCE and \
|
|
|
|
|
isinstance(n, Binary) and \
|
|
|
|
|
(n.left == n.right or (n.left.name == SELECTION and isinstance(n.left, Unary) and n.left.child == n.right)):
|
|
|
|
|
return Unary(
|
|
|
|
|
SELECTION,
|
|
|
|
|
'False',
|
|
|
|
|
n.get_left_leaf()
|
|
|
|
@ -182,11 +144,12 @@ def down_to_unions_subtractions_intersections(n: parser.Node) -> Tuple[parser.No
|
|
|
|
|
'''
|
|
|
|
|
changes = 0
|
|
|
|
|
_o = (UNION, DIFFERENCE, INTERSECTION)
|
|
|
|
|
if n.name == SELECTION and n.child.name in _o:
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -194,8 +157,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 n.name == PROJECTION 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
|
|
|
|
@ -205,14 +168,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 n.name == SELECTION 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -222,12 +185,17 @@ def swap_union_renames(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
and replaces them with
|
|
|
|
|
ρ a➡b(R ∪ Q).
|
|
|
|
|
Does the same with subtraction and intersection'''
|
|
|
|
|
if n.name in (DIFFERENCE, UNION, INTERSECTION) and n.left.name == RENAME and n.right.name == RENAME:
|
|
|
|
|
if n.name in (DIFFERENCE, UNION, INTERSECTION) and \
|
|
|
|
|
isinstance(n, Binary) and \
|
|
|
|
|
n.left.name == RENAME and \
|
|
|
|
|
isinstance(n.left, Unary) and\
|
|
|
|
|
n.right.name == RENAME and \
|
|
|
|
|
isinstance(n.right, Unary):
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -239,7 +207,7 @@ def futile_renames(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
|
|
|
|
|
or removes the operation entirely if they all get removed
|
|
|
|
|
'''
|
|
|
|
|
if n.name == RENAME:
|
|
|
|
|
if isinstance(n, Unary) and n.name == RENAME:
|
|
|
|
|
renames = n.get_rename_prop()
|
|
|
|
|
changes = False
|
|
|
|
|
for k, v in renames.items():
|
|
|
|
@ -261,11 +229,14 @@ def subsequent_renames(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
into
|
|
|
|
|
ρ ... (A)
|
|
|
|
|
'''
|
|
|
|
|
if n.name == RENAME and n.child.name == RENAME:
|
|
|
|
|
if isinstance(n, Unary) and \
|
|
|
|
|
n.name == RENAME and \
|
|
|
|
|
isinstance(n.child, Unary) and \
|
|
|
|
|
n.child.name == RENAME:
|
|
|
|
|
# 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()
|
|
|
|
@ -292,11 +263,11 @@ def subsequent_renames(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class level_string(str):
|
|
|
|
|
class LevelString(str):
|
|
|
|
|
level = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def tokenize_select(expression):
|
|
|
|
|
def tokenize_select(expression: str) -> List[LevelString]:
|
|
|
|
|
'''This function returns the list of tokens present in a
|
|
|
|
|
selection. The expression can contain parenthesis.
|
|
|
|
|
It will use a subclass of str with the attribute level, which
|
|
|
|
@ -304,8 +275,6 @@ def tokenize_select(expression):
|
|
|
|
|
g = generate_tokens(StringIO(str(expression)).readline)
|
|
|
|
|
l = list(token[1] for token in g)
|
|
|
|
|
|
|
|
|
|
l.remove('')
|
|
|
|
|
|
|
|
|
|
# Changes the 'a','.','method' token group into a single 'a.method' token
|
|
|
|
|
try:
|
|
|
|
|
while True:
|
|
|
|
@ -316,17 +285,21 @@ def tokenize_select(expression):
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
r = []
|
|
|
|
|
level = 0
|
|
|
|
|
for i in range(len(l)):
|
|
|
|
|
l[i] = level_string(l[i])
|
|
|
|
|
l[i].level = level
|
|
|
|
|
for i in l:
|
|
|
|
|
if not i:
|
|
|
|
|
continue
|
|
|
|
|
value = LevelString(i)
|
|
|
|
|
value.level = level
|
|
|
|
|
|
|
|
|
|
if l[i] == '(':
|
|
|
|
|
if value == '(':
|
|
|
|
|
level += 1
|
|
|
|
|
elif l[i] == ')':
|
|
|
|
|
elif value == ')':
|
|
|
|
|
level -= 1
|
|
|
|
|
r.append(value)
|
|
|
|
|
|
|
|
|
|
return l
|
|
|
|
|
return r
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def swap_rename_projection(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
@ -340,7 +313,10 @@ 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 n.name == PROJECTION and n.child.name == RENAME:
|
|
|
|
|
if isinstance(n, Unary) and \
|
|
|
|
|
n.name == PROJECTION and \
|
|
|
|
|
isinstance(n.child, Unary) and \
|
|
|
|
|
n.child.name == RENAME:
|
|
|
|
|
# π index,name(ρ id➡index(R))
|
|
|
|
|
renames = n.child.get_rename_prop()
|
|
|
|
|
projections = set(n.get_projection_prop())
|
|
|
|
@ -356,16 +332,16 @@ 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.set_projection_prop(projections)
|
|
|
|
|
n = parser.Unary(RENAME, '', child)
|
|
|
|
|
child = Unary(PROJECTION,'' , n.child.child)
|
|
|
|
|
child.set_projection_prop(list(projections))
|
|
|
|
|
n = Unary(RENAME, '', child)
|
|
|
|
|
n.set_rename_prop(renames)
|
|
|
|
|
return n, 1
|
|
|
|
|
|
|
|
|
|
return n, 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def swap_rename_select(n: parser.Node) -> int:
|
|
|
|
|
def swap_rename_select(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
'''This function locates things like
|
|
|
|
|
σ k(ρ j(R))
|
|
|
|
|
and replaces them with
|
|
|
|
@ -373,7 +349,10 @@ def swap_rename_select(n: parser.Node) -> int:
|
|
|
|
|
Renaming the attributes used in the
|
|
|
|
|
selection, so the operation is still valid.'''
|
|
|
|
|
|
|
|
|
|
if n.name == SELECTION and n.child.name == RENAME:
|
|
|
|
|
if isinstance(n, Unary) and \
|
|
|
|
|
n.name == SELECTION and \
|
|
|
|
|
isinstance(n.child, Unary) 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()}
|
|
|
|
|
|
|
|
|
@ -384,26 +363,28 @@ def swap_rename_select(n: parser.Node) -> int:
|
|
|
|
|
for i in range(len(tokens)):
|
|
|
|
|
splitted = tokens[i].split('.', 1)
|
|
|
|
|
if splitted[0] in renames:
|
|
|
|
|
tokens[i] = renames[splitted[0]]
|
|
|
|
|
tokens[i] = LevelString(renames[splitted[0]])
|
|
|
|
|
if len(splitted) > 1:
|
|
|
|
|
tokens[i] += '.' + splitted[1]
|
|
|
|
|
tokens[i] = LevelString(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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def select_union_intersect_subtract(n: parser.Node) -> int:
|
|
|
|
|
def select_union_intersect_subtract(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
'''This function locates things like
|
|
|
|
|
σ i(a) ∪ σ q(a)
|
|
|
|
|
and replaces them with
|
|
|
|
|
σ (i OR q) (a)
|
|
|
|
|
Removing a O(n²) operation like the union'''
|
|
|
|
|
if n.name in {UNION, INTERSECTION, DIFFERENCE} and \
|
|
|
|
|
if isinstance(n, Binary) and \
|
|
|
|
|
n.name in {UNION, INTERSECTION, DIFFERENCE} and \
|
|
|
|
|
isinstance(n.left, Unary) and \
|
|
|
|
|
n.left.name == SELECTION and \
|
|
|
|
|
isinstance(n.right, Unary) and \
|
|
|
|
|
n.right.name == SELECTION and \
|
|
|
|
|
n.left.child == n.right.child:
|
|
|
|
|
|
|
|
|
|
d = {UNION: 'or', INTERSECTION: 'and', DIFFERENCE: 'and not'}
|
|
|
|
|
op = d[n.name]
|
|
|
|
|
|
|
|
|
@ -423,7 +404,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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -432,18 +413,23 @@ def union_and_product(n: parser.Node) -> Tuple[parser.Node, int]:
|
|
|
|
|
A * B ∪ A * C = A * (B ∪ C)
|
|
|
|
|
Same thing with inner join
|
|
|
|
|
'''
|
|
|
|
|
if n.name == UNION and n.left.name in {PRODUCT, JOIN} and n.left.name == n.right.name:
|
|
|
|
|
if isinstance(n, Binary) and \
|
|
|
|
|
n.name == UNION and \
|
|
|
|
|
isinstance(n.left, Binary) and \
|
|
|
|
|
n.left.name in {PRODUCT, JOIN} and \
|
|
|
|
|
isinstance(n.right, Binary) and \
|
|
|
|
|
n.left.name == n.right.name:
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -459,37 +445,41 @@ def projection_and_union(n: parser.Node, rels: Dict[str, Relation]) -> Tuple[par
|
|
|
|
|
'''
|
|
|
|
|
changes = 0
|
|
|
|
|
if n.name == UNION and \
|
|
|
|
|
isinstance(n, Binary) and \
|
|
|
|
|
n.left.name == PROJECTION and \
|
|
|
|
|
isinstance(n.left, Unary) and \
|
|
|
|
|
n.right.name == PROJECTION and \
|
|
|
|
|
isinstance(n.right, Unary) 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def selection_and_product(n: parser.Node, rels: Dict[str, Relation]) -> parser.Node:
|
|
|
|
|
def selection_and_product(n: parser.Node, rels: Dict[str, Relation]) -> Tuple[parser.Node, int]:
|
|
|
|
|
'''This function locates things like σ k (R*Q) and converts them into
|
|
|
|
|
σ 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 n.name == SELECTION and n.child.name in (PRODUCT, JOIN):
|
|
|
|
|
if isinstance(n, Unary) and n.name == SELECTION and \
|
|
|
|
|
isinstance(n.child, Binary) and \
|
|
|
|
|
n.child.name in (PRODUCT, JOIN):
|
|
|
|
|
l_attr = n.child.left.result_format(rels)
|
|
|
|
|
r_attr = n.child.right.result_format(rels)
|
|
|
|
|
|
|
|
|
|
tokens = tokenize_select(n.prop)
|
|
|
|
|
groups = []
|
|
|
|
|
temp = []
|
|
|
|
|
groups: List[List[LevelString]] = []
|
|
|
|
|
temp: List[LevelString] = []
|
|
|
|
|
|
|
|
|
|
for i in tokens:
|
|
|
|
|
if i == 'and' and i.level == 0:
|
|
|
|
|
for k in tokens:
|
|
|
|
|
if k == 'and' and k.level == 0:
|
|
|
|
|
groups.append(temp)
|
|
|
|
|
temp = []
|
|
|
|
|
else:
|
|
|
|
|
temp.append(i)
|
|
|
|
|
if len(temp) != 0:
|
|
|
|
|
temp.append(k)
|
|
|
|
|
if len(temp):
|
|
|
|
|
groups.append(temp)
|
|
|
|
|
temp = []
|
|
|
|
|
del temp
|
|
|
|
|
|
|
|
|
|
left = []
|
|
|
|
|
right = []
|
|
|
|
@ -500,10 +490,10 @@ def selection_and_product(n: parser.Node, rels: Dict[str, Relation]) -> parser.N
|
|
|
|
|
r_fields = False # has fields in left?
|
|
|
|
|
|
|
|
|
|
for j in set(i).difference(sel_op):
|
|
|
|
|
j = j.split('.')[0]
|
|
|
|
|
if j in l_attr: # Field in left
|
|
|
|
|
t = j.split('.')[0]
|
|
|
|
|
if t in l_attr: # Field in left
|
|
|
|
|
l_fields = True
|
|
|
|
|
if j in r_attr: # Field in right
|
|
|
|
|
if t in r_attr: # Field in right
|
|
|
|
|
r_fields = True
|
|
|
|
|
|
|
|
|
|
if l_fields and not r_fields:
|
|
|
|
@ -518,7 +508,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: Node = Unary(SELECTION, l_prop, n.child.left)
|
|
|
|
|
else:
|
|
|
|
|
l_node = n.child.left
|
|
|
|
|
|
|
|
|
@ -527,18 +517,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: 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
|
|
|
|
@ -550,7 +540,7 @@ def useless_projection(n: parser.Node, rels: Dict[str, Relation]) -> Tuple[parse
|
|
|
|
|
'''
|
|
|
|
|
Removes projections that are over all the fields
|
|
|
|
|
'''
|
|
|
|
|
if 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
|
|
|
|
|
|
|
|
|
|