Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2ce6eb8
added thoma comment
Jan 15, 2023
80e7ad3
changed the comment
Harris-Kousiavelos Jan 15, 2023
50e7ee8
changed the comment
Harris-Kousiavelos Jan 15, 2023
dc2b483
Init of evaluate_where_clause function
Jan 31, 2023
a82fe52
added basic operators list
Feb 1, 2023
1fdd7c8
completed where without parenthesis
Feb 1, 2023
5e5da15
added functionality for nested expressions
Feb 1, 2023
741e694
Added functionallity in where statement
Feb 4, 2023
e76c23c
Changed the execute_dic function
Feb 4, 2023
4233250
Trying to implement the select using complex where statement
Feb 4, 2023
46a1668
First implementation of the functionality of and/or
Feb 9, 2023
a662dd2
Added todo comment
Feb 9, 2023
33b4a6d
added comments
Feb 12, 2023
2f97f10
Merge pull request #1 from anthonyrouss/feature/enrich-where/and-or
roussosan Feb 12, 2023
d360c24
Changed the query plan of the create index query
Harris-Kousiavelos Feb 12, 2023
ab76248
Added between functionality
dpribas Feb 12, 2023
33d8837
changed the create table query plan (added unique)
Feb 12, 2023
22b6527
Added unique column functionality
Feb 12, 2023
56d5d97
Added ifs at create_index
Feb 12, 2023
6e4b9b8
changed the create_index code
Feb 15, 2023
01ff7da
hash constructor
Harris-Kousiavelos Feb 15, 2023
a927e81
removed hash check on index creation
Feb 15, 2023
b2b88c7
Added new column for meta_indexes db
Feb 16, 2023
a644016
added hash index
Harris-Kousiavelos Feb 16, 2023
e49c2f5
Changed _has_index function to also check for table_column
Feb 16, 2023
1fb36ba
Changes on db select function
Feb 17, 2023
4954899
Removed unnecessary prints
Feb 17, 2023
309f076
Fixed parenthesis check in form_where_clause
Feb 17, 2023
a20a375
Fixed exception with meta_indexes table
Feb 17, 2023
f436120
Changed meta_indexes
Feb 17, 2023
75baf48
added not functionality
dpribas Feb 18, 2023
e44dc32
.
Feb 18, 2023
e2dffc2
Merge not-between branch into and-or
Feb 18, 2023
fd70a12
Changed the _select_where to check for the supported btrees on all co…
Feb 18, 2023
eec0a52
Added comments to methods
Feb 18, 2023
07ac071
Merge master
Feb 18, 2023
2cd4c5a
changes 19feb
Feb 19, 2023
1e830a8
update btree when inserting a new row
Feb 20, 2023
ac9437c
init hash
Feb 20, 2023
f619fed
added functionality for creating hash index on pk
Feb 20, 2023
b7c72cd
Changed create index dictionary
Feb 20, 2023
6ddbb27
Merge btree branch
Feb 20, 2023
2845288
Implemented hash index functionality
Feb 20, 2023
920cef9
Changed the _delete_where method
Feb 20, 2023
d1013d2
Fixed some errors in _delete_from
Feb 20, 2023
1341df6
Added RA functionality
dpribas Feb 20, 2023
2d9aefe
Bug fix on RA functionality
dpribas Feb 20, 2023
f7557f4
Merge with RA branch
Feb 20, 2023
b054d9d
Final changes
Feb 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ dbdata/
__pycache__/
.idea/
.pytest_cache
test.py
todo.txt
test/
164 changes: 134 additions & 30 deletions mdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@
import readline
import traceback
import shutil

sys.path.append('miniDB')

from database import Database
from table import Table
from misc import convert_to_RA
# art font is "big"
art = '''
_ _ _____ ____
(_) (_)| __ \ | _ \
_ _ _____ ____
(_) (_)| __ \ | _ \
_ __ ___ _ _ __ _ | | | || |_) |
| '_ ` _ \ | || '_ \ | || | | || _ <
| '_ ` _ \ | || '_ \ | || | | || _ <
| | | | | || || | | || || |__| || |_) |
|_| |_| |_||_||_| |_||_||_____/ |____/ 2022
'''
|_| |_| |_||_||_| |_||_||_____/ |____/ 2022
'''


def search_between(s, first, last):
Expand All @@ -37,7 +39,6 @@ def in_paren(qsplit, ind):
'''
return qsplit[:ind].count('(')>qsplit[:ind].count(')')


