diff --git a/README.md b/README.md
index 7e276754..8dbf37a9 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+
PULL REQUEST BY P16058 P16197
+
diff --git a/dbdata/None_db/meta_indexes.pkl b/dbdata/None_db/meta_indexes.pkl
new file mode 100644
index 00000000..d38a5c9f
Binary files /dev/null and b/dbdata/None_db/meta_indexes.pkl differ
diff --git a/dbdata/None_db/meta_insert_stack.pkl b/dbdata/None_db/meta_insert_stack.pkl
new file mode 100644
index 00000000..f3b54cab
Binary files /dev/null and b/dbdata/None_db/meta_insert_stack.pkl differ
diff --git a/dbdata/None_db/meta_length.pkl b/dbdata/None_db/meta_length.pkl
new file mode 100644
index 00000000..1ab860e0
Binary files /dev/null and b/dbdata/None_db/meta_length.pkl differ
diff --git a/dbdata/None_db/meta_locks.pkl b/dbdata/None_db/meta_locks.pkl
new file mode 100644
index 00000000..f745e04c
Binary files /dev/null and b/dbdata/None_db/meta_locks.pkl differ
diff --git a/dbdata/None_db/unique_test.pkl b/dbdata/None_db/unique_test.pkl
new file mode 100644
index 00000000..b67ba1ce
Binary files /dev/null and b/dbdata/None_db/unique_test.pkl differ
diff --git a/dbdata/smdb_db/advisor.pkl b/dbdata/smdb_db/advisor.pkl
new file mode 100644
index 00000000..7e60291f
Binary files /dev/null and b/dbdata/smdb_db/advisor.pkl differ
diff --git a/dbdata/smdb_db/classroom.pkl b/dbdata/smdb_db/classroom.pkl
new file mode 100644
index 00000000..63a85753
Binary files /dev/null and b/dbdata/smdb_db/classroom.pkl differ
diff --git a/dbdata/smdb_db/course.pkl b/dbdata/smdb_db/course.pkl
new file mode 100644
index 00000000..ace69cd1
Binary files /dev/null and b/dbdata/smdb_db/course.pkl differ
diff --git a/dbdata/smdb_db/department.pkl b/dbdata/smdb_db/department.pkl
new file mode 100644
index 00000000..f41676fe
Binary files /dev/null and b/dbdata/smdb_db/department.pkl differ
diff --git a/dbdata/smdb_db/instructor.pkl b/dbdata/smdb_db/instructor.pkl
new file mode 100644
index 00000000..4c4a6cee
Binary files /dev/null and b/dbdata/smdb_db/instructor.pkl differ
diff --git a/dbdata/smdb_db/meta_indexes.pkl b/dbdata/smdb_db/meta_indexes.pkl
new file mode 100644
index 00000000..d38a5c9f
Binary files /dev/null and b/dbdata/smdb_db/meta_indexes.pkl differ
diff --git a/dbdata/smdb_db/meta_insert_stack.pkl b/dbdata/smdb_db/meta_insert_stack.pkl
new file mode 100644
index 00000000..2bc855c4
Binary files /dev/null and b/dbdata/smdb_db/meta_insert_stack.pkl differ
diff --git a/dbdata/smdb_db/meta_length.pkl b/dbdata/smdb_db/meta_length.pkl
new file mode 100644
index 00000000..fa2d5b3e
Binary files /dev/null and b/dbdata/smdb_db/meta_length.pkl differ
diff --git a/dbdata/smdb_db/meta_locks.pkl b/dbdata/smdb_db/meta_locks.pkl
new file mode 100644
index 00000000..f745e04c
Binary files /dev/null and b/dbdata/smdb_db/meta_locks.pkl differ
diff --git a/dbdata/smdb_db/prereq.pkl b/dbdata/smdb_db/prereq.pkl
new file mode 100644
index 00000000..234fe214
Binary files /dev/null and b/dbdata/smdb_db/prereq.pkl differ
diff --git a/dbdata/smdb_db/section.pkl b/dbdata/smdb_db/section.pkl
new file mode 100644
index 00000000..38b96009
Binary files /dev/null and b/dbdata/smdb_db/section.pkl differ
diff --git a/dbdata/smdb_db/student.pkl b/dbdata/smdb_db/student.pkl
new file mode 100644
index 00000000..73e5677a
Binary files /dev/null and b/dbdata/smdb_db/student.pkl differ
diff --git a/dbdata/smdb_db/takes.pkl b/dbdata/smdb_db/takes.pkl
new file mode 100644
index 00000000..25e9ece4
Binary files /dev/null and b/dbdata/smdb_db/takes.pkl differ
diff --git a/dbdata/smdb_db/teaches.pkl b/dbdata/smdb_db/teaches.pkl
new file mode 100644
index 00000000..ff3e0d5c
Binary files /dev/null and b/dbdata/smdb_db/teaches.pkl differ
diff --git a/dbdata/smdb_db/time_slot.pkl b/dbdata/smdb_db/time_slot.pkl
new file mode 100644
index 00000000..bd150378
Binary files /dev/null and b/dbdata/smdb_db/time_slot.pkl differ
diff --git a/dbdata/smdb_db/unique_test.pkl b/dbdata/smdb_db/unique_test.pkl
new file mode 100644
index 00000000..19c7870b
Binary files /dev/null and b/dbdata/smdb_db/unique_test.pkl differ
diff --git a/documentation.pdf b/documentation.pdf
new file mode 100644
index 00000000..bf241007
Binary files /dev/null and b/documentation.pdf differ
diff --git a/mdb.py b/mdb.py
index a981e5be..02c160b3 100644
--- a/mdb.py
+++ b/mdb.py
@@ -1,3 +1,6 @@
+#CHANGES BY P16058 P16197
+
+
import os
import re
from pprint import pprint
@@ -5,6 +8,7 @@
import readline
import traceback
import shutil
+from tabulate import tabulate
sys.path.append('miniDB')
from database import Database
@@ -105,6 +109,13 @@ def create_query_plan(query, keywords, action):
dic['primary key'] = arglist[arglist.index('primary')-2]
else:
dic['primary key'] = None
+ #adding the unique constraing in the way of the primary key
+ if 'unique' in args:
+ arglist=args[1:-1].split(' ')
+ #REMOVING ALL CHARACTERS BEFORE THE COMMA
+ u=arglist[arglist.index('unique')-2].split(",",1)
+ dic['unique']=u[1]
+
if action=='import':
dic = {'import table' if key=='import' else key: val for key, val in dic.items()}
@@ -290,9 +301,75 @@ def remove_db(db_name):
dic = interpret(line.removeprefix('explain '))
pprint(dic, sort_dicts=False)
else:
- dic = interpret(line)
- result = execute_dic(dic)
- if isinstance(result,Table):
- result.show()
+ #this condition changes the way the queries are executed
+ l=line.split()
+ queries=[]
+ initial_part=[]
+ conditions=[]
+ position=0
+ #first we get the select (condition) part of statement
+ for i in range(0,len(l)):
+ initial_part.append(l[i])
+ if(l[i]=='where'):
+ position=i
+ break
+ #this part collects the parts that correspond to the condition
+ q=[]
+ for i in range(position+1,len(l)):
+ if(l[i]=='or'):
+ queries.append(q)
+ q=[]
+ conditions.append('or')
+ elif(l[i]=='and'):
+ queries.append(q)
+ q=[]
+ conditions.append('and')
+ else:
+ q.append(l[i])
+ queries.append(q)
+ #converting the lists into the strings that the parser operates on
+ for i in range(0,len(queries)):
+ queries[i]=' '.join(queries[i])
+ if(queries[i][-1]!=';'):
+ queries[i]=queries[i]+';'
+
+ initial_part=' '.join(initial_part)
+ for i in range(0,len(queries)):
+ queries[i]=initial_part+' '+queries[i]
+ if(len(queries)==1):
+ #this condition prints only one condition
+ dic = interpret(line)
+ result = execute_dic(dic)
+ if isinstance(result,Table):
+ result.show()
+ else:
+ #multiple conditions
+ dicts=[]
+ for q in queries:
+ dicts.append(interpret(q))
+ #this line gets the headers in order to print like normal
+ headers=execute_dic(dicts[0]).get_op_headers()
+ results=[]
+ for d in dicts:
+ results.append(execute_dic(d).get_op_res())
+ output=results[0]
+ for i in range(1,len(results)):
+ if(conditions[i-1]=='or'):
+ for r in results[i]:
+ if(r not in output):
+ output.append(r)
+ elif(conditions[i-1]=='and'):
+ positions=[]
+ for r in results[i]:
+ for j in range(0,len(output)):
+ if(r==output[j]):
+ positions.append(j)
+ to_be_popped=[]
+ for j in range(0,len(output)):
+ if(j not in positions):
+ to_be_popped.append(j)
+ for j in range(len(to_be_popped)-1,-1,-1):
+ output.pop(to_be_popped[j])
+ print(tabulate(output,headers=headers))
except Exception:
print(traceback.format_exc())
diff --git a/miniDB/__pycache__/__init__.cpython-39.pyc b/miniDB/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 00000000..07e26563
Binary files /dev/null and b/miniDB/__pycache__/__init__.cpython-39.pyc differ
diff --git a/miniDB/__pycache__/btree.cpython-39.pyc b/miniDB/__pycache__/btree.cpython-39.pyc
new file mode 100644
index 00000000..45a6c700
Binary files /dev/null and b/miniDB/__pycache__/btree.cpython-39.pyc differ
diff --git a/miniDB/__pycache__/database.cpython-39.pyc b/miniDB/__pycache__/database.cpython-39.pyc
new file mode 100644
index 00000000..13513661
Binary files /dev/null and b/miniDB/__pycache__/database.cpython-39.pyc differ
diff --git a/miniDB/__pycache__/joins.cpython-39.pyc b/miniDB/__pycache__/joins.cpython-39.pyc
new file mode 100644
index 00000000..234876ee
Binary files /dev/null and b/miniDB/__pycache__/joins.cpython-39.pyc differ
diff --git a/miniDB/__pycache__/misc.cpython-39.pyc b/miniDB/__pycache__/misc.cpython-39.pyc
new file mode 100644
index 00000000..cff86a62
Binary files /dev/null and b/miniDB/__pycache__/misc.cpython-39.pyc differ
diff --git a/miniDB/__pycache__/table.cpython-39.pyc b/miniDB/__pycache__/table.cpython-39.pyc
new file mode 100644
index 00000000..2d762f6f
Binary files /dev/null and b/miniDB/__pycache__/table.cpython-39.pyc differ
diff --git a/miniDB/database.py b/miniDB/database.py
index a3ac6be7..cffeccfc 100644
--- a/miniDB/database.py
+++ b/miniDB/database.py
@@ -1,3 +1,5 @@
+#CHANGES BY P16058 P16197
+
from __future__ import annotations
import pickle
from time import sleep, localtime, strftime
@@ -100,8 +102,8 @@ def _update(self):
self._update_meta_length()
self._update_meta_insert_stack()
-
- def create_table(self, name, column_names, column_types, primary_key=None, load=None):
+ #modifying the constructor in order to use the unique identifier
+ def create_table(self, name, column_names, column_types, primary_key=None,unique_key=None,load=None):
'''
This method create a new table. This table is saved and can be accessed via db_object.tables['table_name'] or db_object.table_name
@@ -113,7 +115,7 @@ def create_table(self, name, column_names, column_types, primary_key=None, load=
load: boolean. Defines table object parameters as the name of the table and the column names.
'''
# print('here -> ', column_names.split(','))
- self.tables.update({name: Table(name=name, column_names=column_names.split(','), column_types=column_types.split(','), primary_key=primary_key, load=load)})
+ self.tables.update({name: Table(name=name, column_names=column_names.split(','), column_types=column_types.split(','), primary_key=primary_key, unique=unique_key,load=load)})
# self._name = Table(name=name, column_names=column_names, column_types=column_types, load=load)
# check that new dynamic var doesnt exist already
# self.no_of_tables += 1
@@ -370,6 +372,11 @@ def select(self, columns, table_name, condition, distinct=None, order_by=None, \
index_name = self.select('*', 'meta_indexes', f'table_name={table_name}', return_object=True).column_by_name('index_name')[0]
bt = self._load_idx(index_name)
table = self.tables[table_name]._select_where_with_btree(columns, bt, condition, distinct, order_by, desc, limit)
+ #the following code block performs the btree search with the unique column
+ elif self._has_index(table_name) and condition_column==self.tables[table_name].column_names[self.tables[table_name].unique_idx]:
+ index_name = self.select('*', 'meta_indexes', f'table_name={table_name}', return_object=True).column_by_name('index_name')[0]
+ bt = self._load_idx(index_name)
+ table = self.tables[table_name]._select_where_with_btree(columns, bt, condition, distinct, order_by, desc, limit)
else:
table = self.tables[table_name]._select_where(columns, condition, distinct, order_by, desc, limit)
# self.unlock_table(table_name)
diff --git a/miniDB/misc.py b/miniDB/misc.py
index aefada74..5f1a4c6f 100644
--- a/miniDB/misc.py
+++ b/miniDB/misc.py
@@ -1,3 +1,6 @@
+#CHANGES BY P16058 P16197
+
+
import operator
def get_op(op, a, b):
@@ -8,7 +11,11 @@ def get_op(op, a, b):
'<': operator.lt,
'>=': operator.ge,
'<=': operator.le,
- '=': operator.eq}
+ '=': operator.eq,
+ #addition to implement NOT operator
+ 'not'.casefold():operator.ne,
+ #addition to implement BETWEEN operator
+ 'between'.casefold():between_op}
try:
return ops[op](a,b)
@@ -20,7 +27,11 @@ def split_condition(condition):
'<=': operator.le,
'=': operator.eq,
'>': operator.gt,
- '<': operator.lt}
+ '<': operator.lt,
+ #addition to implement NOT operator
+ 'not'.casefold():operator.ne,
+ #addition to implement BETWEEN operator
+ 'between'.casefold():between_op}
for op_key in ops.keys():
splt=condition.split(op_key)
@@ -34,7 +45,6 @@ def split_condition(condition):
if right.find('"') != -1: # If there are any double quotes in the value, throw. (Notice we've already removed the leading and trailing ones)
raise ValueError(f'Invalid condition: {condition}\nDouble quotation marks are not allowed inside values.')
-
return left, op_key, right
def reverse_op(op):
@@ -48,3 +58,17 @@ def reverse_op(op):
'<=' : '>=',
'=' : '='
}.get(op)
+
+
+#BETWEEN operator function
+def between_op(column_value,between_range):
+ between=between_range.split(',')
+ res=False
+ if(type(column_value)==int or type(column_value)==float):
+ #numerical comparison
+ res=(float(between[0])column_value)
+ else:
+ #string comparison
+ res=between[0]column_value
+ return res
+
diff --git a/miniDB/table.py b/miniDB/table.py
index f5c7d937..bd3396b7 100644
--- a/miniDB/table.py
+++ b/miniDB/table.py
@@ -1,3 +1,5 @@
+#CHANGES BY P16058 P16197
+
from __future__ import annotations
from tabulate import tabulate
import pickle
@@ -26,7 +28,8 @@ class Table:
- a dictionary that includes the appropriate info (all the attributes in __init__)
'''
- def __init__(self, name=None, column_names=None, column_types=None, primary_key=None, load=None):
+ #adding the unique constraint to the constructor
+ def __init__(self, name=None, column_names=None, column_types=None, primary_key=None,unique=None,load=None):
if load is not None:
# if load is a dict, replace the object dict with it (replaces the object with the specified one)
@@ -69,6 +72,11 @@ def __init__(self, name=None, column_names=None, column_types=None, primary_key=
self.pk = primary_key
# self._update()
+ #we will save in the database the unique constraint like the primary key constraint
+ if(unique is not None):
+ self.unique_idx=self.column_names.index(unique)
+ else:
+ self.unique_idx=None
# if any of the name, columns_names and column types are none. return an empty table object
@@ -536,10 +544,48 @@ def show(self, no_of_rows=None, is_locked=False):
# detect the rows that are no tfull of nones (these rows have been deleted)
# if we dont skip these rows, the returning table has empty rows at the deleted positions
non_none_rows = [row for row in self.data if any(row)]
+ #adding the header unique
+ if(hasattr(self,'unique_idx')):
+ if(self.unique_idx is not None and self.unique_idx