- Internally uses set instead of lists to describe relation's content
- Discards the old and not so functional tlb format git-svn-id: http://galileo.dmi.unict.it/svn/relational/trunk@250 014f5005-505e-4b48-8d0a-63407b615a7c
This commit is contained in:
parent
a408e517df
commit
2073b4db4a
@ -1,6 +1,8 @@
|
|||||||
0.12
|
0.12
|
||||||
- Removed encoding from .desktop file (was deprecated)
|
- Removed encoding from .desktop file (was deprecated)
|
||||||
- Added manpage for relational-cli
|
- Added manpage for relational-cli
|
||||||
|
- Internally uses set instead of lists to describe relation's content
|
||||||
|
- Discards the old and not so functional tlb format
|
||||||
|
|
||||||
0.11
|
0.11
|
||||||
- Font is set only on windows (Rev 206)
|
- Font is set only on windows (Rev 206)
|
||||||
|
@ -25,67 +25,50 @@ class relation (object):
|
|||||||
A relation can be represented using a table
|
A relation can be represented using a table
|
||||||
Calling an operation and providing a non relation parameter when it is expected will
|
Calling an operation and providing a non relation parameter when it is expected will
|
||||||
result in a None value'''
|
result in a None value'''
|
||||||
def __init__(self,filename="",comma_separated=True):
|
def __init__(self,filename=""):
|
||||||
'''Creates a relation, accepts a filename and then it will load the relation from
|
'''Creates a relation, accepts a filename and then it will load the relation from
|
||||||
that file. If no parameter is supplied an empty relation is created. Empty
|
that file. If no parameter is supplied an empty relation is created. Empty
|
||||||
relations are used in internal operations.
|
relations are used in internal operations.
|
||||||
By default the file will be handled like a comma separated as described in
|
By default the file will be handled like a comma separated as described in
|
||||||
RFC4180, but it can also be handled like a space separated file (previous
|
RFC4180, but it can also be handled like a space separated file (previous
|
||||||
default format) setting to false the 2nd parameter.
|
default format) setting to false the 2nd parameter.
|
||||||
The old format is deprecated since it doesn't permit fields
|
The old format is no longer supported.'''
|
||||||
with spaces, you should avoid using it.'''
|
|
||||||
if len(filename)==0:#Empty relation
|
if len(filename)==0:#Empty relation
|
||||||
self.content=[]
|
self.content=set()
|
||||||
self.header=header([])
|
self.header=header([])
|
||||||
return
|
return
|
||||||
#Opening file
|
#Opening file
|
||||||
fp=file(filename)
|
fp=file(filename)
|
||||||
if comma_separated:
|
|
||||||
reader=csv.reader(fp) #Creating a csv reader
|
reader=csv.reader(fp) #Creating a csv reader
|
||||||
self.header=header(reader.next()) # read 1st line
|
self.header=header(reader.next()) # read 1st line
|
||||||
self.content=[]
|
self.content=set()
|
||||||
for i in reader.__iter__(): #Iterating rows
|
|
||||||
self.content.append(i)
|
|
||||||
else: #Old format
|
|
||||||
self.header=header(fp.readline().replace("\n","").strip().split(" "))
|
|
||||||
|
|
||||||
self.content=[]
|
for i in reader.__iter__(): #Iterating rows
|
||||||
row=fp.readline()
|
self.content.add(tuple(i))
|
||||||
while len(row)!=0:#Reads the content of the relation
|
|
||||||
self.content.append(row.replace("\n","").strip().split(" "))
|
|
||||||
row=fp.readline()
|
|
||||||
|
|
||||||
#Closing file
|
#Closing file
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
def save(self,filename,comma_separated=True):
|
def save(self,filename):
|
||||||
'''Saves the relation in a file. By default will save using the csv
|
'''Saves the relation in a file. By default will save using the csv
|
||||||
format as defined in RFC4180, but setting comma_separated to False,
|
format as defined in RFC4180, but setting comma_separated to False,
|
||||||
it will use the old format with space separated values.
|
it will use the old format with space separated values.
|
||||||
The old format is deprecated since it doesn't permit fields
|
The old format is no longer supported.'''
|
||||||
with spaces, you should avoid using it.'''
|
|
||||||
|
|
||||||
fp=file(filename,'w') #Opening file in write mode
|
fp=file(filename,'w') #Opening file in write mode
|
||||||
if comma_separated: #writing csv
|
|
||||||
writer=csv.writer(fp) #Creating csv writer
|
writer=csv.writer(fp) #Creating csv writer
|
||||||
|
|
||||||
#It wants an iterable containing iterables
|
#It wants an iterable containing iterables
|
||||||
head=[]
|
head=(self.header.attributes,)
|
||||||
head.append(self.header.attributes)
|
|
||||||
writer.writerows(head)
|
writer.writerows(head)
|
||||||
|
|
||||||
#Writing content, already in the correct format
|
#Writing content, already in the correct format
|
||||||
writer.writerows(self.content)
|
writer.writerows(self.content)
|
||||||
else: #Writing in the old, deprecated, format
|
|
||||||
res=""
|
|
||||||
res+=" ".join(self.header.attributes)
|
|
||||||
|
|
||||||
for r in self.content:
|
|
||||||
res+="\n"
|
|
||||||
res+=" ".join(r)
|
|
||||||
fp.write(res)
|
|
||||||
fp.close() #Closing file
|
fp.close() #Closing file
|
||||||
|
|
||||||
def _rearrange_(self,other):
|
def _rearrange_(self,other):
|
||||||
'''If two relations share the same attributes in a different order, this method
|
'''If two relations share the same attributes in a different order, this method
|
||||||
will use projection to make them have the same attributes' order.
|
will use projection to make them have the same attributes' order.
|
||||||
@ -105,6 +88,7 @@ class relation (object):
|
|||||||
newt=relation()
|
newt=relation()
|
||||||
newt.header=header(list(self.header.attributes))
|
newt.header=header(list(self.header.attributes))
|
||||||
for i in self.content:
|
for i in self.content:
|
||||||
|
#Fills the attributes dictionary with the values of the tuple
|
||||||
for j in range(len(self.header.attributes)):
|
for j in range(len(self.header.attributes)):
|
||||||
if len(i[j])>0 and i[j].isdigit():
|
if len(i[j])>0 and i[j].isdigit():
|
||||||
attributes[self.header.attributes[j]]=int(i[j])
|
attributes[self.header.attributes[j]]=int(i[j])
|
||||||
@ -117,7 +101,7 @@ class relation (object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if eval(expr,attributes):
|
if eval(expr,attributes):
|
||||||
newt.content.append(i)
|
newt.content.add(i)
|
||||||
except Exception,e:
|
except Exception,e:
|
||||||
raise Exception("Failed to evaluate %s\n%s" % (expr,e.__str__()))
|
raise Exception("Failed to evaluate %s\n%s" % (expr,e.__str__()))
|
||||||
return newt
|
return newt
|
||||||
@ -134,7 +118,7 @@ class relation (object):
|
|||||||
|
|
||||||
for i in self.content:
|
for i in self.content:
|
||||||
for j in other.content:
|
for j in other.content:
|
||||||
newt.content.append(i+j)
|
newt.content.add(i+j)
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
|
|
||||||
@ -174,8 +158,7 @@ class relation (object):
|
|||||||
row=[]
|
row=[]
|
||||||
for j in ids:
|
for j in ids:
|
||||||
row.append(i[j])
|
row.append(i[j])
|
||||||
if attributes_same_count or row not in newt.content:
|
newt.content.add(tuple(row))
|
||||||
newt.content.append(row)
|
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def rename(self,params):
|
def rename(self,params):
|
||||||
@ -192,7 +175,8 @@ class relation (object):
|
|||||||
if (newt.header.rename(old,new)) == False:
|
if (newt.header.rename(old,new)) == False:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
newt.content=list(self.content)
|
#TODO only copy the link and mark the new relation as read only
|
||||||
|
newt.content=set(self.content)
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def intersection(self,other):
|
def intersection(self,other):
|
||||||
@ -207,10 +191,7 @@ class relation (object):
|
|||||||
newt=relation()
|
newt=relation()
|
||||||
newt.header=header(list(self.header.attributes))
|
newt.header=header(list(self.header.attributes))
|
||||||
|
|
||||||
#Adds only element not in other, duplicating them
|
newt.content=self.content.intersection(other.content)
|
||||||
for e in self.content:
|
|
||||||
if e in other.content:
|
|
||||||
newt.content.append(list(e))
|
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def difference(self,other):
|
def difference(self,other):
|
||||||
@ -225,10 +206,7 @@ class relation (object):
|
|||||||
newt=relation()
|
newt=relation()
|
||||||
newt.header=header(list(self.header.attributes))
|
newt.header=header(list(self.header.attributes))
|
||||||
|
|
||||||
#Adds only element not in other, duplicating them
|
newt.content=self.content.difference(other.content)
|
||||||
for e in self.content:
|
|
||||||
if e not in other.content:
|
|
||||||
newt.content.append(list(e))
|
|
||||||
return newt
|
return newt
|
||||||
def division(self,other):
|
def division(self,other):
|
||||||
'''Division operator
|
'''Division operator
|
||||||
@ -272,13 +250,7 @@ class relation (object):
|
|||||||
newt=relation()
|
newt=relation()
|
||||||
newt.header=header(list(self.header.attributes))
|
newt.header=header(list(self.header.attributes))
|
||||||
|
|
||||||
#Adds element from self, duplicating them all
|
newt.content=self.content.union(other.content)
|
||||||
for e in self.content:
|
|
||||||
newt.content.append(list(e))
|
|
||||||
|
|
||||||
for e in other.content:
|
|
||||||
if e not in newt.content:
|
|
||||||
newt.content.append(list(e))
|
|
||||||
return newt
|
return newt
|
||||||
def thetajoin(self,other,expr):
|
def thetajoin(self,other,expr):
|
||||||
'''Defined as product and then selection with the given expression.'''
|
'''Defined as product and then selection with the given expression.'''
|
||||||
@ -344,34 +316,32 @@ class relation (object):
|
|||||||
for l in noid:
|
for l in noid:
|
||||||
item.append(j[l])
|
item.append(j[l])
|
||||||
|
|
||||||
newt.content.append(item)
|
newt.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=list(i)
|
item=list(i)
|
||||||
for l in range(len(noid)):
|
for l in range(len(noid)):
|
||||||
item.append("---")
|
item.append("---")
|
||||||
newt.content.append(item)
|
newt.content.add(tuple(item))
|
||||||
|
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def join(self,other):
|
def join(self,other):
|
||||||
'''Natural join, joins on shared attributes (one or more). If there are no
|
'''Natural join, joins on shared attributes (one or more). If there are no
|
||||||
shared attributes, it will behave as cartesian product.'''
|
shared attributes, it will behave as cartesian product.'''
|
||||||
shared=[]
|
|
||||||
for i in self.header.attributes:
|
#List of attributes in common between the relations
|
||||||
if i in other.header.attributes:
|
shared=list(set(self.header.attributes).intersection(set(other.header.attributes)))
|
||||||
shared.append(i)
|
|
||||||
|
|
||||||
newt=relation() #Creates the new relation
|
newt=relation() #Creates the new relation
|
||||||
|
|
||||||
#Adds all the attributes of the 1st relation
|
#Adding to the headers all the fields, done like that because order is needed
|
||||||
newt.header=header(list(self.header.attributes))
|
newt.header=header(list(self.header.attributes))
|
||||||
|
|
||||||
#Adds all the attributes of the 2nd, when non shared
|
|
||||||
for i in other.header.attributes:
|
for i in other.header.attributes:
|
||||||
if i not in shared:
|
if i not in shared:
|
||||||
newt.header.attributes.append(i)
|
newt.header.attributes.append(i)
|
||||||
|
|
||||||
#Shared ids of self
|
#Shared ids of self
|
||||||
sid=self.header.getAttributesId(shared)
|
sid=self.header.getAttributesId(shared)
|
||||||
#Shared ids of the other relation
|
#Shared ids of the other relation
|
||||||
@ -394,7 +364,7 @@ class relation (object):
|
|||||||
for l in noid:
|
for l in noid:
|
||||||
item.append(j[l])
|
item.append(j[l])
|
||||||
|
|
||||||
newt.content.append(item)
|
newt.content.add(tuple(item))
|
||||||
|
|
||||||
return newt
|
return newt
|
||||||
def __eq__(self,other):
|
def __eq__(self,other):
|
||||||
@ -412,12 +382,7 @@ class relation (object):
|
|||||||
return False #Non shared attribute
|
return False #Non shared attribute
|
||||||
|
|
||||||
#comparing content
|
#comparing content
|
||||||
if len(self.content) != len(other.content):
|
return self.content==other.content
|
||||||
return False #Not the same
|
|
||||||
for i in self.content:
|
|
||||||
if i not in other.content:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
'''Returns a string representation of the relation, can be printed with
|
'''Returns a string representation of the relation, can be printed with
|
||||||
@ -433,7 +398,6 @@ class relation (object):
|
|||||||
m_len[col]=len(i)
|
m_len[col]=len(i)
|
||||||
col+=1
|
col+=1
|
||||||
|
|
||||||
|
|
||||||
res=""
|
res=""
|
||||||
for f in range(len(self.header.attributes)):
|
for f in range(len(self.header.attributes)):
|
||||||
res+="%s"%(self.header.attributes[f].ljust(2+m_len[f]))
|
res+="%s"%(self.header.attributes[f].ljust(2+m_len[f]))
|
||||||
@ -482,8 +446,9 @@ class relation (object):
|
|||||||
self.content.remove(i)
|
self.content.remove(i)
|
||||||
for k in range(len(keys)):
|
for k in range(len(keys)):
|
||||||
new_tuple[f_ids[k]]=str(dic[keys[k]])
|
new_tuple[f_ids[k]]=str(dic[keys[k]])
|
||||||
self.content.append(new_tuple)
|
self.content.add(tuple(new_tuple))
|
||||||
return affected
|
return affected
|
||||||
|
|
||||||
def insert(self,values):
|
def insert(self,values):
|
||||||
'''Inserts a tuple in the relation.
|
'''Inserts a tuple in the relation.
|
||||||
This function will not insert duplicate tuples.
|
This function will not insert duplicate tuples.
|
||||||
@ -499,11 +464,9 @@ class relation (object):
|
|||||||
for i in values:
|
for i in values:
|
||||||
t.append(str(i))
|
t.append(str(i))
|
||||||
|
|
||||||
if t not in self.content:
|
prevlen=len(self.content)
|
||||||
self.content.append(t)
|
self.content.add(tuple(t))
|
||||||
return 1
|
return len(self.content)-prevlen
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def delete(self,expr):
|
def delete(self,expr):
|
||||||
'''Delete, expr must be a valid boolean expression, can contain field names,
|
'''Delete, expr must be a valid boolean expression, can contain field names,
|
||||||
@ -513,7 +476,7 @@ class relation (object):
|
|||||||
Returns the number of affected rows.'''
|
Returns the number of affected rows.'''
|
||||||
attributes={}
|
attributes={}
|
||||||
affected=len(self.content)
|
affected=len(self.content)
|
||||||
new_content=[] #New content of the relation
|
new_content=set() #New content of the relation
|
||||||
for i in self.content:
|
for i in self.content:
|
||||||
for j in range(len(self.header.attributes)):
|
for j in range(len(self.header.attributes)):
|
||||||
if i[j].isdigit():
|
if i[j].isdigit():
|
||||||
@ -526,7 +489,7 @@ class relation (object):
|
|||||||
attributes[self.header.attributes[j]]=i[j]
|
attributes[self.header.attributes[j]]=i[j]
|
||||||
if not eval(expr,attributes):
|
if not eval(expr,attributes):
|
||||||
affected-=1
|
affected-=1
|
||||||
new_content.append(i)
|
new_content.add(i)
|
||||||
self.content=new_content
|
self.content=new_content
|
||||||
return affected
|
return affected
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ class Ui_Form(object):
|
|||||||
#Patch provided by Angelo 'Havoc' Puglisi
|
#Patch provided by Angelo 'Havoc' Puglisi
|
||||||
self.relations[str(res[0].toUtf8())]=relation.relation(str(filename.toUtf8()),use_csv)
|
self.relations[str(res[0].toUtf8())]=relation.relation(str(filename.toUtf8()),use_csv)
|
||||||
else: #name was decided by caller
|
else: #name was decided by caller
|
||||||
self.relations[name]=relation.relation(filename,use_csv)
|
self.relations[name]=relation.relation(filename)
|
||||||
|
|
||||||
self.updateRelations()
|
self.updateRelations()
|
||||||
def insertTuple(self):
|
def insertTuple(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user