def create_query_plan(query, keywords, action):
'''
Given a query, the set of keywords that we expect to pe present and the overall action, return the query plan for this query.
Expand All @@ -53,31 +54,32 @@ def create_query_plan(query, keywords, action):
kw_positions = []
i=0
while i<len(ql):
if in_paren(ql, i):
if in_paren(ql, i):
i+=1
continue
if ql[i] in keywords:
kw_in_query.append(ql[i])
kw_positions.append(i)

elif i!=len(ql)-1 and f'{ql[i]} {ql[i+1]}' in keywords:
kw_in_query.append(f'{ql[i]} {ql[i+1]}')
ql[i] = ql[i]+' '+ql[i+1]
ql.pop(i+1)
kw_positions.append(i)
i+=1




for i in range(len(kw_in_query)-1):
dic[kw_in_query[i]] = ' '.join(ql[kw_positions[i]+1:kw_positions[i+1]])

if action == 'create view':
dic['as'] = interpret(dic['as'])

if action=='select':
dic = evaluate_from_clause(dic)

if dic['where'] is not None:
dic = evaluate_where_clause(dic)

if dic['distinct'] is not None:
dic['select'] = dic['distinct']
dic['distinct'] = True
Expand All @@ -89,41 +91,62 @@ def create_query_plan(query, keywords, action):
else:
dic['desc'] = False
dic['order by'] = dic['order by'].removesuffix(' asc').removesuffix(' desc')

else:
dic['desc'] = None

if action=='create table':
args = dic['create table'][dic['create table'].index('('):dic['create table'].index(')')+1]
dic['create table'] = dic['create table'].removesuffix(args).strip()
arg_nopk = args.replace('primary key', '')[1:-1]

arglist = [val.strip().split(' ') for val in arg_nopk.split(',')]

dic['column_names'] = ','.join([val[0] for val in arglist])
dic['column_types'] = ','.join([val[1] for val in arglist])

if 'primary key' in args:
arglist = args[1:-1].split(' ')
dic['primary key'] = arglist[arglist.index('primary')-2]
else:
dic['primary key'] = None

# Check if there are unique keywords
if 'unique' in args:
arglist = args[1:-1].split(' ')
# Get the column names
column_names = [arglist[i-2] for i, kw in enumerate(arglist) if kw.rstrip(',') == 'unique']
dic['unique'] = ','.join(column_names)
else:
dic['unique'] = None

if action=='import':
if action == 'delete from':
if dic['where'] is not None:
dic = evaluate_where_clause(dic)

if action=='import':
dic = {'import table' if key=='import' else key: val for key, val in dic.items()}

if action=='insert into':
if dic['values'][0] == '(' and dic['values'][-1] == ')':
dic['values'] = dic['values'][1:-1]
else:
raise ValueError('Your parens are not right m8')

if action=='unlock table':
if dic['force'] is not None:
dic['force'] = True
else:
dic['force'] = False

return dic

if action=='create index':
on_value = dic["on"].split(" ")

if len(on_value) == 4 and on_value[1] == '(' and on_value[-1]==')':
dic['on'] = on_value[0]
dic['column'] = on_value[2]

return dic

def evaluate_from_clause(dic):
'''
Expand Down Expand Up @@ -157,9 +180,79 @@ def evaluate_from_clause(dic):
join_dic['right'] = interpret(join_dic['right'][1:-1].strip())

dic['from'] = join_dic


return dic

def has_parenth(list):
return list[0]=='(' and list[-1]==')'

def evaluate_where_clause(dic):
'''
Evaluate the part of the query that is supplied as the 'where' argument
'''
where_clause = dic['where']
where_split = where_clause.split(' ')
if len(where_split) == 1:
where_dic = where_clause
else:
where_dic = form_where_clause(where_clause)

dic['where'] = where_dic

return dic

def form_where_clause(where_split):
'''
Evaluate the recursive part of the where clause. Returns a dictionary
'''
logical_operators = ['and', 'or', 'between', 'not']

if(type(where_split) != list):
where_split = where_split.split(' ')

operator_idx = [i for i,word in enumerate(where_split) if word in logical_operators and not in_paren(where_split,i)]

