!!! Code written during my undergraduate years (a complete mess)… when will I ever refactor this?
- Designed the data I/O flow to follow the order: Index layer → Buffer layer → Disk layer
- The Index layer and Buffer layer operate entirely in memory
- The Disk space layer performs disk I/O via system calls
Build a database system that supports data storage and basic operations such as insert, delete, and find, with concurrency control.
There are two main parts in the final disk-based B+ tree implementation.
-
The first part provides an overview and brief explanation of the B+ tree:
- Introduction to the index layer (open, insert, find, delete)
- Introduction to the file API (write, read, allocate, etc.)
- Introduction to supporting sub-functions for the file API
-
The second part consists of diagrams explaining the role of each function and the overall design of the disk-based B+ tree.
open_tableuses a system call such as
if (0 < (fd = open(pathname, O_RDWR | O_CREAT | O_EXCL, 0644))),
which opens an existing table or creates a new one if it does not exist- After creation, it calls the
page_settingfunction to define the page size and initialize the header page at the first position- Only the header page is created initially
- Opens the file and retrieves the target page number using the key
- Initially, only the header page exists
- Uses the header to obtain a free or mapped page for insertion
- Checks for duplicate keys and determines whether page splitting is required
- Writes the key to the appropriate position and initializes it as the root if needed
- Opens the file and retrieves the page number using the key
- Searches for the key using the
file_readAPI- Returns 0 if the key is found, or -1 if the key does not exist
- Opens the file and retrieves the page number using the key
- Finds the correct position using the search function
- Deletes data by overwriting existing values
- Decreases the number of keys in the page and checks whether rebalancing is required
- Executes
fsync()to ensure persistence
- Retrieves the root page using the header page
- Internal pages (including the root) store keys and page IDs, while leaf pages store keys and values
- Determines whether a page is a leaf
- If the page is a leaf, prints all keys and values
- If the page is an internal node, prints only keys (not page IDs)
- Printing proceeds from root to leaf in level-order traversal
- An additional function (
sub_print) is used to print sibling nodes and supportsdb_print
- Checks the root from the header page
- If a free page exists in the header, it returns that page and updates the next free page
- Otherwise, creates a new free page; the first free page is placed immediately after the header
- Finds subsequent free pages using the
find_free_page()function
- Checks the number of keys and the
is_leafflag of the given page- Sets the
is_leafflag appropriately (1 for leaf, 0 for internal)- Writes data directly if the page already exists
- Performs linear sorting while writing
- If the page is new, writes data to the first position
- An additional API used exclusively for writing internal pages after splitting
- When a split occurs, writes the left child’s key and page number to the parent
- Copies keys and page numbers from the left child
- Writes them to the correct position in the parent page
- Handles special logic for leftmost pages
- Opens the file and searches using the provided page parameter
- Uses binary search
- Returns 0 if the key is found, or -1 otherwise
- Checks the return value of the search result
- Invoked when a page split is required
- Allocates a new child and parent page using
file_alloc_page()- If no parent exists, calls
insert_new_parent()- Updates parent page numbers for both new and old pages
- Calls
write_internal_page()- Increments the key count and sets sibling pointers
- Used for searching both leaf and internal pages
- Based on a binary search algorithm
- Checks whether the page is a leaf and whether it is leftmost
- Supports searching in leaf pages, normal internal pages, and leftmost internal pages
- Returns the correct position if found, or -1 otherwise
- Retrieves the root page initially
- Checks whether the root exists
- Allocates a new page and sets it as the root if none exists
- Calls
find_leaf_page()if the root exists- Returns the resulting page number
- Retrieves
num_keysandis_leaf- Returns the page immediately if it is a leaf
- Otherwise, determines whether the page is leftmost
- Continues searching from the root until a leaf page is found
- Sorts keys and values upon insertion
- Compares keys sequentially to find the correct position
- Shifts existing keys backward to make space
- Writes the key and value at the correct position
- Allocates a new child page using
file_alloc_page()- Checks whether the old page is a leaf
- Splits leaf pages or internal pages accordingly
- Splits at half of
num_keys- Returns the new child page number
- Delayed merge is executed only when a page has no keys
- Checks the right sibling and moves data from it
- If no sibling exists, no merge is performed
- After removing data from a leaf page, checks whether a parent exists
- If a parent exists, determines whether it is leftmost
- Finds and removes the corresponding key in the parent using
search_key()- Recursively checks and updates ancestor pages
- Mapping tables (header, free, internal/leaf)
- File open wrapper with error checking
- Synchronization functions for
fsync()and error handlinglltoa,itoafunctions for integer anduint64_tto string conversionkeys_plusfunction for incrementing key counts
- The code consists of three components:
main,file.c, andfile.h - The main module includes open, find, delete, insert, and print operations
file.hserves as the header filefile.ccontains three categories of functions:- Index layer functions
- File operation functions
- Supporting utility functions
- Insert operations invoke at least four functions
(db_insert → get_page_number → get_mapping_table → write → sort) - Find operations invoke at least four functions
(db_find → get_page_number → get_mapping_table → search → read) - Delete operations invoke at least three functions
(db_delete → get_page_number → search → delete) - Split and merge operations may also be triggered
- Split condition:
num_keys < leaf_order - fill_factor - Merge condition:
num_keys == 0and a sibling exists
- Split condition:
