Exercise 3: scan() Preserves Insertion Order Across Multiple Pages — Possible Solution ==================================================================== THE TEST ------------------------------ inserted = [[f"row_number_{i:03d}"] for i in range(15)] for r in inserted: table.insert(r) scanned = list(table.scan()) scanned == inserted RESULT ------------------------------ True -- scanned matches inserted exactly, first row to last, even though 15 rows of this size don't all fit on one page. WHY THE ORDER IS PRESERVED ACROSS PAGE BOUNDARIES ------------------------------ Two facts combine to guarantee this. First, insert() always tries the CURRENT last page before allocating a new one (Exercise 2's own finding) -- meaning pages fill up strictly in the order they're created, and a new page is only ever added once every existing page is genuinely full. Rows therefore land in pages in a strictly increasing sequence: however many rows fit on page 0 go there first, in insertion order (since add_record() always appends to the next free slot within a page); once page 0 is full, the next rows go to page 1, again in insertion order; and so on. Second, scan()'s own iteration order is: def scan(self): for page_num in range(self.heap_file.num_pages()): page = self.heap_file.read_page(page_num) for slot in range(page.num_slots): yield decode_row(self.schema, page.get_record(slot)) The outer loop visits pages in increasing page_num order (0, 1, 2, ...), and the inner loop visits each page's own slots in increasing slot order (0, 1, 2, ...) -- both loops walk forward, never backward, and never skip or reorder anything. Since rows were WRITTEN into increasing (page_num, slot) positions in exactly their insertion order, and scan() READS positions back in that same increasing (page_num, slot) order, the two orders match exactly -- the write order and the read order are both, independently, "walk forward through pages, and forward through slots within each page," which is what makes them agree. WHY THIS WORKS AS AN ANSWER ------------------------------ This confirms a heap table's own "unordered" reputation refers to the lack of any SORTING by the data's own values (that's what Chapters 5-6's B-tree index is for) -- not to the physical storage order, which is actually completely deterministic and insertion-ordered here, a direct consequence of both insert() and scan() walking forward through pages and slots in the same, consistent direction.