droid
Overflow Pages for Large Values
Memory and Space · Stage 7 of 8

Overflow Pages for Large Values

A value bigger than a page keeps its start in the leaf and the rest in a chain of pages of its own

Lift the 1000-byte row limit. A row over 1000 bytes keeps its first 996 bytes and the number of its first overflow page in its cell; the rest of the row fills a chain of overflow pages, 4000 bytes each. INSERT writes the chain, SELECT reads it back, and DELETE, UPDATE and DROP TABLE give its pages to the free list.

Not run yet · 12 tests
Concepts in this stage
  • A cell must stay small so a leaf can always split; a value has no such reason to
  • Overflow: keep a fixed prefix in the cell, and the rest in a linked chain of pages
  • A row's own size says whether a chain follows, so no flag is needed
  • Chains are allocated and freed through the free list, like any page
  • How much to keep inline is a trade-off between leaf capacity and extra reads
Where this stage sits in the engine
Talking to the user
REPL
Sessions
Printer
Server
Wire protocol
Understanding SQL
LexerParserValidator
Answering a query
PlannerExecutor
Keeping changes safe
Transactions
Write-ahead log
Undo log
Lock manager
Read views
Storing rows
Row format
Slotted page
B+Tree
Reaching the disk
Pager
Database file
this stagebuilt alreadystill to comeA statement enters at the top; rows come back up.

Why this stage exists

Since the slotted leaf, a row has been limited to 1000 bytes. The limit was never about the data: it was about splitting. A leaf split must divide a full leaf plus one new cell into two halves that each fit in a page, and that is only always possible when no cell is too large. With cells of at most 1004 bytes, any four fit in an empty leaf, so every split works.

But a note, a description or a JSON document is easily longer than 1000 bytes, and refusing it is not an answer a database can give. The way out keeps the split's guarantee and drops the limit: a large row keeps a fixed-size piece of itself in its cell, and puts the rest somewhere the tree never has to divide, a chain of pages of its own. Real databases call these overflow pages. They need the free list from earlier in this part, because chains come and go with their rows.

What changes: a cell that points onward

A row of at most 1000 bytes is stored exactly as before. A larger row has a cell of 1004 bytes: the key, the row's first 996 bytes, then the number of its first overflow page. The row's own row_size, which is in those first bytes, says how long the row really is. That is how a reader knows a chain follows: a row_size over 1000 cannot fit in the cell, so the last four bytes must be a page number. No flag is needed.

0
01
00
00
00
1d
27
00
00
02
00
00
01
00
00
00
10
27
  • keybytes 0–3 (4 bytes) · 1
  • row_sizebytes 4–7 (4 bytes) · 10013: more than 1000, so a chain follows
  • column_countbytes 8–9 (2 bytes) · 2
  • null bitmapbytes 10–10 (1 bytes) · no NULLs
  • idbytes 11–14 (4 bytes) · 1
  • body lengthbytes 15–16 (2 bytes) · 10000
The first 17 bytes of the cell for a row of notes (id int, body varchar(65535)) with a 10,000-byte body. The cell goes on with the first 983 bytes of the body, which ends the row's first 996 bytes, and closes with 4 bytes: the first overflow page, 3.

An overflow page is page type 5. It says how many of the row's bytes it holds, and which page comes next, and holds those bytes from byte 16, at most 4000:

0
05
00
00
00
00
00
a0
0f
00
00
04
00
00
00
00
00
  • typebytes 0–0 (1 bytes) · 5: an overflow page
  • zerosbytes 1–5 (5 bytes) · unused
  • bytes heldbytes 6–9 (4 bytes) · 4000
  • nextbytes 10–13 (4 bytes) · the next page of the chain: 4. The last page has 0
  • zerosbytes 14–15 (2 bytes) · unused; the row's bytes start at byte 16
The first 16 bytes of overflow page 3. Then 4000 bytes of the row, zeros up to the trailer, and the trailer, as on any page. The next page sits at bytes 10 to 13, the same place a leaf keeps its next leaf and a free page the next free page.

A row of s bytes has a chain of ceil((s − 996) / 4000) pages, all full but the last. The 10,013-byte row above has 9,017 bytes after its first 996, so three pages: 4000, 4000 and 1017. Follow it from the leaf:

