Browse Source

Step 10

master
Timothy Warren 5 months ago
parent
commit
cd4c6002c6
2 changed files with 400 additions and 69 deletions
  1. 196
    15
      db.c
  2. 204
    54
      spec/main_spec.rb

+ 196
- 15
db.c View File

@@ -109,6 +109,22 @@ const uint32_t PARENT_POINTER_SIZE = sizeof(uint32_t);
109 109
 const uint32_t PARENT_POINTER_OFFSET = IS_ROOT_OFFSET + IS_ROOT_SIZE;
110 110
 const uint32_t COMMON_NODE_HEADER_SIZE = NODE_TYPE_SIZE + IS_ROOT_SIZE + PARENT_POINTER_SIZE;
111 111
 
112
+/*
113
+ * Internal Node Header Layout
114
+ */
115
+const uint32_t INTERNAL_NODE_NUM_KEYS_SIZE = sizeof(uint32_t);
116
+const uint32_t INTERNAL_NODE_NUM_KEYS_OFFSET = COMMON_NODE_HEADER_SIZE;
117
+const uint32_t INTERNAL_NODE_RIGHT_CHILD_SIZE = sizeof(uint32_t);
118
+const uint32_t INTERNAL_NODE_RIGHT_CHILD_OFFSET = INTERNAL_NODE_NUM_KEYS_OFFSET + INTERNAL_NODE_NUM_KEYS_SIZE;
119
+const uint32_t INTERNAL_NODE_HEADER_SIZE = COMMON_NODE_HEADER_SIZE + INTERNAL_NODE_NUM_KEYS_SIZE + INTERNAL_NODE_RIGHT_CHILD_SIZE;
120
+
121
+/*
122
+ * Internal Node Body Layout
123
+ */
124
+const uint32_t INTERNAL_NODE_KEY_SIZE = sizeof(uint32_t);
125
+const uint32_t INTERNAL_NODE_CHILD_SIZE = sizeof(uint32_t);
126
+const uint32_t INTERNAL_NODE_CELL_SIZE = INTERNAL_NODE_CHILD_SIZE + INTERNAL_NODE_KEY_SIZE;
127
+
112 128
 /*
113 129
  * Leaf Node Header Layout
114 130
  */
@@ -126,6 +142,8 @@ const uint32_t LEAF_NODE_VALUE_OFFSET = LEAF_NODE_KEY_OFFSET + LEAF_NODE_KEY_SIZ
126 142
 const uint32_t LEAF_NODE_CELL_SIZE = LEAF_NODE_KEY_SIZE + LEAF_NODE_VALUE_SIZE;
127 143
 const uint32_t LEAF_NODE_SPACE_FOR_CELLS = PAGE_SIZE - LEAF_NODE_HEADER_SIZE;
128 144
 const uint32_t LEAF_NODE_MAX_CELLS = LEAF_NODE_SPACE_FOR_CELLS / LEAF_NODE_CELL_SIZE;
145
+const uint32_t LEAF_NODE_RIGHT_SPLIT_COUNT = (LEAF_NODE_MAX_CELLS + 1) / 2;
146
+const uint32_t LEAF_NODE_LEFT_SPLIT_COUNT = (LEAF_NODE_MAX_CELLS + 1) - LEAF_NODE_RIGHT_SPLIT_COUNT;
129 147
 
130 148
 NodeType get_node_type(void* node) {
131 149
     uint8_t value = *((uint8_t*)(node + NODE_TYPE_OFFSET));
@@ -137,6 +155,44 @@ void set_node_type(void* node, NodeType type) {
137 155
     *((uint8_t*)(node + NODE_TYPE_OFFSET)) = value;
138 156
 }
139 157
 
158
+bool is_node_root(void* node) {
159
+    uint8_t value = *((uint8_t*)(node + IS_ROOT_OFFSET));
160
+    return (bool)value;
161
+}
162
+
163
+void set_node_root(void* node, bool is_root) {
164
+    uint8_t value = is_root;
165
+    *((uint8_t*)(node + IS_ROOT_OFFSET)) = value;
166
+}
167
+
168
+uint32_t* internal_node_num_keys(void* node) {
169
+    return node + INTERNAL_NODE_NUM_KEYS_OFFSET;
170
+}
171
+
172
+uint32_t* internal_node_right_child(void* node) {
173
+    return node + INTERNAL_NODE_RIGHT_CHILD_OFFSET;
174
+}
175
+
176
+uint32_t* internal_node_cell(void* node, uint32_t cell_num) {
177
+    return node + INTERNAL_NODE_HEADER_SIZE + cell_num * INTERNAL_NODE_CELL_SIZE;
178
+}
179
+
180
+uint32_t* internal_node_child(void* node, uint32_t child_num) {
181
+    uint32_t num_keys = *internal_node_num_keys(node);
182
+    if (child_num > num_keys) {
183
+        printf("Tried to access child_num %d > num_keys %d\n", child_num, num_keys);
184
+        exit(EXIT_FAILURE);
185
+    } else if (child_num == num_keys) {
186
+        return internal_node_right_child(node);
187
+    } else {
188
+        return internal_node_cell(node, child_num);
189
+    }
190
+}
191
+
192
+uint32_t* internal_node_key(void* node, uint32_t key_num) {
193
+    return internal_node_cell(node, key_num) + INTERNAL_NODE_CHILD_SIZE;
194
+}
195
+
140 196
 uint32_t* leaf_node_num_cells(void* node) {
141 197
     return (char *)node + LEAF_NODE_NUM_CELLS_OFFSET;
142 198
 }
@@ -153,6 +209,15 @@ void* leaf_node_value(void* node, uint32_t cell_num) {
153 209
     return leaf_node_cell(node, cell_num) + LEAF_NODE_KEY_SIZE;
154 210
 }
155 211
 
212
+uint32_t get_node_max_key(void* node) {
213
+    switch (get_node_type(node)) {
214
+        case NODE_INTERNAL:
215
+            return *internal_node_key(node, *internal_node_num_keys(node) - 1);
216
+        case NODE_LEAF:
217
+            return *leaf_node_key(node, *leaf_node_num_cells(node) - 1);
218
+    }
219
+}
220
+
156 221
 void print_constants() {
157 222
     printf("ROW_SIZE: %d\n", ROW_SIZE);
158 223
     printf("COMMON_NODE_HEADER_SIZE: %d\n", COMMON_NODE_HEADER_SIZE);
@@ -197,6 +262,43 @@ void* get_page(Pager* pager, uint32_t page_num) {
197 262
     return pager->pages[page_num];
198 263
 }
199 264
 
265
+void indent(uint32_t level) {
266
+    for (uint32_t i = 0; i < level; i++) {
267
+        printf("  ");
268
+    }
269
+}
270
+
271
+void print_tree(Pager* pager, uint32_t page_num, uint32_t indentation_level) {
272
+    void* node = get_page(pager, page_num);
273
+    uint32_t num_keys, child;
274
+
275
+    switch(get_node_type(node)) {
276
+        case (NODE_LEAF):
277
+            num_keys = *leaf_node_num_cells(node);
278
+            indent(indentation_level);
279
+            printf("- leaf (size %d\n", num_keys);
280
+            for (uint32_t i = 0; i < num_keys; i++) {
281
+                indent(indentation_level + 1);
282
+                printf("- %d\n", *leaf_node_key(node, i));
283
+            }
284
+            break;
285
+        case (NODE_INTERNAL):
286
+            num_keys = *internal_node_num_keys(node);
287
+            indent(indentation_level);
288
+            printf("- internal (size %d)\n", num_keys);
289
+            for (uint32_t i = 0; i < num_keys; i++) {
290
+                child = *internal_node_child(node, i);
291
+                print_tree(pager, child, indentation_level + 1);
292
+
293
+                indent(indentation_level + 1);
294
+                printf("- key %d\n", *internal_node_key(node, i));
295
+            }
296
+            child = *internal_node_right_child(node);
297
+            print_tree(pager, child, indentation_level + 1);
298
+            break;
299
+    }
300
+}
301
+
200 302
 void serialize_row(Row* source, void* destination) {
201 303
     memcpy(destination + ID_OFFSET, &(source->id), ID_SIZE);
202 304
     memcpy(destination + USERNAME_OFFSET, &(source->username), USERNAME_SIZE);
@@ -211,9 +313,16 @@ void deserialize_row(void* source, Row* destination) {
211 313
 
212 314
 void initialize_leaf_node(void* node) {
213 315
     set_node_type(node, NODE_LEAF);
316
+    set_node_root(node, false);
214 317
     *leaf_node_num_cells(node) = 0;
215 318
 }
216 319
 
320
+void initialize_internal_node(void* node) {
321
+    set_node_type(node, NODE_INTERNAL);
322
+    set_node_root(node, false);
323
+    *internal_node_num_keys(node) = 0;
324
+}
325
+
217 326
 Cursor* leaf_node_find(Table* table, uint32_t page_num, uint32_t key) {
218 327
     void* node = get_page(table->pager, page_num);
219 328
     uint32_t num_cells = *leaf_node_num_cells(node);
@@ -328,6 +437,7 @@ Table* db_open(const char* filename) {
328 437
         // New database file. Initialize page 0 as leaf node.
329 438
         void* root_node = get_page(pager, 0);
330 439
         initialize_leaf_node(root_node);
440
+        set_node_root(root_node, true);
331 441
     }
332 442
 
333 443
     return table;
@@ -412,22 +522,13 @@ void db_close(Table* table) {
412 522
     free(pager);
413 523
 }
414 524
 
415
-void print_leaf_node(void* node) {
416
-    uint32_t num_cells = *leaf_node_num_cells(node);
417
-    printf("leaf (size %d)\n", num_cells);
418
-    for (uint32_t i = 0; i < num_cells; i++) {
419
-        uint32_t key = *leaf_node_key(node, i);
420
-        printf("  - %d : %d\n", i, key);
421
-    }
422
-}
423
-
424 525
 MetaCommandResult do_meta_command(InputBuffer* input_buffer, Table* table) {
425 526
     if (strcmp(input_buffer->buffer, ".exit") == 0) {
426 527
         db_close(table);
427 528
         exit(EXIT_SUCCESS);
428 529
     } else if (strcmp(input_buffer->buffer, ".btree") == 0){
429 530
         printf("Tree:\n");
430
-        print_leaf_node(get_page(table->pager, 0));
531
+        print_tree(table->pager, 0, 0);
431 532
         return META_COMMAND_SUCCESS;
432 533
     } else if (strcmp(input_buffer->buffer, ".constants") == 0) {
433 534
         printf("Constants:\n");
@@ -480,14 +581,97 @@ PrepareResult prepare_statement(InputBuffer* input_buffer, Statement* statement)
480 581
     return PREPARE_UNRECOGNIZED_STATEMENT;
481 582
 }
482 583
 
584
+/*
585
+ * Until we start recycling free pages, new pages will always
586
+ * go onto the end of the database file
587
+ */
588
+uint32_t get_unused_page_num(Pager * pager) {
589
+    return pager->num_pages;
590
+}
591
+
592
+void create_new_root(Table* table, uint32_t right_child_page_num) {
593
+    /*
594
+     * Handle splitting the root.
595
+     * Old root copied to new page, becomes left child.
596
+     * Address of right child passed in.
597
+     * Re-initialize root page to contain the new root node.
598
+     * New root points to two children.
599
+     */
600
+
601
+    void* root = get_page(table->pager, table->root_page_num);
602
+    void* right_child = get_page(table->pager, right_child_page_num);
603
+    uint32_t left_child_page_num = get_unused_page_num(table->pager);
604
+    void* left_child = get_page(table->pager, left_child_page_num);
605
+
606
+    /* Left child has data copied from old root */
607
+    memcpy(left_child, root, PAGE_SIZE);
608
+    set_node_root(left_child, false);
609
+
610
+    /* Root node is a new internal node with one key and two children */
611
+    initialize_internal_node(root);
612
+    set_node_root(root, true);
613
+    *internal_node_num_keys(root) = 1;
614
+    *internal_node_child(root, 0) = left_child_page_num;
615
+    uint32_t left_child_max_key = get_node_max_key(left_child);
616
+    *internal_node_key(root, 0) = left_child_max_key;
617
+    *internal_node_right_child(root) = right_child_page_num;
618
+}
619
+
620
+void leaf_node_split_and_insert(Cursor* cursor, uint32_t key, Row* value) {
621
+    /*
622
+     * Create a new node and move half the cells over.
623
+     * Insert the new value in one of the two nodes.
624
+     * Update parent or create a new parent.
625
+     */
626
+    void* old_node = get_page(cursor->table->pager, cursor->page_num);
627
+    uint32_t new_page_num = get_unused_page_num(cursor->table->pager);
628
+    void* new_node = get_page(cursor->table->pager, new_page_num);
629
+    initialize_leaf_node(new_node);
630
+
631
+    /*
632
+     * All existing keys plus new key should be divided
633
+     * evenly between old (left) and new (right) nodes.
634
+     * Starting from the right, move each key to correct position.
635
+     */
636
+    for (int32_t i = LEAF_NODE_MAX_CELLS; i >= 0; i--) {
637
+        void* destination_node;
638
+        if (i >+ LEAF_NODE_LEFT_SPLIT_COUNT) {
639
+            destination_node = new_node;
640
+        } else {
641
+            destination_node = old_node;
642
+        }
643
+        uint32_t index_within_node = i % LEAF_NODE_LEFT_SPLIT_COUNT;
644
+        void* destination = leaf_node_cell(destination_node, index_within_node);
645
+
646
+        if (i == cursor->cell_num) {
647
+            serialize_row(value, destination);
648
+        } else if (i > cursor->cell_num) {
649
+            memcpy(destination, leaf_node_cell(old_node, i - 1), LEAF_NODE_CELL_SIZE);
650
+        } else {
651
+            memcpy(destination, leaf_node_cell(old_node, i), LEAF_NODE_CELL_SIZE);
652
+        }
653
+    }
654
+
655
+    /* Update cell count on both leaf nodes */
656
+    *(leaf_node_num_cells(old_node)) = LEAF_NODE_LEFT_SPLIT_COUNT;
657
+    *(leaf_node_num_cells(new_node)) = LEAF_NODE_RIGHT_SPLIT_COUNT;
658
+
659
+    if (is_node_root(old_node)) {
660
+        return create_new_root(cursor->table, new_page_num);
661
+    } else {
662
+        printf("Need to implement updating parent after split\n");
663
+        exit(EXIT_FAILURE);
664
+    }
665
+}
666
+
483 667
 void leaf_node_insert(Cursor* cursor, uint32_t key, Row* value) {
484 668
     void* node = get_page(cursor->table->pager, cursor->page_num);
485 669
 
486 670
     uint32_t num_cells = *leaf_node_num_cells(node);
487 671
     if (num_cells >= LEAF_NODE_MAX_CELLS) {
488 672
         // Node full
489
-        printf("Need to implement splitting a leaf node.\n");
490
-        exit(EXIT_FAILURE);
673
+        leaf_node_split_and_insert(cursor, key, value);
674
+        return;
491 675
     }
492 676
 
493 677
     if (cursor->cell_num < num_cells) {
@@ -506,9 +690,6 @@ void leaf_node_insert(Cursor* cursor, uint32_t key, Row* value) {
506 690
 ExecuteResult execute_insert(Statement* statement, Table* table) {
507 691
     void* node = get_page(table->pager, table->root_page_num);
508 692
     uint32_t num_cells = (*leaf_node_num_cells(node));
509
-    if (num_cells >= LEAF_NODE_MAX_CELLS) {
510
-        return EXECUTE_TABLE_FULL;
511
-    }
512 693
 
513 694
     Row* row_to_insert = &(statement->row_to_insert);
514 695
     uint32_t key_to_insert = row_to_insert->id;

+ 204
- 54
spec/main_spec.rb View File

@@ -7,7 +7,11 @@ describe 'database' do
7 7
     raw_output = nil
8 8
     IO.popen("./db test.db", "r+") do |pipe|
9 9
       commands.each do |command|
10
-        pipe.puts command
10
+        begin
11
+          pipe.puts command
12
+        rescue Errno::EPIPE
13
+          break
14
+        end
11 15
       end
12 16
 
13 17
       pipe.close_write
@@ -24,7 +28,7 @@ describe 'database' do
24 28
       "select",
25 29
       ".exit",
26 30
     ])
27
-    expect(result).to eq([
31
+    expect(result).to match_array([
28 32
       "db > Executed.",
29 33
       "db > (1, user1, person1@example.com)",
30 34
       "Executed.",
@@ -32,13 +36,37 @@ describe 'database' do
32 36
     ])
33 37
   end
34 38
 
39
+  it 'keeps data after closing connection' do
40
+    result1 = run_script([
41
+      "insert 1 user1 person1@example.com",
42
+      ".exit",
43
+    ])
44
+    expect(result1).to match_array([
45
+      "db > Executed.",
46
+      "db > ",
47
+    ])
48
+
49
+    result2 = run_script([
50
+      "select",
51
+      ".exit",
52
+    ])
53
+    expect(result2).to match_array([
54
+      "db > (1, user1, person1@example.com)",
55
+      "Executed.",
56
+      "db > ",
57
+    ])
58
+  end
59
+
35 60
   it 'prints error message when table is full' do
36 61
     script = (1..1401).map do |i|
37 62
       "insert #{i} user#{i} person#{i}@example.com"
38 63
     end
39 64
     script << ".exit"
40 65
     result = run_script(script)
41
-    expect(result[-2]).to eq('db > Error: Table full.')
66
+    expect(result.last(2)).to match_array([
67
+      "db > Executed.",
68
+      "db > Need to implement splitting internal node",
69
+    ])
42 70
   end
43 71
 
44 72
   it 'allows inserting strings that are the maximum length' do
@@ -50,7 +78,7 @@ describe 'database' do
50 78
       ".exit",
51 79
     ]
52 80
     result = run_script(script)
53
-    expect(result).to eq([
81
+    expect(result).to match_array([
54 82
       "db > Executed.",
55 83
       "db > (1, #{long_username}, #{long_email})",
56 84
       "Executed.",
@@ -67,7 +95,7 @@ describe 'database' do
67 95
       ".exit",
68 96
     ]
69 97
     result = run_script(script)
70
-    expect(result).to eq([
98
+    expect(result).to match_array([
71 99
       "db > String is too long.",
72 100
       "db > Executed.",
73 101
       "db > ",
@@ -81,88 +109,210 @@ describe 'database' do
81 109
       ".exit",
82 110
     ]
83 111
     result = run_script(script)
84
-    expect(result).to eq([
112
+    expect(result).to match_array([
85 113
       "db > ID must be positive.",
86 114
       "db > Executed.",
87 115
       "db > ",
88 116
     ])
89 117
   end
90 118
 
91
-  it 'keeps data after closing connection' do
92
-    result1 = run_script([
119
+  it 'prints an error message if there is a duplicate id' do
120
+    script = [
121
+      "insert 1 user1 person1@example.com",
93 122
       "insert 1 user1 person1@example.com",
94
-      ".exit",
95
-    ])
96
-    expect(result1).to eq([
97
-      "db > Executed.",
98
-      "db > ",
99
-    ])
100
-    result2 = run_script([
101 123
       "select",
102 124
       ".exit",
103
-    ])
104
-    expect(result2).to eq([
125
+    ]
126
+    result = run_script(script)
127
+    expect(result).to match_array([
128
+      "db > Executed.",
129
+      "db > Error: Duplicate key.",
105 130
       "db > (1, user1, person1@example.com)",
106 131
       "Executed.",
107 132
       "db > ",
108 133
     ])
109 134
   end
110 135
 
111
-  it 'prints constants' do
112
-    script = [
113
-        ".constants",
114
-        ".exit",
115
-    ]
136
+  it 'allows printing out the structure of a one-node btree' do
137
+    script = [3, 1, 2].map do |i|
138
+      "insert #{i} user#{i} person#{i}@example.com"
139
+    end
140
+    script << ".btree"
141
+    script << ".exit"
116 142
     result = run_script(script)
117 143
 
118 144
     expect(result).to match_array([
119
-        "db > Constants:",
120
-        "ROW_SIZE: 293",
121
-        "COMMON_NODE_HEADER_SIZE: 6",
122
-        "LEAF_NODE_HEADER_SIZE: 10",
123
-        "LEAF_NODE_CELL_SIZE: 297",
124
-        "LEAF_NODE_SPACE_FOR_CELLS: 4086",
125
-        "LEAF_NODE_MAX_CELLS: 13",
126
-        "db > ",
145
+      "db > Executed.",
146
+      "db > Executed.",
147
+      "db > Executed.",
148
+      "db > Tree:",
149
+      "- leaf (size 3)",
150
+      "  - 1",
151
+      "  - 2",
152
+      "  - 3",
153
+      "db > "
127 154
     ])
128 155
   end
129 156
 
130
-  it 'allows printing out the structure of a one-node btree' do
131
-    script = [3, 1, 2].map do |i|
132
-        "insert #{i} user#{i} person#{i}@example.com"
157
+  it 'allows printing out the structure of a 3-leaf-node btree' do
158
+    script = (1..14).map do |i|
159
+      "insert #{i} user#{i} person#{i}@example.com"
133 160
     end
134
-
135 161
     script << ".btree"
162
+    script << "insert 15 user15 person15@example.com"
136 163
     script << ".exit"
137 164
     result = run_script(script)
138 165
 
139
-    expect(result).to match_array([
140
-        "db > Executed.",
141
-        "db > Executed.",
142
-        "db > Executed.",
143
-        "db > Tree:",
144
-        "leaf (size 3)",
145
-        "  - 0 : 1",
146
-        "  - 1 : 2",
147
-        "  - 2 : 3",
148
-        "db > "
166
+    expect(result[14...(result.length)]).to match_array([
167
+      "db > Tree:",
168
+      "- internal (size 1)",
169
+      "  - leaf (size 7)",
170
+      "    - 1",
171
+      "    - 2",
172
+      "    - 3",
173
+      "    - 4",
174
+      "    - 5",
175
+      "    - 6",
176
+      "    - 7",
177
+      "  - key 7",
178
+      "  - leaf (size 7)",
179
+      "    - 8",
180
+      "    - 9",
181
+      "    - 10",
182
+      "    - 11",
183
+      "    - 12",
184
+      "    - 13",
185
+      "    - 14",
186
+      "db > Executed.",
187
+      "db > ",
149 188
     ])
150 189
   end
151 190
 
152
-  it 'prints an error message if there is a duplicate id' do
191
+  it 'allows printing out the structure of a 4-leaf-node btree' do
153 192
     script = [
154
-        "insert 1 user1 person1@example.com",
155
-        "insert 1 user1 person1@example.com",
156
-        "select",
157
-        ".exit",
193
+      "insert 18 user18 person18@example.com",
194
+      "insert 7 user7 person7@example.com",
195
+      "insert 10 user10 person10@example.com",
196
+      "insert 29 user29 person29@example.com",
197
+      "insert 23 user23 person23@example.com",
198
+      "insert 4 user4 person4@example.com",
199
+      "insert 14 user14 person14@example.com",
200
+      "insert 30 user30 person30@example.com",
201
+      "insert 15 user15 person15@example.com",
202
+      "insert 26 user26 person26@example.com",
203
+      "insert 22 user22 person22@example.com",
204
+      "insert 19 user19 person19@example.com",
205
+      "insert 2 user2 person2@example.com",
206
+      "insert 1 user1 person1@example.com",
207
+      "insert 21 user21 person21@example.com",
208
+      "insert 11 user11 person11@example.com",
209
+      "insert 6 user6 person6@example.com",
210
+      "insert 20 user20 person20@example.com",
211
+      "insert 5 user5 person5@example.com",
212
+      "insert 8 user8 person8@example.com",
213
+      "insert 9 user9 person9@example.com",
214
+      "insert 3 user3 person3@example.com",
215
+      "insert 12 user12 person12@example.com",
216
+      "insert 27 user27 person27@example.com",
217
+      "insert 17 user17 person17@example.com",
218
+      "insert 16 user16 person16@example.com",
219
+      "insert 13 user13 person13@example.com",
220
+      "insert 24 user24 person24@example.com",
221
+      "insert 25 user25 person25@example.com",
222
+      "insert 28 user28 person28@example.com",
223
+      ".btree",
224
+      ".exit",
225
+    ]
226
+    result = run_script(script)
227
+
228
+    expect(result[30...(result.length)]).to match_array([
229
+      "db > Tree:",
230
+      "- internal (size 3)",
231
+      "  - leaf (size 7)",
232
+      "    - 1",
233
+      "    - 2",
234
+      "    - 3",
235
+      "    - 4",
236
+      "    - 5",
237
+      "    - 6",
238
+      "    - 7",
239
+      "  - key 7",
240
+      "  - leaf (size 8)",
241
+      "    - 8",
242
+      "    - 9",
243
+      "    - 10",
244
+      "    - 11",
245
+      "    - 12",
246
+      "    - 13",
247
+      "    - 14",
248
+      "    - 15",
249
+      "  - key 15",
250
+      "  - leaf (size 7)",
251
+      "    - 16",
252
+      "    - 17",
253
+      "    - 18",
254
+      "    - 19",
255
+      "    - 20",
256
+      "    - 21",
257
+      "    - 22",
258
+      "  - key 22",
259
+      "  - leaf (size 8)",
260
+      "    - 23",
261
+      "    - 24",
262
+      "    - 25",
263
+      "    - 26",
264
+      "    - 27",
265
+      "    - 28",
266
+      "    - 29",
267
+      "    - 30",
268
+      "db > ",
269
+    ])
270
+  end
271
+
272
+  it 'prints constants' do
273
+    script = [
274
+      ".constants",
275
+      ".exit",
158 276
     ]
159 277
     result = run_script(script)
278
+
160 279
     expect(result).to match_array([
161
-        "db > Executed.",
162
-        "db > Error: Duplicate key.",
163
-        "db > (1, user1, person1@example.com)",
164
-        "Executed.",
165
-        "db > ",
280
+      "db > Constants:",
281
+      "ROW_SIZE: 293",
282
+      "COMMON_NODE_HEADER_SIZE: 6",
283
+      "LEAF_NODE_HEADER_SIZE: 14",
284
+      "LEAF_NODE_CELL_SIZE: 297",
285
+      "LEAF_NODE_SPACE_FOR_CELLS: 4082",
286
+      "LEAF_NODE_MAX_CELLS: 13",
287
+      "db > ",
288
+    ])
289
+  end
290
+
291
+  it 'prints all rows in a multi-level tree' do
292
+    script = []
293
+    (1..15).each do |i|
294
+      script << "insert #{i} user#{i} person#{i}@example.com"
295
+    end
296
+    script << "select"
297
+    script << ".exit"
298
+    result = run_script(script)
299
+    expect(result[15...result.length]).to match_array([
300
+      "db > (1, user1, person1@example.com)",
301
+      "(2, user2, person2@example.com)",
302
+      "(3, user3, person3@example.com)",
303
+      "(4, user4, person4@example.com)",
304
+      "(5, user5, person5@example.com)",
305
+      "(6, user6, person6@example.com)",
306
+      "(7, user7, person7@example.com)",
307
+      "(8, user8, person8@example.com)",
308
+      "(9, user9, person9@example.com)",
309
+      "(10, user10, person10@example.com)",
310
+      "(11, user11, person11@example.com)",
311
+      "(12, user12, person12@example.com)",
312
+      "(13, user13, person13@example.com)",
313
+      "(14, user14, person14@example.com)",
314
+      "(15, user15, person15@example.com)",
315
+      "Executed.", "db > ",
166 316
     ])
167 317
   end
168 318
 end

Loading…
Cancel
Save