CDXLPlay added with permission from Fredik Wikstrom.
[AROS-Contrib.git] / sqlite3 / test / btree.test
blob8e882951d9ad7a95ce6fd012c43dc2f8f7289d75
1 # 2001 September 15
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
6 #    May you do good and not evil.
7 #    May you find forgiveness for yourself and forgive others.
8 #    May you share freely, never taking more than you give.
10 #***********************************************************************
11 # This file implements regression tests for SQLite library.  The
12 # focus of this script is btree database backend
14 # $Id: btree.test,v 1.35 2005/01/11 10:25:07 danielk1977 Exp $
17 set testdir [file dirname $argv0]
18 source $testdir/tester.tcl
20 ifcapable default_autovacuum {
21   finish_test
22   return
25 # Basic functionality.  Open and close a database.
27 do_test btree-1.1 {
28   file delete -force test1.bt
29   file delete -force test1.bt-journal
30   set rc [catch {btree_open test1.bt 2000 0} ::b1]
31 } {0}
33 # The second element of the list returned by btree_pager_stats is the
34 # number of pages currently checked out.  We'll be checking this value
35 # frequently during this test script, to make sure the btree library
36 # is properly releasing the pages it checks out, and thus avoiding
37 # page leaks.
39 do_test btree-1.1.1 {
40   lindex [btree_pager_stats $::b1] 1
41 } {0}
42 do_test btree-1.2 {
43   set rc [catch {btree_open test1.bt 2000 0} ::b2]
44 } {0}
45 do_test btree-1.3 {
46   set rc [catch {btree_close $::b2} msg]
47   lappend rc $msg
48 } {0 {}}
50 # Do an insert and verify that the database file grows in size.
52 do_test btree-1.4 {
53   set rc [catch {btree_begin_transaction $::b1} msg]
54   lappend rc $msg
55 } {0 {}}
56 do_test btree-1.4.1 {
57   lindex [btree_pager_stats $::b1] 1
58 } {1}
59 do_test btree-1.5 {
60   set rc [catch {btree_cursor $::b1 1 1} ::c1]
61   if {$rc} {lappend rc $::c1}
62   set rc
63 } {0}
64 do_test btree-1.6 {
65   set rc [catch {btree_insert $::c1 100 1.00} msg]
66   lappend rc $msg
67 } {0 {}}
68 do_test btree-1.7 {
69   btree_move_to $::c1 100
70   btree_key $::c1
71 } {100}
72 do_test btree-1.8 {
73   btree_data $::c1
74 } {1.00}
75 do_test btree-1.9 {
76   set rc [catch {btree_close_cursor $::c1} msg]
77   lappend rc $msg
78 } {0 {}}
79 do_test btree-1.10 {
80   set rc [catch {btree_commit $::b1} msg]
81   lappend rc $msg
82 } {0 {}}
83 do_test btree-1.11 {
84   file size test1.bt
85 } {1024}
86 do_test btree-1.12 {
87   lindex [btree_pager_stats $::b1] 1
88 } {0}
90 # Reopen the database and attempt to read the record that we wrote.
92 do_test btree-2.1 {
93   set rc [catch {btree_cursor $::b1 1 1} ::c1]
94   if {$rc} {lappend rc $::c1}
95   set rc
96 } {0}
97 do_test btree-2.2 {
98   btree_move_to $::c1 99
99 } {1}
100 do_test btree-2.3 {
101   btree_move_to $::c1 101
102 } {-1}
103 do_test btree-2.4 {
104   btree_move_to $::c1 100
105 } {0}
106 do_test btree-2.5 {
107   btree_key $::c1
108 } {100}
109 do_test btree-2.6 {
110   btree_data $::c1
111 } {1.00}
112 do_test btree-2.7 {
113   lindex [btree_pager_stats $::b1] 1
114 } {1}
116 # Do some additional inserts
118 do_test btree-3.1 {
119   btree_begin_transaction $::b1
120   btree_insert $::c1 200 2.00
121   btree_move_to $::c1 200
122   btree_key $::c1
123 } {200}
124 do_test btree-3.1.1 {
125   lindex [btree_pager_stats $::b1] 1
126 } {1}
127 do_test btree-3.2 {
128   btree_insert $::c1 300 3.00
129   btree_move_to $::c1 300
130   btree_key $::c1
131 } {300}
132 do_test btree-3.4 {
133   btree_insert $::c1 400 4.00
134   btree_move_to $::c1 400
135   btree_key $::c1
136 } {400}
137 do_test btree-3.5 {
138   btree_insert $::c1 500 5.00
139   btree_move_to $::c1 500
140   btree_key $::c1
141 } {500}
142 do_test btree-3.6 {
143   btree_insert $::c1 600 6.00
144   btree_move_to $::c1 600
145   btree_key $::c1
146 } {600}
147 #btree_page_dump $::b1 2
148 do_test btree-3.7 {
149   set rc [btree_move_to $::c1 0]
150   expr {$rc>0}
151 } {1}
152 do_test btree-3.8 {
153   btree_key $::c1
154 } {100}
155 do_test btree-3.9 {
156   btree_data $::c1
157 } {1.00}
158 do_test btree-3.10 {
159   btree_next $::c1
160   btree_key $::c1
161 } {200}
162 do_test btree-3.11 {
163   btree_data $::c1
164 } {2.00}
165 do_test btree-3.12 {
166   btree_next $::c1
167   btree_key $::c1
168 } {300}
169 do_test btree-3.13 {
170   btree_data $::c1
171 } {3.00}
172 do_test btree-3.14 {
173   btree_next $::c1
174   btree_key $::c1
175 } {400}
176 do_test btree-3.15 {
177   btree_data $::c1
178 } {4.00}
179 do_test btree-3.16 {
180   btree_next $::c1
181   btree_key $::c1
182 } {500}
183 do_test btree-3.17 {
184   btree_data $::c1
185 } {5.00}
186 do_test btree-3.18 {
187   btree_next $::c1
188   btree_key $::c1
189 } {600}
190 do_test btree-3.19 {
191   btree_data $::c1
192 } {6.00}
193 do_test btree-3.20.1 {
194   btree_next $::c1
195   btree_key $::c1
196 } {0}
197 do_test btree-3.20.2 {
198   btree_eof $::c1
199 } {1}
200 # This test case used to test that one couldn't request data from an 
201 # invalid cursor. That is now an assert()ed condition.
203 # do_test btree-3.21 {
204 #   set rc [catch {btree_data $::c1} res]
205 #   lappend rc $res
206 # } {1 SQLITE_INTERNAL}
208 # Commit the changes, reopen and reread the data
210 do_test btree-3.22 {
211   set rc [catch {btree_close_cursor $::c1} msg]
212   lappend rc $msg
213 } {0 {}}
214 do_test btree-3.22.1 {
215   lindex [btree_pager_stats $::b1] 1
216 } {1}
217 do_test btree-3.23 {
218   set rc [catch {btree_commit $::b1} msg]
219   lappend rc $msg
220 } {0 {}}
221 do_test btree-3.23.1 {
222   lindex [btree_pager_stats $::b1] 1
223 } {0}
224 do_test btree-3.24 {
225   file size test1.bt
226 } {1024}
227 do_test btree-3.25 {
228   set rc [catch {btree_cursor $::b1 1 1} ::c1]
229   if {$rc} {lappend rc $::c1}
230   set rc
231 } {0}
232 do_test btree-3.25.1 {
233   lindex [btree_pager_stats $::b1] 1
234 } {1}
235 do_test btree-3.26 {
236   set rc [btree_move_to $::c1 0]
237   expr {$rc>0}
238 } {1}
239 do_test btree-3.27 {
240   btree_key $::c1
241 } {100}
242 do_test btree-3.28 {
243   btree_data $::c1
244 } {1.00}
245 do_test btree-3.29 {
246   btree_next $::c1
247   btree_key $::c1
248 } {200}
249 do_test btree-3.30 {
250   btree_data $::c1
251 } {2.00}
252 do_test btree-3.31 {
253   btree_next $::c1
254   btree_key $::c1
255 } {300}
256 do_test btree-3.32 {
257   btree_data $::c1
258 } {3.00}
259 do_test btree-3.33 {
260   btree_next $::c1
261   btree_key $::c1
262 } {400}
263 do_test btree-3.34 {
264   btree_data $::c1
265 } {4.00}
266 do_test btree-3.35 {
267   btree_next $::c1
268   btree_key $::c1
269 } {500}
270 do_test btree-3.36 {
271   btree_data $::c1
272 } {5.00}
273 do_test btree-3.37 {
274   btree_next $::c1
275   btree_key $::c1
276 } {600}
277 do_test btree-3.38 {
278   btree_data $::c1
279 } {6.00}
280 do_test btree-3.39 {
281   btree_next $::c1
282   btree_key $::c1
283 } {0}
284 # This test case used to test that requesting data from an invalid cursor
285 # returned SQLITE_INTERNAL. That is now an assert()ed condition.
287 # do_test btree-3.40 {
288 #   set rc [catch {btree_data $::c1} res]
289 #   lappend rc $res
290 # } {1 SQLITE_INTERNAL}
291 do_test btree-3.41 {
292   lindex [btree_pager_stats $::b1] 1
293 } {1}
296 # Now try a delete
298 do_test btree-4.1 {
299   btree_begin_transaction $::b1
300   btree_move_to $::c1 100
301   btree_key $::c1
302 } {100}
303 do_test btree-4.1.1 {
304   lindex [btree_pager_stats $::b1] 1
305 } {1}
306 do_test btree-4.2 {
307   btree_delete $::c1
308 } {}
309 do_test btree-4.3 {
310   btree_move_to $::c1 100
311   btree_key $::c1
312 } {200}
313 do_test btree-4.4 {
314   btree_next $::c1
315   btree_key $::c1
316 } {300}
317 do_test btree-4.5 {
318   btree_next $::c1
319   btree_key $::c1
320 } {400}
321 do_test btree-4.4 {
322   btree_move_to $::c1 0
323   set r {}
324   while 1 {
325     set key [btree_key $::c1]
326     if {[btree_eof $::c1]} break
327     lappend r $key
328     lappend r [btree_data $::c1]
329     btree_next $::c1
330   }
331   set r   
332 } {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00}
334 # Commit and make sure the delete is still there.
336 do_test btree-4.5 {
337   btree_commit $::b1
338   btree_move_to $::c1 0
339   set r {}
340   while 1 {
341     set key [btree_key $::c1]
342     if {[btree_eof $::c1]} break
343     lappend r $key
344     lappend r [btree_data $::c1]
345     btree_next $::c1
346   }
347   set r   
348 } {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00}
350 # Completely close the database and reopen it.  Then check
351 # the data again.
353 do_test btree-4.6 {
354   lindex [btree_pager_stats $::b1] 1
355 } {1}
356 do_test btree-4.7 {
357   btree_close_cursor $::c1
358   lindex [btree_pager_stats $::b1] 1
359 } {0}
360 do_test btree-4.8 {
361   btree_close $::b1
362   set ::b1 [btree_open test1.bt 2000 0]
363   set ::c1 [btree_cursor $::b1 1 1]
364   lindex [btree_pager_stats $::b1] 1
365 } {1}
366 do_test btree-4.9 {
367   set r {}
368   btree_first $::c1
369   while 1 {
370     set key [btree_key $::c1]
371     if {[btree_eof $::c1]} break
372     lappend r $key
373     lappend r [btree_data $::c1]
374     btree_next $::c1
375   }
376   set r   
377 } {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00}
379 # Try to read and write meta data
381 do_test btree-5.1 {
382   btree_get_meta $::b1
383 } {0 0 0 0 0 0 0 0 0 0}
384 do_test btree-5.2 {
385   set rc [catch {
386     btree_update_meta $::b1 0 1 2 3 4 5 6 7 8 9
387   } msg]
388   lappend rc $msg
389 } {1 SQLITE_ERROR}
390 do_test btree-5.3 {
391   btree_begin_transaction $::b1
392   set rc [catch {
393     btree_update_meta $::b1 0 1 2 3 0 5 6 7 8 9
394   } msg]
395   lappend rc $msg
396 } {0 {}}
397 do_test btree-5.4 {
398   btree_get_meta $::b1
399 } {0 1 2 3 0 5 6 7 8 9}
400 do_test btree-5.5 {
401   btree_close_cursor $::c1
402   btree_rollback $::b1
403   btree_get_meta $::b1
404 } {0 0 0 0 0 0 0 0 0 0}
405 do_test btree-5.6 {
406   btree_begin_transaction $::b1
407   btree_update_meta $::b1 0 10 20 30 0 50 60 70 80 90
408   btree_commit $::b1
409   btree_get_meta $::b1
410 } {0 10 20 30 0 50 60 70 80 90}
412 proc select_all {cursor} {
413   set r {}
414   btree_first $cursor
415   while {![btree_eof $cursor]} {
416     set key [btree_key $cursor]
417     lappend r $key
418     lappend r [btree_data $cursor]
419     btree_next $cursor
420   }
421   return $r
423 proc select_keys {cursor} {
424   set r {}
425   btree_first $cursor
426   while {![btree_eof $cursor]} {
427     set key [btree_key $cursor]
428     lappend r $key
429     btree_next $cursor
430   }
431   return $r
434 # Try to create a new table in the database file
436 do_test btree-6.1 {
437   set rc [catch {btree_create_table $::b1 0} msg]
438   lappend rc $msg
439 } {1 SQLITE_ERROR}
440 do_test btree-6.2 {
441   btree_begin_transaction $::b1
442   set ::t2 [btree_create_table $::b1 0]
443 } {2}
444 do_test btree-6.2.1 {
445   lindex [btree_pager_stats $::b1] 1
446 } {1}
447 do_test btree-6.2.2 {
448   set ::c2 [btree_cursor $::b1 $::t2 1]
449   lindex [btree_pager_stats $::b1] 1
450 } {2}
451 do_test btree-6.2.3 {
452   btree_insert $::c2 ten 10
453   btree_move_to $::c2 ten
454   btree_key $::c2
455 } {ten}
456 do_test btree-6.3 {
457   btree_commit $::b1
458   set ::c1 [btree_cursor $::b1 1 1]
459   lindex [btree_pager_stats $::b1] 1
460 } {2}
461 do_test btree-6.3.1 {
462   select_all $::c1
463 } {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00}
464 #btree_page_dump $::b1 3
465 do_test btree-6.4 {
466   select_all $::c2
467 } {ten 10}
469 # Drop the new table, then create it again anew.
471 do_test btree-6.5 {
472   btree_begin_transaction $::b1
473 } {}
474 do_test btree-6.6 {
475   btree_close_cursor $::c2
476 } {}
477 do_test btree-6.6.1 {
478   lindex [btree_pager_stats $::b1] 1
479 } {1}
480 do_test btree-6.7 {
481   btree_close_cursor $::c1
482   btree_drop_table $::b1 $::t2
483 } {}
484 do_test btree-6.7.1 {
485   lindex [btree_get_meta $::b1] 0
486 } {1}
487 do_test btree-6.8 {
488   set ::t2 [btree_create_table $::b1 0]
489 } {2}
490 do_test btree-6.8.1 {
491   lindex [btree_get_meta $::b1] 0
492 } {0}
493 do_test btree-6.9 {
494   set ::c2 [btree_cursor $::b1 $::t2 1]
495   lindex [btree_pager_stats $::b1] 1
496 } {2}
498 # This test case used to test that requesting the key from an invalid cursor
499 # returned an empty string.  But that is now an assert()ed condition.
501 # do_test btree-6.9.1 {
502 #   btree_move_to $::c2 {}
503 #   btree_key $::c2
504 # } {}
506 # If we drop table 1 it just clears the table.  Table 1 always exists.
508 do_test btree-6.10 {
509   btree_close_cursor $::c2
510   btree_drop_table $::b1 1
511   set ::c2 [btree_cursor $::b1 $::t2 1]
512   set ::c1 [btree_cursor $::b1 1 1]
513   btree_first $::c1
514   btree_eof $::c1
515 } {1}
516 do_test btree-6.11 {
517   btree_commit $::b1
518   select_all $::c1
519 } {}
520 do_test btree-6.12 {
521   select_all $::c2
522 } {}
523 do_test btree-6.13 {
524   btree_close_cursor $::c2
525   lindex [btree_pager_stats $::b1] 1
526 } {1}
528 # Check to see that pages defragment properly.  To do this test we will
530 #   1.  Fill the first page of table 1 with data.
531 #   2.  Delete every other entry of table 1.
532 #   3.  Insert a single entry that requires more contiguous
533 #       space than is available.
535 do_test btree-7.1 {
536   btree_begin_transaction $::b1
537 } {}
538 catch {unset key}
539 catch {unset data}
540 if 0 {
541 do_test btree-7.2 {
542   # Each record will be 10 bytes in size.
543   #   + 100 bytes of database header
544   #   + 8 bytes of table header
545   #   + 91*10=910 bytes of cells
546   # Totals 1018 bytes.  6 bytes left over
547   # Keys are 1000 through 1090.
548   for {set i 1000} {$i<1091} {incr i} {
549     set key $i
550     set data [format %5d $i]
551     btree_insert $::c1 $key $data
552   }
553   lrange [btree_cursor_info $::c1] 4 5
554 } {6 0}
555 #btree_tree_dump $::b1 1
556 do_test btree-7.3 {
557   for {set i 1001} {$i<1091} {incr i 2} {
558     btree_move_to $::c1 $i
559     btree_delete $::c1
560   }
561   # Freed 45 blocks.  Total freespace is 456
562   # Keys remaining are even numbers between 1000 and 1090, inclusive
563   lrange [btree_cursor_info $::c1] 4 5
564 } {456 45}
565 #btree_tree_dump $::b1 1
566 do_test btree-7.4 {
567   # The largest free block is 8 bytes long.  But there is also a
568   # huge hole between the cell pointer array and the cellcontent.
569   # But if we insert a large enough record, it should force a defrag.
570   set data 123456789_
571   append data $data
572   append data $data
573   append data $data
574   append data $data
575   append data $data
576   btree_insert $::c1 2000 $data
577   btree_move_to $::c1 2000
578   btree_key $::c1
579 } {2000}
580 do_test btree-7.5 {
581   lrange [btree_cursor_info $::c1] 4 5
582 } {343 0}
583 #btree_tree_dump $::b1 1
585 # Delete an entry to make a hole of a known size, then immediately recreate
586 # that entry.  This tests the path into allocateSpace where the hole exactly
587 # matches the size of the desired space.
589 # Keys are even numbers between 1000 and 1090 and one record of 2000.
590 # There are 47 keys total.
592 do_test btree-7.6 {
593   btree_move_to $::c1 1006
594   btree_delete $::c1
595   btree_move_to $::c1 1010
596   btree_delete $::c1
597 } {}
598 do_test btree-7.7 {
599   lrange [btree_cursor_info $::c1] 4 5
600 } {363 2}   ;# Create two new holes of 10 bytes each
601 #btree_page_dump $::b1 1
602 do_test btree-7.8 {
603   btree_insert $::c1 1006 { 1006}
604   lrange [btree_cursor_info $::c1] 4 5
605 } {353 1}   ;# Filled in the first hole
606 btree_page_dump $::b1 1
608 # Make sure the freeSpace() routine properly coaleses adjacent memory blocks
610 do_test btree-7.9 {
611   btree_move_to $::c1 1012
612   btree_delete $::c1
613   lrange [btree_cursor_info $::c1] 4 5
614 } {363 2}  ;# Coalesce with the hole before
615 btree_page_dump $::b1 1
616 exit
617 do_test btree-7.10 {
618   btree_move_to $::c1 1008
619   btree_delete $::c1
620   lrange [btree_cursor_info $::c1] 4 5
621 } {468 2}  ;# Coalesce with whole after
622 do_test btree-7.11 {
623   btree_move_to $::c1 1030
624   btree_delete $::c1
625   lrange [btree_cursor_info $::c1] 4 5
626 } {478 3}   ;# Make a new hole
627 do_test btree-7.13 {
628   btree_move_to $::c1 1034
629   btree_delete $::c1
630   lrange [btree_cursor_info $::c1] 4 5
631 } {488 4}   ;# Make another hole
632 do_test btree-7.14 {
633   btree_move_to $::c1 1032
634   btree_delete $::c1
635   lrange [btree_cursor_info $::c1] 4 5
636 } {498 3}   ;# The freed space should coalesce on both ends
637 #btree_page_dump $::b1 2
638 do_test btree-7.15 {
639   lindex [btree_pager_stats $::b1] 1
640 } {1}
641 } ;# endif
643 # Check to see that data on overflow pages work correctly.
645 do_test btree-8.1 {
646   set data "*** This is a very long key "
647   while {[string length $data]<1234} {append data $data}
648   set ::data $data
649   btree_insert $::c1 2020 $data
650 } {}
651 #btree_page_dump $::b1 1
652 do_test btree-8.1.1 {
653   lindex [btree_pager_stats $::b1] 1
654 } {1}
655 #btree_pager_ref_dump $::b1
656 do_test btree-8.2 {
657   btree_move_to $::c1 2020
658   string length [btree_data $::c1]
659 } [string length $::data]
660 do_test btree-8.3 {
661   btree_data $::c1
662 } $::data
663 do_test btree-8.4 {
664   btree_delete $::c1
665 } {}
666 do_test btree-8.4.1 {
667   lindex [btree_get_meta $::b1] 0
668 } [expr {int(([string length $::data]-238+1019)/1020)}]
669 do_test btree-8.4.2 {
670   btree_integrity_check $::b1 1 2
671 } {}
672 do_test btree-8.5 {
673   set data "*** This is an even longer key "
674   while {[string length $data]<2000} {append data $data}
675   append data END
676   set ::data $data
677   btree_insert $::c1 2030 $data
678 } {}
679 do_test btree-8.6 {
680   btree_move_to $::c1 2030
681   string length [btree_data $::c1]
682 } [string length $::data]
683 do_test btree-8.7 {
684   btree_data $::c1
685 } $::data
686 do_test btree-8.8 {
687   btree_commit $::b1
688   btree_data $::c1
689 } $::data
690 do_test btree-8.9.1 {
691   btree_close_cursor $::c1
692   btree_close $::b1
693   set ::b1 [btree_open test1.bt 2000 0]
694   set ::c1 [btree_cursor $::b1 1 1]
695   btree_move_to $::c1 2030
696   btree_data $::c1
697 } $::data
698 do_test btree-8.9.2 {
699   btree_integrity_check $::b1 1 2
700 } {}
701 do_test btree-8.10 {
702   btree_begin_transaction $::b1
703   btree_delete $::c1
704 } {}
705 do_test btree-8.11 {
706   lindex [btree_get_meta $::b1] 0
707 } {4}
709 # Now check out keys on overflow pages.
711 do_test btree-8.12.1 {
712   set ::keyprefix "This is a long prefix to a key "
713   while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix}
714   btree_close_cursor $::c1
715   btree_clear_table $::b1 2
716   lindex [btree_get_meta $::b1] 0
717 } {4}
718 do_test btree-8.12.2 {
719   btree_integrity_check $::b1 1 2
720 } {}
721 do_test btree-8.12.3 {
722   set ::c1 [btree_cursor $::b1 2 1]
723   btree_insert $::c1 ${::keyprefix}1 1
724   btree_first $::c1
725   btree_data $::c1
726 } {1}
727 do_test btree-8.13 {
728   btree_key $::c1
729 } ${keyprefix}1
730 do_test btree-8.14 {
731   btree_insert $::c1 ${::keyprefix}2 2
732   btree_insert $::c1 ${::keyprefix}3 3
733   btree_last $::c1
734   btree_key $::c1
735 } ${keyprefix}3
736 do_test btree-8.15 {
737   btree_move_to $::c1 ${::keyprefix}2
738   btree_data $::c1
739 } {2}
740 do_test btree-8.16 {
741   btree_move_to $::c1 ${::keyprefix}1
742   btree_data $::c1
743 } {1}
744 do_test btree-8.17 {
745   btree_move_to $::c1 ${::keyprefix}3
746   btree_data $::c1
747 } {3}
748 do_test btree-8.18 {
749   lindex [btree_get_meta $::b1] 0
750 } {1}
751 do_test btree-8.19 {
752   btree_move_to $::c1 ${::keyprefix}2
753   btree_key $::c1
754 } ${::keyprefix}2
755 #btree_page_dump $::b1 2
756 do_test btree-8.20 {
757   btree_delete $::c1
758   btree_next $::c1
759   btree_key $::c1
760 } ${::keyprefix}3
761 #btree_page_dump $::b1 2
762 do_test btree-8.21 {
763   lindex [btree_get_meta $::b1] 0
764 } {2}
765 do_test btree-8.22 {
766   lindex [btree_pager_stats $::b1] 1
767 } {2}
768 do_test btree-8.23.1 {
769   btree_close_cursor $::c1
770   btree_drop_table $::b1 2
771   btree_integrity_check $::b1 1
772 } {}
773 do_test btree-8.23.2 {
774   btree_create_table $::b1 0
775 } {2}
776 do_test btree-8.23.3 {
777   set ::c1 [btree_cursor $::b1 2 1]
778   lindex [btree_get_meta $::b1] 0
779 } {4}
780 do_test btree-8.24 {
781   lindex [btree_pager_stats $::b1] 1
782 } {2}
783 #btree_pager_ref_dump $::b1
784 do_test btree-8.25 {
785   btree_integrity_check $::b1 1 2
786 } {}
788 # Check page splitting logic
790 do_test btree-9.1 {
791   for {set i 1} {$i<=19} {incr i} {
792     set key [format %03d $i]
793     set data "*** $key *** $key *** $key *** $key ***"
794     btree_insert $::c1 $key $data
795   }
796 } {}
797 #btree_tree_dump $::b1 2
798 #btree_pager_ref_dump $::b1
799 #set pager_refinfo_enable 1
800 do_test btree-9.2 {
801   btree_insert $::c1 020 {*** 020 *** 020 *** 020 *** 020 ***}
802   select_keys $::c1
803 } {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020}
804 #btree_page_dump $::b1 2
805 #btree_pager_ref_dump $::b1
806 #set pager_refinfo_enable 0
808 # The previous "select_keys" command left the cursor pointing at the root
809 # page.  So there should only be two pages checked out.  2 (the root) and
810 # page 1.
811 do_test btree-9.2.1 {
812   lindex [btree_pager_stats $::b1] 1
813 } {2}
814 for {set i 1} {$i<=20} {incr i} {
815   do_test btree-9.3.$i.1 [subst {
816     btree_move_to $::c1 [format %03d $i]
817     btree_key $::c1
818   }] [format %03d $i]
819   do_test btree-9.3.$i.2 [subst {
820     btree_move_to $::c1 [format %03d $i]
821     string range \[btree_data $::c1\] 0 10
822   }] "*** [format %03d $i] ***"
824 do_test btree-9.4.1 {
825   lindex [btree_pager_stats $::b1] 1
826 } {2}
828 # Check the page joining logic.
830 #btree_page_dump $::b1 2
831 #btree_pager_ref_dump $::b1
832 do_test btree-9.4.2 {
833   btree_move_to $::c1 005
834   btree_delete $::c1
835 } {}
836 #btree_page_dump $::b1 2
837 for {set i 1} {$i<=19} {incr i} {
838   if {$i==5} continue
839   do_test btree-9.5.$i.1 [subst {
840     btree_move_to $::c1 [format %03d $i]
841     btree_key $::c1
842   }] [format %03d $i]
843   do_test btree-9.5.$i.2 [subst {
844     btree_move_to $::c1 [format %03d $i]
845     string range \[btree_data $::c1\] 0 10
846   }] "*** [format %03d $i] ***"
848 #btree_pager_ref_dump $::b1
849 do_test btree-9.6 {
850   btree_close_cursor $::c1
851   lindex [btree_pager_stats $::b1] 1
852 } {1}
853 do_test btree-9.7 {
854   btree_integrity_check $::b1 1 2
855 } {}
856 do_test btree-9.8 {
857   btree_rollback $::b1
858   lindex [btree_pager_stats $::b1] 1
859 } {0}
860 do_test btree-9.9 {
861   btree_integrity_check $::b1 1 2
862 } {}
863 do_test btree-9.10 {
864   btree_close $::b1
865   set ::b1 [btree_open test1.bt 2000 0]
866   btree_integrity_check $::b1 1 2
867 } {}
869 # Create a tree of depth two.  That is, there is a single divider entry
870 # on the root pages and two leaf pages.  Then delete the divider entry
871 # see what happens.
873 do_test btree-10.1 {
874   btree_begin_transaction $::b1
875   btree_clear_table $::b1 2
876   lindex [btree_pager_stats $::b1] 1
877 } {1}
878 do_test btree-10.2 {
879   set ::c1 [btree_cursor $::b1 2 1]
880   lindex [btree_pager_stats $::b1] 1
881 } {2}
882 do_test btree-10.3 {
883   for {set i 1} {$i<=30} {incr i} {
884     set key [format %03d $i]
885     set data "*** $key *** $key *** $key *** $key ***"
886     btree_insert $::c1 $key $data
887   }
888   select_keys $::c1
889 } {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030}
890 #btree_tree_dump $::b1 2
891 do_test btree-10.4 {
892   # The divider entry is 012.  This is found by uncommenting the 
893   # btree_tree_dump call above and looking at the tree.  If the page size
894   # changes, this test will no longer work.
895   btree_move_to $::c1 012
896   btree_delete $::c1
897   select_keys $::c1
898 } {001 002 003 004 005 006 007 008 009 010 011 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030}
899 #btree_pager_ref_dump $::b1
900 #btree_tree_dump $::b1 2
901 for {set i 1} {$i<=30} {incr i} {
902   # Check the number of unreference pages.  This should be 3 in most cases,
903   # but 2 when the cursor is pointing to the divider entry which is now 013.
904   do_test btree-10.5.$i {
905     btree_move_to $::c1 [format %03d $i]
906     lindex [btree_pager_stats $::b1] 1
907   } [expr {$i==13?2:3}]
908   #btree_pager_ref_dump $::b1
909   #btree_tree_dump $::b1 2
912 # Create a tree with lots more pages
914 catch {unset ::data}
915 catch {unset ::key}
916 for {set i 31} {$i<=2000} {incr i} {
917   do_test btree-11.1.$i.1 {
918     set key [format %03d $i]
919     set ::data "*** $key *** $key *** $key *** $key ***"
920     btree_insert $::c1 $key $data
921     btree_move_to $::c1 $key
922     btree_key $::c1
923   } [format %03d $i]
924   do_test btree-11.1.$i.2 {
925     btree_data $::c1
926   } $::data
927   set ::key [format %03d [expr {$i/2}]]
928   if {$::key=="012"} {set ::key 013}
929   do_test btree-11.1.$i.3 {
930     btree_move_to $::c1 $::key
931     btree_key $::c1
932   } $::key
934 catch {unset ::data}
935 catch {unset ::key}
937 # Make sure our reference count is still correct.
939 do_test btree-11.2 {
940   btree_close_cursor $::c1
941   lindex [btree_pager_stats $::b1] 1
942 } {1}
943 do_test btree-11.3 {
944   set ::c1 [btree_cursor $::b1 2 1]
945   lindex [btree_pager_stats $::b1] 1
946 } {2}
948 # Delete the dividers on the root page
950 #btree_page_dump $::b1 2
951 do_test btree-11.4 {
952   btree_move_to $::c1 1667
953   btree_delete $::c1
954   btree_move_to $::c1 1667
955   set k [btree_key $::c1]
956   if {$k==1666} {
957     set k [btree_next $::c1]
958   }
959   btree_key $::c1
960 } {1668}
961 #btree_page_dump $::b1 2
963 # Change the data on an intermediate node such that the node becomes overfull
964 # and has to split.  We happen to know that intermediate nodes exist on
965 # 337, 401 and 465 by the btree_page_dumps above
967 catch {unset ::data}
968 set ::data {This is going to be a very long data segment}
969 append ::data $::data
970 append ::data $::data
971 do_test btree-12.1 {
972   btree_insert $::c1 337 $::data
973   btree_move_to $::c1 337
974   btree_data $::c1
975 } $::data
976 do_test btree-12.2 {
977   btree_insert $::c1 401 $::data
978   btree_move_to $::c1 401
979   btree_data $::c1
980 } $::data
981 do_test btree-12.3 {
982   btree_insert $::c1 465 $::data
983   btree_move_to $::c1 465
984   btree_data $::c1
985 } $::data
986 do_test btree-12.4 {
987   btree_move_to $::c1 337
988   btree_key $::c1
989 } {337}
990 do_test btree-12.5 {
991   btree_data $::c1
992 } $::data
993 do_test btree-12.6 {
994   btree_next $::c1
995   btree_key $::c1
996 } {338}
997 do_test btree-12.7 {
998   btree_move_to $::c1 464
999   btree_key $::c1
1000 } {464}
1001 do_test btree-12.8 {
1002   btree_next $::c1
1003   btree_data $::c1
1004 } $::data
1005 do_test btree-12.9 {
1006   btree_next $::c1
1007   btree_key $::c1
1008 } {466}
1009 do_test btree-12.10 {
1010   btree_move_to $::c1 400
1011   btree_key $::c1
1012 } {400}
1013 do_test btree-12.11 {
1014   btree_next $::c1
1015   btree_data $::c1
1016 } $::data
1017 do_test btree-12.12 {
1018   btree_next $::c1
1019   btree_key $::c1
1020 } {402}
1021 # btree_commit $::b1
1022 # btree_tree_dump $::b1 1
1023 do_test btree-13.1 {
1024   btree_integrity_check $::b1 1 2
1025 } {}
1027 # To Do:
1029 #   1.  Do some deletes from the 3-layer tree
1030 #   2.  Commit and reopen the database
1031 #   3.  Read every 15th entry and make sure it works
1032 #   4.  Implement btree_sanity and put it throughout this script
1035 do_test btree-15.98 {
1036   btree_close_cursor $::c1
1037   lindex [btree_pager_stats $::b1] 1
1038 } {1}
1039 do_test btree-15.99 {
1040   btree_rollback $::b1
1041   lindex [btree_pager_stats $::b1] 1
1042 } {0}
1043 btree_pager_ref_dump $::b1
1045 # Miscellaneous tests.
1047 # btree-16.1 - Check that a statement cannot be started if a transaction 
1048 #              is not active.
1049 # btree-16.2 - Check that it is an error to request more payload from a 
1050 #              btree entry than the entry contains.
1051 do_test btree-16.1 {
1052   catch {btree_begin_statement $::b1} msg
1053   set msg
1054 } SQLITE_ERROR
1056 do_test btree-16.2 {
1057   btree_begin_transaction $::b1
1058   set ::c1 [btree_cursor $::b1 2 1]
1059   btree_insert $::c1 1 helloworld
1060   btree_close_cursor $::c1
1061   btree_commit $::b1
1062 } {}
1063 do_test btree-16.3 {
1064   set ::c1 [btree_cursor $::b1 2 1]
1065   btree_first $::c1
1066 } 0
1067 do_test btree-16.4 {
1068   catch {btree_data $::c1 [expr [btree_payload_size $::c1] + 10]} msg
1069   set msg
1070 } SQLITE_ERROR
1072 if {$tcl_platform(platform)=="unix"} {
1073   do_test btree-16.5 {
1074     btree_close $::b1
1075     set ::origperm [file attributes test1.bt -permissions]
1076     file attributes test1.bt -permissions o-w,g-w,a-w
1077     set ::b1 [btree_open test1.bt 2000 0]
1078     catch {btree_cursor $::b1 2 1} msg
1079     file attributes test1.bt -permissions $::origperm
1080     btree_close $::b1
1081     set ::b1 [btree_open test1.bt 2000 0]
1082     set msg
1083   } {SQLITE_READONLY}
1086 do_test btree-16.6 {
1087   set ::c1 [btree_cursor $::b1 2 1]
1088   set ::c2 [btree_cursor $::b1 2 1]
1089   btree_begin_transaction $::b1
1090   for {set i 0} {$i<100} {incr i} {
1091     btree_insert $::c1 $i [string repeat helloworld 10]
1092   }
1093   btree_last $::c2
1094   btree_insert $::c1 100 [string repeat helloworld 10]
1095 } {}
1097 do_test btree-16.7 {
1098   btree_close_cursor $::c1
1099   btree_close_cursor $::c2
1100   btree_commit $::b1
1101   set ::c1 [btree_cursor $::b1 2 1]
1102   catch {btree_insert $::c1 101 helloworld} msg
1103   set msg
1104 } {SQLITE_ERROR}
1105 do_test btree-16.8 {
1106   btree_first $::c1
1107   catch {btree_delete $::c1} msg
1108   set msg
1109 } {SQLITE_ERROR}
1110 do_test btree-16.9 {
1111   btree_close_cursor $::c1
1112   btree_begin_transaction $::b1
1113   set ::c1 [btree_cursor $::b1 2 0]
1114   catch {btree_insert $::c1 101 helloworld} msg
1115   set msg
1116 } {SQLITE_PERM}
1117 do_test btree-16.10 {
1118   catch {btree_delete $::c1} msg
1119   set msg
1120 } {SQLITE_PERM}
1121 do_test btree-16.11 {
1122   btree_close_cursor $::c1
1123   set ::c2 [btree_cursor $::b1 2 1]
1124   set ::c1 [btree_cursor $::b1 2 0]
1125   catch {btree_insert $::c2 101 helloworld} msg
1126   set msg
1127 } {SQLITE_LOCKED}
1128 do_test btree-16.12 {
1129   btree_first $::c2
1130   catch {btree_delete $::c2} msg
1131   set msg
1132 } {SQLITE_LOCKED}
1133 do_test btree-16.13 {
1134   catch {btree_clear_table $::b1 2} msg
1135   set msg
1136 } {SQLITE_LOCKED}
1137 do_test btree-16.14 {
1138   btree_close_cursor $::c1
1139   btree_close_cursor $::c2
1140   btree_commit $::b1
1141   catch {btree_clear_table $::b1 2} msg
1142   set msg
1143 } {SQLITE_ERROR}
1144 do_test btree-16.15 {
1145   catch {btree_drop_table $::b1 2} msg
1146   set msg
1147 } {SQLITE_ERROR}
1148 do_test btree-16.16 {
1149   btree_begin_transaction $::b1
1150   set ::c1 [btree_cursor $::b1 2 0]
1151   catch {btree_drop_table $::b1 2} msg
1152   set msg
1153 } {SQLITE_LOCKED}
1155 do_test btree-99.1 {
1156   btree_close $::b1
1157 } {}
1158 catch {unset data}
1159 catch {unset key}
1161 finish_test