WhereHoldsNext
leaf 2, cell for key 1the key, the row's bytes 0 to 995page 3
page 3the row's bytes 996 to 4995 (4000)page 4
page 4the row's bytes 4996 to 8995 (4000)page 5
page 5the row's bytes 8996 to 10012 (1017)0: the end

What changes: writing, reading and freeing chains

INSERT allocates the whole chain first, first page first, and writes it, before the cell goes into its leaf and before any split. If the pages cannot all be allocated, the statement is 00602 and changes nothing: not a page of the chain, not the leaf. Reading a row puts it back together: the 996 bytes from the cell, then each page's bytes in chain order. Everything that reads rows, SELECT, WHERE, btree find, sees the whole row, as if it were in the cell.

Freeing goes through the free list. A chain is freed from its last page to its first, so its first page ends up at the head of the list, and a new chain of the same length takes the same pages back in the same order. DELETE frees a row's chain before the cell leaves its leaf. UPDATE frees the old chain and then stores the new row as INSERT would: a row that keeps its size reuses its pages, one that shrinks under 1001 bytes gives them all back, one that grows gets a chain. DROP TABLE and TRUNCATE free each leaf's chains, its rows in key order, just before the leaf itself.

The tree never sees a chain. A cell is still at most 1004 bytes, so leaves split, borrow and merge exactly as before, and a merge that moves a cell moves its page number with it.

The trade-off: how much to keep inline

996 bytes is a choice. Keep more in the cell, and fewer reads need the chain, but fewer cells fit in a leaf, the tree gets taller, and every search pays for it. Keep less, and leaves stay dense, but more rows need an extra page read to be returned. A row that needs a chain costs one more read per 4000 bytes, so a scan of a table full of large rows reads mostly overflow pages.

The prefix here is the most that keeps four cells in any leaf. Real engines make other choices for other reasons.

Real-world numbers. SQLite keeps a prefix in the cell and the rest in a chain of overflow pages, as here, with the prefix sized so at least four cells fit in a page. PostgreSQL moves large values out of the row entirely into a separate TOAST table, compressing them first, and keeps only a pointer in the row.

The algorithm in words

1

Store a row

Before the cell goes into its leaf.

  1. 1.A row of at most 1000 bytes is stored exactly as before.
  2. 2.A larger row needs ceil((size - 996) / 4000) overflow pages. Allocate them all first, first page first, before the cell goes into its leaf and before any split. If they cannot all be allocated, the statement is 00602 and changes nothing.
  3. 3.Write the row's bytes after the first 996 into the pages, 4000 to a page, each page saying how many bytes it holds and which page is next; the last says 0.
  4. 4.The cell is the key, the row's first 996 bytes, and the first page's number: 1004 bytes, never more, so any four cells still fit in an empty leaf.
2

Read a row

Everything that reads a row sees all of it.

  1. 1.Read the row_size at the start of the row. If it is at most 1000, the cell holds the whole row.
  2. 2.Otherwise take the 996 bytes in the cell, then follow the chain from the page number after them, appending each page's bytes, until a page names 0 as its next.
  3. 3.SELECT, WHERE comparisons, btree find and the [EXEC] counts work on that whole row, as if it were in the cell.
3

Free a chain

Whenever a row with a chain goes away or changes.

  1. 1.Free the chain's pages from its last to its first, so the first ends up at the head of the free list.
  2. 2.DELETE frees the row's chain before the cell leaves the leaf, and so before any merge.
  3. 3.UPDATE frees the old row's chain and then stores the new row as INSERT would. A row that keeps its size gets the same pages back, in the same order.
  4. 4.DROP TABLE and TRUNCATE free a leaf's chains, its rows in key order, just before the leaf itself.

Output contract for this stage

The full definition is on the output contract page, section 29.

InputOutput
a row over 1000 bytesstored; 00407 no longer happens
btree dump of a leaf, a cell with a chain[BTREE] Cell <i>: key=<k> (<row_size> bytes) overflow=<first page> pages=<chain length>
btree dump of a leaf, any other cellas before
an INSERT whose chain cannot be allocated[ERROR:00602] <message>, nothing changed
a CREATE TABLE whose column list is over 65535 bytes[ERROR:00405] <message>
serialize of a row over 1000 bytesthe three [SERIALIZE] lines of section 15, the whole row
btree dump or a page command on an overflow page00703 or 00606

