@@ -823,6 +823,77 @@ static uint8_t *build_table_cell(int64_t rowid, const uint8_t *payload, int payl
823823 return cell ;
824824}
825825
826+ // Build a table leaf cell with overflow: stores only the first local_len bytes of
827+ // payload inline, followed by a 4-byte overflow page number.
828+ // total_payload_len is the FULL original payload length (written as the payload-size
829+ // varint so SQLite knows the real record size).
830+ static uint8_t * build_table_cell_overflow (int64_t rowid , const uint8_t * payload ,
831+ int total_payload_len , int local_len ,
832+ uint32_t overflow_page , int * out_cell_len ) {
833+ int rl = varint_len (total_payload_len );
834+ int kl = varint_len (rowid );
835+ // cell = varint(total_payload_len) + varint(rowid) + payload[0..local_len) + uint32(overflow)
836+ int total = rl + kl + local_len + BTREE_PTR_SIZE ;
837+ uint8_t * cell = (uint8_t * )malloc (total );
838+ if (!cell ) {
839+ return NULL ;
840+ }
841+ int pos = 0 ;
842+ pos += put_varint (cell + pos , total_payload_len );
843+ pos += put_varint (cell + pos , rowid );
844+ memcpy (cell + pos , payload , local_len );
845+ pos += local_len ;
846+ put_u32 (cell + pos , overflow_page );
847+ pos += BTREE_PTR_SIZE ;
848+ * out_cell_len = pos ;
849+ return cell ;
850+ }
851+
852+ // --- Overflow page writer ---
853+ // Writes overflow pages for payload bytes that exceed local storage.
854+ // Returns the first overflow page number (embedded in the leaf cell).
855+ // Each overflow page: 4-byte next-page pointer + up to (CBM_PAGE_SIZE-4) bytes of data.
856+ static uint32_t write_overflow_pages (FILE * fp , uint32_t * next_page , const uint8_t * data ,
857+ int data_len ) {
858+ int per_page = CBM_PAGE_SIZE - BTREE_PTR_SIZE ;
859+ uint32_t first_page = 0 ;
860+ long prev_next_ptr_offset = - SKIP_ONE ;
861+
862+ int offset = 0 ;
863+ while (offset < data_len ) {
864+ uint32_t pnum = (* next_page )++ ;
865+ if (first_page == 0 ) {
866+ first_page = pnum ;
867+ }
868+
869+ // Backpatch previous overflow page's next-page pointer
870+ if (prev_next_ptr_offset >= 0 ) {
871+ uint8_t ptr [BTREE_PTR_SIZE ];
872+ put_u32 (ptr , pnum );
873+ (void )fseek (fp , prev_next_ptr_offset , SEEK_SET );
874+ (void )fwrite (ptr , SKIP_ONE , BTREE_PTR_SIZE , fp );
875+ }
876+
877+ int chunk = data_len - offset ;
878+ if (chunk > per_page ) {
879+ chunk = per_page ;
880+ }
881+
882+ uint8_t page [CBM_PAGE_SIZE ];
883+ memset (page , 0 , CBM_PAGE_SIZE );
884+ put_u32 (page , 0 ); // next-page pointer — 0 for now, backpatched on next iteration
885+ memcpy (page + BTREE_PTR_SIZE , data + offset , chunk );
886+
887+ long page_offset = (long )(pnum - SKIP_ONE ) * CBM_PAGE_SIZE ;
888+ prev_next_ptr_offset = page_offset ;
889+ (void )fseek (fp , page_offset , SEEK_SET );
890+ (void )fwrite (page , SKIP_ONE , CBM_PAGE_SIZE , fp );
891+
892+ offset += chunk ;
893+ }
894+ return first_page ;
895+ }
896+
826897// --- Index record builders ---
827898
828899// Build an index entry for a 2-column TEXT index (project, col) + rowid.
@@ -975,11 +1046,43 @@ static bool pb_ensure_leaf_cap(PageBuilder *pb) {
9751046 return true;
9761047}
9771048
1049+ // SQLite overflow thresholds for leaf table B-tree pages (PAGE_SIZE=65536, reserved=0):
1050+ // usable = PAGE_SIZE = 65536
1051+ // max_local = usable - 35 = 65501
1052+ // min_local = (usable - 12) * 32 / 255 - 23 = 8199 (C integer arithmetic, same as SQLite)
1053+ #define TABLE_OVERFLOW_MAX_LOCAL 65501
1054+ #define TABLE_OVERFLOW_MIN_LOCAL 8199
1055+
9781056// Add a table cell to the PageBuilder, flushing leaf pages as needed.
1057+ // If the payload exceeds max_local, overflow pages are written and only the
1058+ // local portion plus a 4-byte overflow page pointer is stored in the leaf cell.
9791059static void pb_add_table_cell_with_flush (PageBuilder * pb , int64_t rowid , const uint8_t * payload ,
9801060 int payload_len , int64_t prev_rowid ) {
9811061 int cell_len = 0 ;
982- uint8_t * cell = build_table_cell (rowid , payload , payload_len , & cell_len );
1062+ uint8_t * cell = NULL ;
1063+
1064+ if (payload_len > TABLE_OVERFLOW_MAX_LOCAL ) {
1065+ // Compute local_len per SQLite spec for leaf table cells.
1066+ int ovfl_page_data = CBM_PAGE_SIZE - BTREE_PTR_SIZE ;
1067+ int remainder = (payload_len - TABLE_OVERFLOW_MIN_LOCAL ) % ovfl_page_data ;
1068+ int local_len = TABLE_OVERFLOW_MIN_LOCAL + remainder ;
1069+ if (local_len > TABLE_OVERFLOW_MAX_LOCAL ) {
1070+ local_len = TABLE_OVERFLOW_MIN_LOCAL ;
1071+ }
1072+
1073+ // Write overflow pages for the bytes that don't fit locally.
1074+ uint32_t overflow_page = write_overflow_pages (pb -> fp , & pb -> next_page , payload + local_len ,
1075+ payload_len - local_len );
1076+ if (overflow_page == 0 ) {
1077+ return ; // overflow write failed
1078+ }
1079+
1080+ cell = build_table_cell_overflow (rowid , payload , payload_len , local_len , overflow_page ,
1081+ & cell_len );
1082+ } else {
1083+ cell = build_table_cell (rowid , payload , payload_len , & cell_len );
1084+ }
1085+
9831086 if (!cell ) {
9841087 return ;
9851088 }
0 commit comments