Skip to content
Merged
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
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,37 @@ High-level overview of todos/roadmap
| 4 | Basic Top-level SQL Parsing | ✔ |
| 5 | Stretch goals... TBD | ❌ |

- - -
## How-to run:

This project is built and executed with `Dune`. After cloning the repo, from the project root, run:

```bash
dune build
```

To start the REPL, run
```bash
dune exec SQCaml
```

You can then run SQL-style commands like:
```sql
INSERT INTO Mbta (id, stop_name, rail_line)
VALUES (100, 'Englewood ave.', 'G');
```

> [!IMPORTANT]
> The current implementaiton only has a fixed single-table schema of:
> ```SQL
> (id:int, stop_name:varchar, rail_line:varchar)
>```
> For a list of accepted commands, see [docs](./docs/commands.md)

- - -
## Acknowledgements
###### 1. A lot of the lower-level disk-writing API was reimplemented from the work by [Artjom Plaunov](https://artjomplaunov.github.io/database/b+/tree/2025/01/13/btrees1.html) - check out their work.
###### 2. Ideas were also taken from the well-known [Let's Build a Simple Database](https://cstack.github.io/db_tutorial/).
- - -

🐫︎
🐫︎
80 changes: 80 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Current working commands:

*Note, all commands are ended by a* `;`
*All commands are case sensitive*
- - -
## INSERT

##### Inserts data into storage engine

To insert data, start a command with INSERT, like:
```sql
-- INSERT INTO *table* (*fields, fields...*)
INSERT INTO ... (..., ..., ...)
-- VALUES (*data to insert*)
VALUES (...);
```

- Currently this is a fixed table schema of (id, stop_name, rail_line)


##### Example
```sql
-- Sample insert
INSERT INTO Mbta (id, stop_name, rail_line)
VALUES (100, 'Englewood ave.', 'G');
```

- Commands don't need newlines to run, so the above example could also be run like:
```sql
-- Sample insert
INSERT INTO Mbta (id, stop_name, rail_line) VALUES (100, 'Englewood ave.', 'G');
```


## SELECT

##### Queries data from the storage engine

To query data, start a command with SELECT, like:
```sql
-- SELECT FROM *table* (*fields, fields...*)
SELECT FROM ... (...);
```

##### Example
```sql
-- Sample query
SELECT FROM Mbta (stop_name); -- will return all stop names in db
```

#### Predicates

Currently a subset of predicates are supported:
- Equals: `==`
- Not equal: `<>`
- greater than, less than, etc. : `>`, `>=`, `<`, `<=`


```sql
-- SELECT FROM *table* (*fields...*) WHERE predicate...
SELECT FROM ... (...)
WHERE ... == ...;
```

##### Example
```sql
-- Sample query
SELECT FROM Mbta (stop_name)
WHERE rail_line <> 'G'; -- Returns all stops not on the greenline (thank god)
```
- - -
## Meta commands

- `.help;` -> Prints out help menu
- `.tree;` -> Prints out current tree layout and ids
- `.exit;` -> Exits SQCaml

- - -
That's all I have for now!
🐫︎
Empty file removed docs/design.md
Empty file.
Empty file removed docs/file_format.md
Empty file.
8 changes: 0 additions & 8 deletions lib/btree/btree.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
[@@@warning "-69"]

(* minimal B+ tree struct *)
type t = {
storage_m : Storage_manager.t; (* file used to store to disk *)
Expand All @@ -8,8 +6,6 @@ type t = {
mutable root_num : int;
}

[@@@warning "+69"]

let serialize (node : Nodes.t) (block_size : int) : Page.Page.t =
(* recursive iterative helpers*)
let rec layout_keys (nd : Nodes.t) (pg : Page.Page.t) pair_size i : unit =
Expand Down Expand Up @@ -131,8 +127,6 @@ let get_node_max_key (node : Nodes.t) : Keys.value =
else
node.keys.(node.cur_size - 1)

[@@@warning "-32"]

let empty_node (btree : t) : Nodes.t =
let block_size = File_manager.get_blocksize btree.storage_m.file_manager in
let key_type = btree.key in
Expand Down Expand Up @@ -182,8 +176,6 @@ let open_btree (storage_m : Storage_manager.t) (key_type : Keys.t) : t =
let root_node = deserialize root_page key_type block_size in
{ storage_m; key = key_type; root = root_node; root_num }

[@@@warning "-32"]

let print_tree (tree : t) : string =
let rec print_node (page_num : int) (indent : int) : string =
let node = get_node tree page_num in
Expand Down
1 change: 1 addition & 0 deletions lib/btree/btree.mli
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ val open_btree : Storage_manager.t -> Keys.t -> t
creates a new btree *)

