Insert and split — the tree grows — separator keys promoted to the parent page

An INSERT writes one cell when there's room; when the leaf is full it splits and a separator key is promoted to the parent — splits cascade upward, and only a root split adds a level.

Previously

Scene 4 left the cursor finding any leaf in 3 page reads. Now we actually run the INSERT — and watch what the leaf does when it has, and doesn't have, room.

Scene 05

Insert and split — the tree grows

  1. Watch
  2. Try it
  3. Predict
  4. Capture
TREE — depth 3p1 (root)p4p5p12p17 (leaf)p22p31p44LEAF ZOOM — p17 (leaf)page_size = 4096 BCELL POINTER ARRAY → grows downptr→ #11 @ offset 11unallocated free space↑ CELL CONTENT AREA ← grows up#11 FILL METER100%90% split60%0%now13%1 / 8 cells usedIN-PLACE INSERT1 cell in the leaf · room for 7 more.Room in the leaf → one cell written. Tree shape unchanged.
What to watch for

Watch INSERT rowid=42 land in the leaf. Each tick adds one row: a new cell goes into the cell content area (bottom), a new pointer goes into the cell-pointer array (top), and the fill meter climbs.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

Leaf.insert
the in-place case — a cell + a pointer, no structural change
def insert(leaf, cell):
if leaf.free_bytes >= cell.size:
offset = leaf.alloc_in_content_area(cell.size)
leaf.write_cell(offset, cell)
leaf.cell_pointer_array.insert_sorted(offset, cell.key)
return
split_leaf(leaf, cell) # no room → split
split_leaf
allocate sibling, redistribute cells, promote a separator
def split_leaf(leaf, new_cell):
sibling = pager.allocate_page(kind='leaf')
median = redistribute(leaf, sibling, new_cell)
sep_key = median.key
promote_to_parent(leaf.parent, sep_key, sibling)
promote_to_parent
cascade upward; root split is the only way depth grows
def promote_to_parent(parent, sep_key, new_child):
if parent is None: # we were the root
new_root = pager.allocate_page(kind='root')
new_root.add_child(old_root)
new_root.add_separator(sep_key, new_child)
tree.depth += 1 # the only path that gains depth
return
if parent.has_room_for(sep_key):
parent.insert_separator(sep_key, new_child)
return
split_interior(parent, sep_key, new_child) # cascade

Where this sits in Build a B-tree storage engine (SQLite-style)

Scene 05 of 11, in the Mutations act — Insert, split, delete — and why the file doesn't shrink.. An INSERT writes one cell when there's room; when full, the leaf splits and a separator promotes to the parent — cascades grow the tree.

Up next. Inserts grow the tree neatly. Deletes — surprisingly — don't undo that growth. The next half-step shows why your file stays huge after a big DELETE and what VACUUM is for.

All 11 scenes in Build a B-tree storage engine (SQLite-style) · Every curriculum

Built with Arqly
Every scene in Build a B-tree storage engine (SQLite-style) builds on the one before it.All 11 Build a B-tree storage engine (SQLite-style) scenes