Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"python.analysis.extraPaths": [
"./miniDB"
]
}
Empty file added docs/student documentation
Empty file.
Binary file added docs/studentsdocumentation .pdf
Binary file not shown.
7 changes: 4 additions & 3 deletions mdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

from database import Database
from table import Table
# art font is "big"
# art font is "big"1
art = '''
_ _ _____ ____
(_) (_)| __ \ | _ \
_ __ ___ _ _ __ _ | | | || |_) |
_ __ ___ _ _ __ _ | | | || |_) e
| '_ ` _ \ | || '_ \ | || | | || _ <
| | | | | || || | | || || |__| || |_) |
|_| |_| |_||_||_| |_||_||_____/ |____/ 2022
Expand Down Expand Up @@ -170,14 +170,15 @@ def interpret(query):
'import': ['import', 'from'],
'export': ['export', 'to'],
'insert into': ['insert into', 'values'],
'select': ['select', 'from', 'where', 'distinct', 'order by', 'limit'],
'select': ['select', 'from', 'where', 'AND','OR', 'distinct', 'order by', 'limit'],
'lock table': ['lock table', 'mode'],
'unlock table': ['unlock table', 'force'],
'delete from': ['delete from', 'where'],
'update table': ['update table', 'set', 'where'],
'create index': ['create index', 'on', 'using'],
'drop index': ['drop index'],
'create view' : ['create view', 'as']

}

if query[-1]!=';':
Expand Down
80 changes: 77 additions & 3 deletions miniDB/btree.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ class Node:
def __init__(self, b, values=None, ptrs=None,left_sibling=None, right_sibling=None, parent=None, is_leaf=False):
self.b = b # branching factor
self.values = [] if values is None else values # Values (the data from the pk column)
self.keys = []
self.children = []
self.ptrs = [] if ptrs is None else ptrs # ptrs (the indexes of each datapoint or the index of another bucket)
self.left_sibling = left_sibling # the index of a buckets left sibling
self.right_sibling = right_sibling # the index of a buckets right sibling
self.parent = parent # the index of a buckets parent
self.is_leaf = is_leaf # a boolean value signaling whether the node is a leaf or not


def find(self, value, return_ops=False):
'''
Returns the index of the next node to search for a value if the node is not a leaf (a ptrs of the available ones).
Expand Down Expand Up @@ -57,6 +59,7 @@ def insert(self, value, ptr, ptr1=None):
ptr: float. The ptr of the inserted value (e.g. its index).
ptr1: float. The 2nd ptr (e.g. in case the user wants to insert into a nonleaf node).
'''

# for each value in the node, if the user supplied value is smaller, insert the value and its ptr into that position
# if a second ptr is provided, insert it right next to the 1st ptr
# else (no value in the node is larger) append value and ptr/s to the back of the list.
Expand All @@ -68,7 +71,7 @@ def insert(self, value, ptr, ptr1=None):
self.ptrs.insert(index+1, ptr)

if ptr1:
self.ptrs.insert(index+1, ptr1)
self.ptrs.insert(index+2, ptr1)
return
self.values.append(value)
self.ptrs.append(ptr)
Expand Down Expand Up @@ -239,7 +242,7 @@ def show(self):
self.nodes[ptr].show()
print('----')


#test
def plot(self):
## arrange the nodes top to bottom left to right
nds = []
Expand Down Expand Up @@ -346,3 +349,74 @@ def find(self, operator, value):
# print the number of operations (usefull for benchamrking)
# print(f'With BTree -> {ops} comparison operations')
return results
'''
creating Row class which is responsible fora single row in a table, and is defined with two attributes
table and values
'''
class Row:
def __init__(self, table, values):
self.table = table
self.values = values
'''
table class responsible for class .. with specific attributes and methods
'''
class Table:
def __init__(self, name, columns):
self.name = name
self.columns = columns
self.rows = []
self.primary_key_index = None
self.unique_indexes = {}
'''
insert fior unique columns
takes a list of values and inserts a new row into the table
'''
def insert(self, values):
if len(values) != len(self.columns):
raise ValueError("the number of values is not the same as columns number")
row = Row(self, values)
for column in self.columns:
if column.is_primary_key:
if self.primary_key_index is None:
self.primary_key_index = Btree(column)
self.primary_key_index.insert(row)
elif column.is_unique:
if column.name not in self.unique_indexes:
self.unique_indexes[column.name] = Btree(column)
self.unique_indexes[column.name].insert(row)
self.rows.append(row)
'''
this method returns a list of rows from the table based on column names kai sto where clause
'''
def select(self, column_names=None, where=None):
if where is None:
rows = self.rows
else:
rows = []
for row in self.rows:
if where.matches(row):
rows.append(row)
if column_names is None:
return [row.values for row in rows]
else:
indices = [self.get_index(column_name) for column_name in column_names]
result = []
for row in rows:
result.append([row.values[index] for index in indices])
return result
'''
get_index method a method that returns the index object for a given column name
raises errror if column doesnt exist in the table...
'''
def get_index(self, column_name):
if column_name in [column.name for column in self.columns]:
for column in self.columns:
if column.name == column_name:
if column.is_primary_key:
return self.primary_key_index
elif column.is_unique:
return self.unique_indexes[column.name]
else:
return None
else:
raise ValueError(f"this column {column_name} does not exist in this table {self.name}")
14 changes: 7 additions & 7 deletions miniDB/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,10 +510,10 @@ def lock_table(self, table_name, mode='x'):

try:
pid = self.tables['meta_locks']._select_where('pid',f'table_name={table_name}').data[0][0]
if pid!=os.getpid():
raise Exception(f'Table "{table_name}" is locked by process with pid={pid}')
else:
return False
#if pid!=os.getpid():
#raise Exception(f'Table "{table_name}" is locked by process with pid={pid}')
#else:
#return False

except IndexError:
pass
Expand All @@ -540,8 +540,8 @@ def unlock_table(self, table_name, force=False):
try:
# pid = self.select('*','meta_locks', f'table_name={table_name}', return_object=True).data[0][1]
pid = self.tables['meta_locks']._select_where('pid',f'table_name={table_name}').data[0][0]
if pid!=os.getpid():
raise Exception(f'Table "{table_name}" is locked by the process with pid={pid}')
#if pid!=os.getpid():
# raise Exception(f'Table "{table_name}" is locked by the process with pid={pid}')
except IndexError:
pass
self.tables['meta_locks']._delete_where(f'table_name={table_name}')
Expand Down Expand Up @@ -635,7 +635,7 @@ def _get_insert_stack_for_table(self, table_name):
table_name: string. Table name (must be part of database).
'''
return self.tables['meta_insert_stack']._select_where('*', f'table_name={table_name}').column_by_name('indexes')[0]
# res = self.select('meta_insert_stack', '*', f'table_name={table_name}', return_object=True).indexes[0]
#res = self.select('meta_insert_stack', '*', f'table_name={table_name}', return_object=True).indexes[0]
# return res