if len(operator_idx) == 0 and has_parenth(where_split):
while has_parenth(where_split):
where_split = where_split[1:-1]
operator_idx = [i for i,word in enumerate(where_split) if word in logical_operators and not in_paren(where_split,i)]

if len(operator_idx) == 0:
return ''.join(where_split)

if (where_split[operator_idx[0]]) == "between":
left = where_split[operator_idx[0]+1]
right = where_split[operator_idx[0]+3]
left = where_split[operator_idx[0]-1] + ">=" + left
right = where_split[operator_idx[0]-1] + "<=" + right
where_split[operator_idx[0]+1] = left
where_split[operator_idx[0]+3] = right
del where_split[0]
del where_split[0]

operator_idx_f = operator_idx[0]
where_dic = {}
left = where_split[:operator_idx_f]
right = where_split[operator_idx_f+1:]

if (where_split[operator_idx[0]]) == "not":
left = None
elif has_parenth(left):
left = form_where_clause(left)
else:
left = ' '.join(left)

if has_parenth(right) or operator_idx_f is not None:
right = form_where_clause(right)
else:
right = ' '.join(right)

where_dic['left'] = left
where_dic['operator'] = ''.join(where_split[operator_idx_f])
where_dic['right'] = right

return where_dic

def interpret(query):
'''
Interpret the query.
Expand All @@ -175,14 +268,14 @@ def interpret(query):
'unlock table': ['unlock table', 'force'],
'delete from': ['delete from', 'where'],
'update table': ['update table', 'set', 'where'],
'create index': ['create index', 'on', 'using'],
'create index': ['create index', 'on', 'column', 'using'],
'drop index': ['drop index'],
'create view' : ['create view', 'as']
}

if query[-1]!=';':
query+=';'

query = query.replace("(", " ( ").replace(")", " ) ").replace(";", " ;").strip()

for kw in kw_per_action.keys():
Expand All @@ -195,13 +288,17 @@ def execute_dic(dic):
'''
Execute the given dictionary
'''

for key in dic.keys():
if isinstance(dic[key],dict):
# Skip the where key
if key != 'where' and isinstance(dic[key],dict):
dic[key] = execute_dic(dic[key])

action = list(dic.keys())[0].replace(' ','_')

return getattr(db, action)(*dic.values())


def interpret_meta(command):
"""
Interpret meta commands. These commands are used to handle DB stuff, something that can not be easily handled with mSQL given the current architecture.
Expand All @@ -224,15 +321,15 @@ def interpret_meta(command):

def list_databases(db_name):
[print(fold.removesuffix('_db')) for fold in os.listdir('dbdata')]

def list_tables(db_name):
[print(pklf.removesuffix('.pkl')) for pklf in os.listdir(f'dbdata/{db_name}_db') if pklf.endswith('.pkl')\
and not pklf.startswith('meta')]

def change_db(db_name):
global db
db = Database(db_name, load=True, verbose=verbose)

def remove_db(db_name):
shutil.rmtree(f'dbdata/{db_name}_db')

Expand All @@ -252,25 +349,27 @@ def remove_db(db_name):

db = Database(dbname, load=True)



if fname is not None:
for line in open(fname, 'r').read().splitlines():
if line.startswith('--'): continue
if line.startswith('explain'):
dic = interpret(line.removeprefix('explain '))
pprint(dic, sort_dicts=False)
else :
if line.startswith('convert'):
dic = convert_to_RA(interpret(line.removeprefix('convert ')))
pprint(dic, sort_dicts=False)
else:
dic = interpret(line.lower())
result = execute_dic(dic)
if isinstance(result,Table):
result.show()


from prompt_toolkit import PromptSession
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory


print(art)
session = PromptSession(history=FileHistory('.inp_history'))
while 1:
Expand All @@ -282,17 +381,22 @@ def remove_db(db_name):
print('\nbye!')
break
try:
if line=='exit':
if line=='exit;':
break
if line.split(' ')[0].removesuffix(';') in ['lsdb', 'lstb', 'cdb', 'rmdb']:
interpret_meta(line)
elif line.startswith('explain'):
dic = interpret(line.removeprefix('explain '))
pprint(dic, sort_dicts=False)
elif line.startswith('convert'):
RA_expression = convert_to_RA(interpret(line.removeprefix('convert ')))
print(RA_expression)
else:
dic = interpret(line)
result = execute_dic(dic)
if isinstance(result,Table):
result.show()
except Exception:
print(traceback.format_exc())


Loading