A line, and so a statement, may be up to 131072 bytes. Each VARCHAR(n) is still at most 65535 bytes. pager write still writes into one page, so text longer than 4091 bytes is 00104, and deserialize keeps its limit of 1000 bytes.

What you should see when done

With notes (id int, body varchar(65535)) on page 2, a 10,000-byte body and a short one (the long value is shortened here):

droid session
Welcome to droid!
>insert into notes (id, body) values (1, 'ahov…');
Insert statement executed successfully (1 rows)
>insert into notes (id, body) values (2, 'small');
Insert statement executed successfully (1 rows)
>btree dump 2
[BTREE] Page 2: type=LEAF num_cells=2 free_space=3042
[BTREE] Cell 0: key=1 (10013 bytes) overflow=3 pages=3
[BTREE] Cell 1: key=2 (18 bytes)
>delete from notes where id = 1;
Delete statement executed successfully (1 rows)
[EXEC] rows_examined=1
>db freelist
[DB] Free list: pages=3 chain=[3,4,5]
>.exit
exiting.. good bye!

Things to notice. The large cell takes 1004 bytes of the leaf, plus its slot, whatever the row's size: the leaf has 3042 bytes free. Its chain is pages 3, 4 and 5, allocated before the cell went in. Deleting the row put the chain on the free list with its first page at the head, ready for the next large row.

Common bugs in this stage

  • Writing the cell before the chain. If the chain then fails to allocate, the leaf holds a cell pointing at nothing. Allocate and write the chain first, then the cell.
  • Getting the chain length wrong at a boundary. A row of 1001 bytes needs one page with 5 bytes in it; one of 4996 needs exactly one full page, and 4997 needs two. Test both sides of each boundary.
  • Deciding by the cell's length. A 1000-byte row and a row with a chain both have 1004-byte cells. Only row_size tells them apart.
  • Leaking chains. DELETE, an UPDATE that shrinks a row, DROP TABLE and TRUNCATE must all free the chains of the rows they remove. A page lost this way is in no tree and on no list.
  • Freeing the chain in the wrong order. The contract fixes it, last page first, and the free list shows it.
  • Reading a chain into a fixed buffer. A row can be 65,548 bytes. Size the buffer from row_size, or grow it.

Language notes

The page above is the same for every language. These are the only language-specific hints.

  • C: malloc the row from its row_size before following the chain, and remember a row is no longer guaranteed to fit on the stack.
  • C++: a std::vector<uint8_t> reserved to row_size, filled with insert from each page, keeps the reassembly short.
  • Rust: build the row in a Vec<u8> with with_capacity(row_size) and extend_from_slice each page's bytes.
  • Zig: allocate the row with the allocator from row_size, and @memcpy each page's bytes into its place.

Checklist

One line per thing the tests check. The tags name the test cases.

  • A value far larger than a page is stored and comes back byte for byte
    large-value-roundtrip
  • A large value survives a restart
    large-value-persists
  • INSERT, UPDATE, serialize and CREATE TABLE take rows over 1000 bytes; a column list over 65535 bytes is 00405
    no-row-limit
  • btree dump shows the row's size, its first overflow page and its chain length
    dump-shows-chain
  • A row of s bytes has a chain of ceil((s - 996) / 4000) pages
    chain-length-exact
  • An overflow page in the file is type 5, its count at 6-9, next at 10-13, data from 16
    overflow-page-layout
  • DELETE frees the chain, first page at the head, and the next large row reuses it
    delete-frees-chain
  • An UPDATE that makes a large row small frees its chain
    update-shrink-frees-chain
  • An UPDATE that makes a row large allocates a chain; one of the same size reuses it
    update-grow-allocates-chain
  • DROP TABLE frees each row's chain just before its leaf
    drop-frees-chains
  • An overflow page is neither a node nor a slotted page
    overflow-page-is-not-a-node
  • An INSERT whose chain cannot be allocated is 00602 and changes nothing
    chain-hits-page-limit