def _update_meta_insert_stack_for_tb(self, table_name, new_stack):
Expand Down
54 changes: 54 additions & 0 deletions miniDB/hashtesting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# import hashlib

#would work in theory B) ^v_v^


# class HashTable(object):
# def __init__(self, size=10)

# self.num_elements = 0
# self.data = [0] * size
# self.size = len(self.data)
# print(self.data)

# def __get_hash_index(self):
# test_hash = int(blah blah)
# return test_hash % self.size

# # making insert method
# def insert(self, key, value):
# '''
# inserting data
# key(str) , value(tuple) ,
# '''
# hash_data = (key, value)
# hash_index = self.__get_hash__index(key)
# self.data[hash_index] = hash_data


# # getter
# def get(self,key):
# '''data from key'''
# hash_index = self.__get__hash_index(key)
# #compare with first element of the list
# if key != self.data[hash_index][0] or self.data[hash_index] ==0
# datakey[0]
# if key !=datakey or data ==0:
# raise KEYeRROR("cant hash that key or no data")
# return data[1]

# #similar for remove
# def remove(self, key):
# hash_index = self.__get__hash_index(key)
# #compare with first element of the list
# if key != self.data[hash_index][0] or self.data[hash_index] ==0
# datakey[0]
# if key !=datakey or data ==0:
# raise KEYeRROR("cant hash that key or no data")
# self.data[hash_index] = 0


# def key_contains(self, substring):


# test_hash_table = HashTable()
4 changes: 2 additions & 2 deletions miniDB/joins.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self, condition, left_table, right_table, index, index_saved):

def join(self):
# Get the column of the left and right tables and the operator, from the condition of the join
column_name_left, operator, column_name_right = Table()._parse_condition(self.condition, join=True)
column_name_left, column_name_right, operator, column_name_right,isnot = Table()._parse_condition(self.condition, join=True)

reversed = False
# If we have the index of the left table, reverse the order of the tables
Expand Down Expand Up @@ -73,7 +73,7 @@ def __init__(self, condition, left_table, right_table):

def join(self):
# Get the column of the left and right tables and the operator, from the condition of the join
column_name_left, operator, column_name_right = Table()._parse_condition(self.condition, join=True)
column_name_left,column_name_right, operator, column_name_right,isnot = Table()._parse_condition(self.condition, join=True)
column_index_left = self.left_table.column_names.index(column_name_left)
column_index_right = self.right_table.column_names.index(column_name_right)

Expand Down
14 changes: 7 additions & 7 deletions miniDB/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ def get_op(op, a, b):
'<': operator.lt,
'>=': operator.ge,
'<=': operator.le,
'=': operator.eq}
'=': operator.eq,
'and':operator.and_,
'or':operator.or_}

try:
return ops[op](a,b)
Expand All @@ -29,12 +31,10 @@ def split_condition(condition):

if right[0] == '"' == right[-1]: # If the value has leading and trailing quotes, remove them.
right = right.strip('"')
elif ' ' in right: # If it has whitespaces but no leading and trailing double quotes, throw.
raise ValueError(f'Invalid condition: {condition}\nValue must be enclosed in double quotation marks to include whitespaces.')

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.')

#elif ' ' in right: # If it has whitespaces but no leading and trailing double quotes, throw.
# raise ValueError(f'Invalid condition: {condition}\nValue must be enclosed in double quotation marks to include whitespaces.')
#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):
Expand Down
Loading