val print_tree : t -> string
(** Takes in a btree [t] and prints out it's internal and leaf nodes *)

val create_new_root :
t ->
Expand Down
15 changes: 15 additions & 0 deletions lib/btree/keys.mli
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,25 @@ type value =
[@@deriving show]

val less_than : value -> value -> bool
(** Takes in two key values and returns the bool of v1 < v2 *)

val equals : value -> value -> bool
(** Takes in two key values and returns the bool of v1 = v2 *)

val leq_than : value -> value -> bool
(** Takes in two key values and returns the bool of v1 <= v2 *)

val greater_than : value -> value -> bool
(** Takes in two key values and returns the bool of v1 > v2 *)

val greq_than : value -> value -> bool
(** Takes in two key values and returns the bool of v1 >= v2 *)

val string_of_key : value -> string
(** Takes in key value [k] and returns a string of that key value *)

val size_of_key : t -> int
(** Returns in bytes the size of [key_type]*)

val empty_key : t -> value
(** Takes in [key_type] and creates one with an empty value *)
6 changes: 0 additions & 6 deletions lib/btree/nodes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ type node_type =
| Internal
[@@deriving show]

[@@@warning "-69"]

type t = {
(* There are 2 node types, leaf or internal *)
mutable node_t : node_type;
Expand All @@ -18,8 +16,6 @@ type t = {
}
[@@deriving show]

[@@@warning "+69"]

(* Constants for serialization*)
let leaf_serial = Int32.of_int 2863311530 (* 0xAAAAAAAA *)
let internal_serial = Int32.of_int 3149642683
Expand Down Expand Up @@ -63,8 +59,6 @@ let internal_node_child_pointer (node : t) (child_num : int) : int =
let num_keys = node.cur_size in
if child_num > num_keys then
failwith "Tried to access child num that's greater than num of keys!"
else if child_num = num_keys then
node.pointers.(num_keys)
else
node.pointers.(child_num)

Expand Down
12 changes: 12 additions & 0 deletions lib/btree/nodes.mli
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,20 @@ type t = {
(** Data structure for nodes *)

val serialize_node : node_type -> Int32.t
(** Takes in [node_t] node_type and serializes it to an Int32.t *)

val int32_to_node_t : Int32.t -> node_type
(** Takes in [i32] Int32.t and de-serializes it to node of node_type *)

val print_leaf_node : t -> string
(** Takes in a leaf node [n] and returns a string of it's Keys.value *)

val print_internal_node : t -> string
(** Takes in an internal node [n] and returns a string of it's Keys.value *)

val internal_node_child_pointer : t -> int -> int
(** Takes in an internal node [node] and an int [child_num] and returns a
pointer to that child node *)

val internal_node_key : t -> int -> Keys.value
(** Returns the key of an internal node *)
12 changes: 12 additions & 0 deletions lib/commands/select.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
type pred_op =
| Eq
| Neq
| Lt
| Gt
| Leq
| Geq
[@@deriving show]

type predicate = {
Expand Down Expand Up @@ -47,11 +51,19 @@ let comp_constants (op : pred_op) (left : Constant.t) (right : Constant.t) :
match op with
| Eq -> l = r
| Neq -> l <> r
| Lt -> l < r
| Gt -> l > r
| Leq -> l <= r
| Geq -> l >= r
end
| Constant.ConstStr l, Constant.ConstStr r -> begin
match op with
| Eq -> l = r
| Neq -> l <> r
| Lt -> l < r
| Gt -> l > r
| Leq -> l <= r
| Geq -> l >= r
end
| _ -> failwith "Bad!"

Expand Down
8 changes: 0 additions & 8 deletions lib/cursor/cursor.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
(* struct/module for pointing to rows in a table *)

(*This will be what is used to do leaf searching and full-table scans *)
type t = {
tree : Btree.t;
Expand Down Expand Up @@ -104,7 +102,6 @@ let rec internal_node_find (tree : Btree.t) (page_num : int) (key : Keys.value)
| Nodes.Internal -> internal_node_find tree child_page_num key
end

(* returns the position of the cursor at said key*)
let tree_find (tree : Btree.t) (key : Keys.value) : t =
let root_node = Btree.get_node tree tree.root_num in
match root_node.node_t with
Expand Down Expand Up @@ -143,7 +140,6 @@ let distribute_node_keys ~(old_node : Nodes.t) ~(new_node : Nodes.t)
in
loop (total_entries - 1)

(* Create a new node and move half the cells and update parent *)
let leaf_node_split_and_insert (cursor : t) (key : Keys.value)
(value_pointer : int) : unit =
let tree = cursor.tree in
Expand Down Expand Up @@ -235,10 +231,6 @@ let leaf_node_insert (cursor : t) (key : Keys.value) (value_pointer : int) :
end

let tree_start (tree : Btree.t) : t =
(* let root_node = Btree.get_node tree tree.root_num in *)
(* let num_cells = root_node.cur_size in *)
(* let eot = num_cells = 0 in *)

(*cursor at key (id) 0: HACKY *)
let cursor = tree_find tree (Keys.Integer (Int32.of_int 0)) in
let node = Btree.get_node tree cursor.page_num in
Expand Down
31 changes: 29 additions & 2 deletions lib/cursor/cursor.mli
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,26 @@ type t = {
}

val make : ?end_of_table:bool -> Btree.t -> t
(** Takes in option [end_of_table] and btree [tree] to create a cursor *)

val cursor_value : t -> int
(** Takes in a cursor and returns the value at the leaf node it's pointing at *)

val cursor_advance : t -> unit
val tree_start : Btree.t -> t
val collect_keys : Btree.t -> int list
(** Takes in a cursor and advances the cell it's pointing at *)

val leaf_node_find : Btree.t -> int -> Keys.value -> t
(** Takes in a Btree [tree] [page_num]int and [key] key value to return a cursor
pointing at the leaf directed by the key and page num *)

val internal_node_find : Btree.t -> int -> Keys.value -> t
(** At a internal node: takes in a Btree, [tree] [page_num]int and [key] key
value to return a cursor pointing at the leaf directed by the key and page
num *)

val tree_find : Btree.t -> Keys.value -> t
(** Takes btree [tree] and key_val [key] returns the position of the cursor at
said [key]*)

val distribute_node_keys :
old_node:Nodes.t ->
Expand All @@ -26,6 +39,20 @@ val distribute_node_keys :
left_count:int ->
total_entries:int ->
unit
(** Takes in an oldnode and new node upon a split and distributes nodes and
pointers for the split *)

val leaf_node_split_and_insert : t -> Keys.value -> int -> unit
(** Takes in a cursor and key value upon a split and creates a new node and
moves half the cells and update parent *)

val leaf_node_insert : t -> Keys.value -> int -> unit
(** Takes in a cursor, key_value, and pointer and inserts into a leaf node *)

val tree_start : Btree.t -> t
(** Takes in a btree [tree] and returns a cursor pointed at the start (left-most
side) of the tree *)

val collect_keys : Btree.t -> int list
(** Takes in a btree [tree] and moves the cursor along, adding the keys to an
int list to return *)
2 changes: 0 additions & 2 deletions lib/db_session.ml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ type t = {
mutable index : Btree.t;
}

(* let block_size = 4096 *)
(* let block_size = 128 *)
let storage_file = "sqcaml.db"

let open_db ?(block_size = 256) (db_dir : string) : t =
Expand Down
7 changes: 0 additions & 7 deletions lib/interpreter.ml
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,6 @@ let _float_step (bop : Ast.binop_t) (e1 : float) (e2 : float) =
| (Ast.Lt | Ast.Gt | Ast.Leq | Ast.Geq | Ast.Neq | Ast.Comp), _, _ ->
_num_comp_step bop e1 e2

(* let _table_step (table : Ast.table) = *)
(* match table with *)
(* | Ast.Id id -> Ast.String id *)

(** [step e] takes a single step of eval of [e]*)
let rec step e : Ast.expr =
match e with
Expand All @@ -84,7 +80,6 @@ let rec step e : Ast.expr =
binop_step op e1 e2
| Ast.Binop (op, e1, e2) when is_value e1 -> Ast.Binop (op, e1, step e2)
| Ast.Binop (op, e1, e2) -> Ast.Binop (op, step e1, e2)
(* | Ast.Table id -> _table_step id *)

(* handles int and float by converting int -> float *)
and binop_step (bop : Ast.binop_t) (e1 : Ast.expr) (e2 : Ast.expr) =
Expand Down Expand Up @@ -123,9 +118,7 @@ let execute_meta (db : Db_session.t) (md : Ast.meta_command) : execution_t =
| Ast.Unk_mcmd _ -> Error "unk meta command: Failure"

let execute_statement (db : Db_session.t) (stmt : Ast.statement) : execution_t =
(* Need to improve this once the B+ is implemented *)
match stmt with
(* | Ast.Create _ -> Ok *)
| Ast.Insert i -> Message (Insert.execute_insert db i)
| Ast.Select s -> Message (Select.execute_select ?prepped_select:(Some s) db)
| Ast.Expr e -> Message (e |> eval |> string_of_val)
Expand Down
Loading
Loading