Relations are now frozen

This commit is contained in:
Salvo 'LtWorf' Tomaselli 2020-08-13 11:04:22 +02:00
parent 87ec732d24
commit 4722c9b0e8
No known key found for this signature in database
GPG Key ID: B3A7CF0C801886CF

View File

@ -20,7 +20,7 @@
# relational operations on them. # relational operations on them.
import csv import csv
from itertools import chain, repeat from itertools import chain, repeat, product as iproduct
from collections import deque from collections import deque
from typing import * from typing import *
from pathlib import Path from pathlib import Path
@ -66,7 +66,7 @@ class Relation(NamedTuple):
reader = csv.reader(fp) # Creating a csv reader reader = csv.reader(fp) # Creating a csv reader
header = Header(next(reader)) # read 1st line header = Header(next(reader)) # read 1st line
#FIXME load properly #FIXME load properly
content = frozenset((tuple(i) for i in reader)) content = frozenset((tuple(Rstring(s) for s in i) for i in reader))
return Relation(header, content) return Relation(header, content)
def __iter__(self): def __iter__(self):
@ -111,14 +111,14 @@ class Relation(NamedTuple):
''' '''
Selection, expr must be a valid Python expression; can contain field names. Selection, expr must be a valid Python expression; can contain field names.
''' '''
newt = Relation() header = Header(self.header)
newt.header = Header(self.header)
try: try:
c_expr = compile(expr, 'selection', 'eval') c_expr = compile(expr, 'selection', 'eval')
except: except:
raise Exception('Failed to compile expression: %s' % expr) raise Exception('Failed to compile expression: %s' % expr)
content = set()
for i in self.content: for i in self.content:
# Fills the attributes dictionary with the values of the tuple # Fills the attributes dictionary with the values of the tuple
attributes = {attr: i[j].autocast() attributes = {attr: i[j].autocast()
@ -127,11 +127,11 @@ class Relation(NamedTuple):
try: try:
if eval(c_expr, attributes): if eval(c_expr, attributes):
newt.content.add(i) content.add(i)
except Exception as e: except Exception as e:
raise Exception( raise Exception(
"Failed to evaluate %s\n%s" % (expr, e.__str__())) "Failed to evaluate %s\n%s" % (expr, e.__str__()))
return newt return Relation(header, frozenset(content))
def product(self, other: 'Relation') -> 'Relation': def product(self, other: 'Relation') -> 'Relation':
''' '''
@ -144,13 +144,10 @@ class Relation(NamedTuple):
raise Exception( raise Exception(
'Unable to perform product on relations with colliding attributes' 'Unable to perform product on relations with colliding attributes'
) )
newt = Relation() header = Header(self.header + other.header)
newt.header = Header(self.header + other.header)
for i in self.content: content = frozenset(i+j for i, j in iproduct(self.content, other.content))
for j in other.content: return Relation(header, content)
newt.content.add(i + j)
return newt
def projection(self, *attributes) -> 'Relation': def projection(self, *attributes) -> 'Relation':
''' '''
@ -172,16 +169,11 @@ class Relation(NamedTuple):
if len(ids) == 0: if len(ids) == 0:
raise Exception('Invalid attributes for projection') raise Exception('Invalid attributes for projection')
newt = Relation() header = Header((self.header[i] for i in ids))
# Create the header
h = (self.header[i] for i in ids)
newt.header = Header(h)
# Create the body content = frozenset(tuple((i[j] for j in ids)) for i in self.content)
for i in self.content:
row = (i[j] for j in ids) return Relation(header, content)
newt.content.add(tuple(row))
return newt
def rename(self, params: Dict[str, str]) -> 'Relation': def rename(self, params: Dict[str, str]) -> 'Relation':
''' '''
@ -192,12 +184,8 @@ class Relation(NamedTuple):
For example if you want to rename a to b, call For example if you want to rename a to b, call
rel.rename({'a':'b'}) rel.rename({'a':'b'})
''' '''
newt = Relation() header = self.header.rename(params)
newt.header = self.header.rename(params) return Relation(header, self.content)
newt.content = self.content
self._make_duplicate(newt)
return newt
def intersection(self, other: 'Relation') -> 'Relation': def intersection(self, other: 'Relation') -> 'Relation':
''' '''
@ -206,22 +194,14 @@ class Relation(NamedTuple):
Will return an empty one if there are no common items. Will return an empty one if there are no common items.
''' '''
other = self._rearrange(other) # Rearranges attributes' order other = self._rearrange(other) # Rearranges attributes' order
newt = Relation() return Relation(self.header, self.content.intersection(other.content))
newt.header = Header(self.header)
newt.content = self.content.intersection(other.content)
return newt
def difference(self, other: 'Relation') -> 'Relation': def difference(self, other: 'Relation') -> 'Relation':
'''Difference operation. The result will contain items present in first '''Difference operation. The result will contain items present in first
operand but not in second one. operand but not in second one.
''' '''
other = self._rearrange(other) # Rearranges attributes' order other = self._rearrange(other) # Rearranges attributes' order
newt = Relation() return Relation(self.header, self.content.difference(other.content))
newt.header = Header(self.header)
newt.content = self.content.difference(other.content)
return newt
def division(self, other: 'Relation') -> 'Relation': def division(self, other: 'Relation') -> 'Relation':
'''Division operator '''Division operator
@ -254,11 +234,7 @@ class Relation(NamedTuple):
and second operands. and second operands.
''' '''
other = self._rearrange(other) # Rearranges attributes' order other = self._rearrange(other) # Rearranges attributes' order
newt = Relation() return Relation(self.header, self.content.union(other.content))
newt.header = Header(self.header)
newt.content = self.content.union(other.content)
return newt
def thetajoin(self, other: 'Relation', expr: str) -> 'Relation': def thetajoin(self, other: 'Relation', expr: str) -> 'Relation':
'''Defined as product and then selection with the given expression.''' '''Defined as product and then selection with the given expression.'''
@ -288,11 +264,10 @@ class Relation(NamedTuple):
shared = self.header.intersection(other.header) shared = self.header.intersection(other.header)
newt = Relation() # Creates the new relation
# Creating the header with all the fields, done like that because order is # Creating the header with all the fields, done like that because order is
# needed # needed
h = (i for i in other.header if i not in shared) h = (i for i in other.header if i not in shared)
newt.header = Header(chain(self.header, h)) header = Header(chain(self.header, h))
# Shared ids of self # Shared ids of self
sid = self.header.getAttributesId(shared) sid = self.header.getAttributesId(shared)
@ -302,6 +277,7 @@ class Relation(NamedTuple):
# Non shared ids of the other relation # Non shared ids of the other relation
noid = [i for i in range(len(other.header)) if i not in oid] noid = [i for i in range(len(other.header)) if i not in oid]
content = set()
for i in self.content: for i in self.content:
# Tuple partecipated to the join? # Tuple partecipated to the join?
added = False added = False
@ -313,14 +289,14 @@ class Relation(NamedTuple):
if match: if match:
item = chain(i, (j[l] for l in noid)) item = chain(i, (j[l] for l in noid))
newt.content.add(tuple(item)) content.add(tuple(item))
added = True added = True
# If it didn't partecipate, adds it # If it didn't partecipate, adds it
if not added: if not added:
item = chain(i, repeat(Rstring('---'), len(noid))) item = chain(i, repeat(Rstring('---'), len(noid)))
newt.content.add(tuple(item)) content.add(tuple(item))
return newt return Relation(header, frozenset(content))
def join(self, other: 'Relation') -> 'Relation': def join(self, other: 'Relation') -> 'Relation':
''' '''
@ -331,12 +307,10 @@ class Relation(NamedTuple):
# List of attributes in common between the relations # List of attributes in common between the relations
shared = self.header.intersection(other.header) shared = self.header.intersection(other.header)
newt = Relation() # Creates the new relation
# Creating the header with all the fields, done like that because order is # Creating the header with all the fields, done like that because order is
# needed # needed
h = (i for i in other.header if i not in shared) h = (i for i in other.header if i not in shared)
newt.header = Header(chain(self.header, h)) header = Header(chain(self.header, h))
# Shared ids of self # Shared ids of self
sid = self.header.getAttributesId(shared) sid = self.header.getAttributesId(shared)
@ -346,6 +320,7 @@ class Relation(NamedTuple):
# Non shared ids of the other relation # Non shared ids of the other relation
noid = [i for i in range(len(other.header)) if i not in oid] noid = [i for i in range(len(other.header)) if i not in oid]
content = set()
for i in self.content: for i in self.content:
for j in other.content: for j in other.content:
match = True match = True
@ -354,9 +329,9 @@ class Relation(NamedTuple):
if match: if match:
item = chain(i, (j[l] for l in noid)) item = chain(i, (j[l] for l in noid))
newt.content.add(tuple(item)) content.add(tuple(item))
return newt return Relation(header, frozenset(content))
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Relation): if not isinstance(other, Relation):
@ -410,7 +385,6 @@ class Relation(NamedTuple):
Returns the number of affected rows. Returns the number of affected rows.
''' '''
self._make_writable(copy_content=False)
affected = self.selection(expr) affected = self.selection(expr)
not_affected = self.difference(affected) not_affected = self.difference(affected)
@ -446,8 +420,6 @@ class Relation(NamedTuple):
) )
) )
self._make_writable()
prevlen = len(self.content) prevlen = len(self.content)
self.content.add(tuple(map(Rstring, values))) self.content.add(tuple(map(Rstring, values)))
return len(self.content) - prevlen return len(self.content) - prevlen
@ -462,7 +434,6 @@ class Relation(NamedTuple):
Returns the number of affected rows.''' Returns the number of affected rows.'''
l = len(self.content) l = len(self.content)
self._make_writable(copy_content=False)
self.content = self.difference(self.selection(expr)).content self.content = self.difference(self.selection(expr)).content
return len(self.content) - l return len(self.content) - l