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

+

mdblogo

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